Skip to content

Transfer destinations ​

Generated from DialerDigital/core commit 0fc18008b3c8 — spec openapi/dialer-v1.yaml v0.134.0. Do not edit by hand; see How this reference is built. Download the OpenAPI spec.

Español

The tenant's registry of the external numbers a live call may be transferred to.

Endpoints ​

MethodPathSummary
GET/v1/transfer-destinationsThe tenant's current external transfer destinations
POST/v1/transfer-destinationsRegister an external transfer destination
PATCH/v1/transfer-destinations/{id}Declare the line type of a registered external transfer destination
DELETE/v1/transfer-destinations/{id}Retire an external transfer destination
GET/v1/transfer/targetsWhere a live call can be transferred, as a picker needs it

The tenant's current external transfer destinations ​

http
GET /v1/transfer-destinations

The registered numbers a blind transfer may leave towards, oldest registration first. Retired destinations are not listed. Readable by a dd_ key and by any dashboard login; a widget token (ddw_) is 403 forbidden.

Responses

200 ​

The current registrations.

FieldTypeRequiredDescription
transfer_destinationsarray of TransferDestinationyes
transfer_destinations[].idstring (uuid)yesThe registration; the id to retire it by.
transfer_destinations[].numberstringyesStrict E.164.
transfer_destinations[].labelstringyes
transfer_destinations[].line_typemobile · landline · voip · unknownyesWhat the tenant declares the line to be — the same values and default as a contact's line_type on import. The consent gate reads it for a transfer to the number: landline lifts the prior-express-consent requirement; unknown, mobile and voip keep it.
transfer_destinations[].line_type_declared_atstring (date-time)yesWhen the current line_type was declared — registered_at until a PATCH changes it.
transfer_destinations[].line_type_declared_bystringyesWho declared the current line_type (same shape as registered_by).
transfer_destinations[].registered_atstring (date-time)yesWhen it was registered; the anchor the RND is queried with for this number.
transfer_destinations[].registered_bystringyesThe user or agent id of the credential that registered it, or tenant-api-key.

401 — Unauthorized ​

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

403 — Forbidden ​

The tenant is suspended. A dashboard session (ddu_) of a tenant whose login policy (ADR 227) is in mode enforce answers 403 with the code login_policy_denied on a request from an address or at an hour the policy does not allow — the same body for both; the session is not revoked and a later request inside the policy goes through. A widget token (ddw_) of a seat is held to the same policy on every request (core#2406, flag widget_login_honors_policy). A synthetic (demo) tenant answers 403 with the code synthetic_tenant on every route that would reach a third party — manual dial, external transfer or conference leg, SMS send (core#2251, ADR #280 addendum). No payment changes it.

json
{
  "error": {
    "code": "forbidden",
    "message": "tenant is suspended"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (core#2503) (code rate_limited). The request was not processed. Wait Retry-After seconds and retry; a refusal is not counted against the budget, so that wait is always enough. Bounded per API key and, above the keys, per tenant (api_tenant_requests_per_minute): a sibling key is unaffected until the tenant's keys together reach that ceiling. The quota applies to every operation reachable with a dd_ key, so the operations that name a more specific 429 (call control, dispositions, SMS, …) can ALSO answer this one with its X-RateLimit-* headers.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Register an external transfer destination ​

http
POST /v1/transfer-destinations

Registers number (strict E.164) with a label. From then on a blind transfer may target it; it still passes the whole GATE_PRE_DIAL chain. The registration instant is also the anchor the Reassigned Numbers Database is queried with for this number, and registering schedules that query (a transfer to it blocks with rnd.no_current_result.v1 until the answer arrives, in a cell with a real RND provider).

Tenant management only: a dd_ key or an account_admin login. A member login is 403 role_forbidden; a widget token (ddw_) is 403 forbidden. Who registered it is taken from the credential, never from the body. A number that is already a CURRENT registration of this tenant is 409 conflict; retire it first to register it again.

line_type is what the tenant declares the line to be, exactly as it declares it for a consumer it imports (same values, same default unknown). It is what the consent gate reads for a transfer to this number: landline lifts the prior-express-consent requirement, and nothing else — every other gate still runs. A blank or absent value is unknown. Change it later with PATCH.

Request body (JSON, required)

FieldTypeRequiredDescription
numberstringyesStrict E.164.
labelstringyesWhat the number is, for the operator (trimmed; blank is refused).
line_typemobile · landline · voip · unknownnoWhat the tenant declares the line to be — the same values and default as a contact's line_type on import. The consent gate reads it for a transfer to the number: landline lifts the prior-express-consent requirement; unknown, mobile and voip keep it.
json
{
  "number": "+13125550170",
  "label": "Legal department",
  "line_type": "landline"
}

Responses

201 ​

Registered.

FieldTypeRequiredDescription
transfer_destinationobject (TransferDestination)yes
transfer_destination.idstring (uuid)yesThe registration; the id to retire it by.
transfer_destination.numberstringyesStrict E.164.
transfer_destination.labelstringyes
transfer_destination.line_typemobile · landline · voip · unknownyesWhat the tenant declares the line to be — the same values and default as a contact's line_type on import. The consent gate reads it for a transfer to the number: landline lifts the prior-express-consent requirement; unknown, mobile and voip keep it.
transfer_destination.line_type_declared_atstring (date-time)yesWhen the current line_type was declared — registered_at until a PATCH changes it.
transfer_destination.line_type_declared_bystringyesWho declared the current line_type (same shape as registered_by).
transfer_destination.registered_atstring (date-time)yesWhen it was registered; the anchor the RND is queried with for this number.
transfer_destination.registered_bystringyesThe user or agent id of the credential that registered it, or tenant-api-key.

400 — BadRequest ​

Malformed request (shape/type errors, invalid filters, bad timestamps).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

403 ​

role_forbidden: a dashboard login without the account_admin role; forbidden: a widget token or a suspended tenant.

json
{
  "error": {
    "code": "role_forbidden",
    "message": "a dashboard login needs the account_admin role to manage transfer destinations"
  }
}

409 — Conflict ​

Invalid state transition, uniqueness conflict, or a seat that is busy/reserved.

json
{
  "error": {
    "code": "conflict",
    "message": "invalid state transition"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (core#2503) (code rate_limited). The request was not processed. Wait Retry-After seconds and retry; a refusal is not counted against the budget, so that wait is always enough. Bounded per API key and, above the keys, per tenant (api_tenant_requests_per_minute): a sibling key is unaffected until the tenant's keys together reach that ceiling. The quota applies to every operation reachable with a dd_ key, so the operations that name a more specific 429 (call control, dispositions, SMS, …) can ALSO answer this one with its X-RateLimit-* headers.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Declare the line type of a registered external transfer destination ​

http
PATCH /v1/transfer-destinations/{id}

Changes the line_type of the CURRENT registration id. The change is a new fact in the registration's append-only history, with who made it (taken from the credential) and when; the answer carries both as line_type_declared_by / line_type_declared_at. The registration itself does not move: same id, same registered_at, so the RND anchor of the number is unchanged. Declaring the value it already has writes nothing.

The next transfer to the number reads the new value. landline lifts the consent gate's prior-express-consent requirement and nothing else; unknown, mobile and voip keep it.

Only line_type is accepted (the number and the label are the registration: retire and register again to change them); any other key is 400. A value outside the list is 400. An id that is not a current registration of this tenant (unknown, retired, or another tenant's) is 404. Same roles as the registration.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.

Request body (JSON, required)

FieldTypeRequiredDescription
line_typemobile · landline · voip · unknownyesWhat the tenant declares the line to be — the same values and default as a contact's line_type on import. The consent gate reads it for a transfer to the number: landline lifts the prior-express-consent requirement; unknown, mobile and voip keep it.
json
{
  "line_type": "landline"
}

Responses

200 ​

The destination with its current line type.

FieldTypeRequiredDescription
transfer_destinationobject (TransferDestination)yes
transfer_destination.idstring (uuid)yesThe registration; the id to retire it by.
transfer_destination.numberstringyesStrict E.164.
transfer_destination.labelstringyes
transfer_destination.line_typemobile · landline · voip · unknownyesWhat the tenant declares the line to be — the same values and default as a contact's line_type on import. The consent gate reads it for a transfer to the number: landline lifts the prior-express-consent requirement; unknown, mobile and voip keep it.
transfer_destination.line_type_declared_atstring (date-time)yesWhen the current line_type was declared — registered_at until a PATCH changes it.
transfer_destination.line_type_declared_bystringyesWho declared the current line_type (same shape as registered_by).
transfer_destination.registered_atstring (date-time)yesWhen it was registered; the anchor the RND is queried with for this number.
transfer_destination.registered_bystringyesThe user or agent id of the credential that registered it, or tenant-api-key.

400 — BadRequest ​

Malformed request (shape/type errors, invalid filters, bad timestamps).

json
{
  "error": {
    "code": "bad_request",
    "message": "from must be RFC-3339"
  }
}

401 — Unauthorized ​

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

403 ​

role_forbidden: a dashboard login without the account_admin role; forbidden: a widget token or a suspended tenant.

404 — NotFound ​

Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).

json
{
  "error": {
    "code": "not_found",
    "message": "resource not found for this tenant"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (core#2503) (code rate_limited). The request was not processed. Wait Retry-After seconds and retry; a refusal is not counted against the budget, so that wait is always enough. Bounded per API key and, above the keys, per tenant (api_tenant_requests_per_minute): a sibling key is unaffected until the tenant's keys together reach that ceiling. The quota applies to every operation reachable with a dd_ key, so the operations that name a more specific 429 (call control, dispositions, SMS, …) can ALSO answer this one with its X-RateLimit-* headers.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Retire an external transfer destination ​

http
DELETE /v1/transfer-destinations/{id}

Retires the CURRENT registration id: a blind transfer to that number is refused again with transfer.destination_unregistered.v1. The history is append-only — the retirement is a new fact, and the answer is the destination that was retired. An id that is not a current registration of this tenant (unknown, already retired, or another tenant's) is 404. Same roles as the registration.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.

Responses

200 ​

Retired.

FieldTypeRequiredDescription
transfer_destinationobject (TransferDestination)yes
transfer_destination.idstring (uuid)yesThe registration; the id to retire it by.
transfer_destination.numberstringyesStrict E.164.
transfer_destination.labelstringyes
transfer_destination.line_typemobile · landline · voip · unknownyesWhat the tenant declares the line to be — the same values and default as a contact's line_type on import. The consent gate reads it for a transfer to the number: landline lifts the prior-express-consent requirement; unknown, mobile and voip keep it.
transfer_destination.line_type_declared_atstring (date-time)yesWhen the current line_type was declared — registered_at until a PATCH changes it.
transfer_destination.line_type_declared_bystringyesWho declared the current line_type (same shape as registered_by).
transfer_destination.registered_atstring (date-time)yesWhen it was registered; the anchor the RND is queried with for this number.
transfer_destination.registered_bystringyesThe user or agent id of the credential that registered it, or tenant-api-key.

401 — Unauthorized ​

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

403 ​

role_forbidden: a dashboard login without the account_admin role; forbidden: a widget token or a suspended tenant.

404 — NotFound ​

Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).

json
{
  "error": {
    "code": "not_found",
    "message": "resource not found for this tenant"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (core#2503) (code rate_limited). The request was not processed. Wait Retry-After seconds and retry; a refusal is not counted against the budget, so that wait is always enough. Bounded per API key and, above the keys, per tenant (api_tenant_requests_per_minute): a sibling key is unaffected until the tenant's keys together reach that ceiling. The quota applies to every operation reachable with a dd_ key, so the operations that name a more specific 429 (call control, dispositions, SMS, …) can ALSO answer this one with its X-RateLimit-* headers.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

Where a live call can be transferred, as a picker needs it ​

http
GET /v1/transfer/targets

The ONE read a transfer picker needs, and the only one of its kind a widget token (ddw_) may reach: the roster (GET /v1/agents) and the registry (GET /v1/transfer-destinations) stay 403 for it, because they answer more than a picker paints — emails, roles, credential state, the E.164 — and the widget lives inside third-party CRMs. Read-only, scoped to the caller's tenant. Three classes, each item carrying only what the picker shows and the value its transfer verb takes as destination:

  • agents: ACTIVE agents with a sip_extension — send the id (or the extension). presence is the seat state NOW and is informative: whether the seat is free is the transfer's own answer (422 destination_unavailable). A widget token does not see its own agent.
  • extensions: roster extensions with no agent behind them — send the sip_extension. Always empty today: the roster has no extension apart from its agents'. The class is in the answer so a picker does not change shape when one appears.
  • external_destinations: the tenant's CURRENT registered external destinations, id and label only — never the number. Send the id to POST /v1/calls/{call_id}/transfer; core resolves the E.164 itself and the transfer passes every gate a transfer to that number passes. The attended transfer does not take them.

Fail-closed: if the roster, the seat state or the registry cannot be read the answer is 503 transfer_targets_unavailable, never an empty list that would read as "there is nowhere to transfer to".

Responses

200 ​

The three classes (any of them may be empty).

FieldTypeRequiredDescription
agentsarray of objectyes
agents[].idstring (uuid)yesThe agent; a valid transfer destination.
agents[].namestringyes
agents[].sip_extensionstringyes
agents[].presenceoffline · available · ringing · on_call · wrap_up · pausedyesThe seat state now, as in the roster; informative only.
extensionsarray of objectyesRoster extensions with no agent behind them. Always empty today.
extensions[].sip_extensionstringyesA valid transfer destination.
extensions[].labelstringyes
external_destinationsarray of objectyesThe current registered external destinations. Never carries the number.
external_destinations[].idstring (uuid)yesThe registration; a valid blind-transfer destination, resolved by core to its E.164.
external_destinations[].labelstringyes
json
{
  "agents": [
    {
      "id": "2f1c9a2e-6b3d-4c1e-9a7f-0d4e5b6c7a81",
      "name": "Ana Ruiz",
      "sip_extension": "1002",
      "presence": "available"
    }
  ],
  "extensions": [],
  "external_destinations": [
    {
      "id": "7b0e1f3a-2c4d-4e5f-8a9b-1c2d3e4f5a6b",
      "label": "Legal department"
    }
  ]
}

401 — Unauthorized ​

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

403 — Forbidden ​

The tenant is suspended. A dashboard session (ddu_) of a tenant whose login policy (ADR 227) is in mode enforce answers 403 with the code login_policy_denied on a request from an address or at an hour the policy does not allow — the same body for both; the session is not revoked and a later request inside the policy goes through. A widget token (ddw_) of a seat is held to the same policy on every request (core#2406, flag widget_login_honors_policy). A synthetic (demo) tenant answers 403 with the code synthetic_tenant on every route that would reach a third party — manual dial, external transfer or conference leg, SMS send (core#2251, ADR #280 addendum). No payment changes it.

json
{
  "error": {
    "code": "forbidden",
    "message": "tenant is suspended"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (core#2503) (code rate_limited). The request was not processed. Wait Retry-After seconds and retry; a refusal is not counted against the budget, so that wait is always enough. Bounded per API key and, above the keys, per tenant (api_tenant_requests_per_minute): a sibling key is unaffected until the tenant's keys together reach that ceiling. The quota applies to every operation reachable with a dd_ key, so the operations that name a more specific 429 (call control, dispositions, SMS, …) can ALSO answer this one with its X-RateLimit-* headers.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

503 ​

transfer_targets_unavailable: the roster, the seat state or the registry could not be read. Retry; an empty class is never used to say this.

json
{
  "error": {
    "code": "transfer_targets_unavailable",
    "message": "transfer targets could not be read, retry"
  }
}

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.