Skip to content

Messaging ​

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

Español

10DLC brand and campaign registration through the SMS provider, and the numbers of an approved campaign. Provider fees land on the balance at cost.

Endpoints ​

MethodPathSummary
GET/v1/messaging/brandsList the tenant's 10DLC brands
POST/v1/messaging/brandsRegister a 10DLC brand with the SMS provider
GET/v1/messaging/brands/{id}Get a 10DLC brand
POST/v1/messaging/brands/{id}/refreshAsk the provider for the current status of a pending brand
GET/v1/messaging/campaignsList the tenant's 10DLC campaigns
POST/v1/messaging/campaignsRegister a 10DLC campaign for an approved brand
GET/v1/messaging/campaigns/{id}Get a 10DLC campaign
POST/v1/messaging/campaigns/{id}/refreshAsk the provider for the current status of a pending campaign
GET/v1/messaging/campaigns/{id}/numbersList the numbers of a campaign
POST/v1/messaging/campaigns/{id}/numbersAssign a number to an approved campaign

List the tenant's 10DLC brands ​

http
GET /v1/messaging/brands

Responses

200 ​

The brands, newest first.

FieldTypeRequiredDescription
brandsarray of MessagingBrandyes
brands[].idstring (uuid)yes
brands[].legal_namestringyes
brands[].display_namestringyes
brands[].entity_typeprivate_profit · public_profit · non_profit · government · sole_proprietoryes
brands[].ein_last4stringno
brands[].verticalstringno
brands[].websitestringno
brands[].emailstringno
brands[].phonestringno
brands[].streetstringno
brands[].citystringno
brands[].statestringno
brands[].postal_codestringno
brands[].countrystringno
brands[].vetting_requestedbooleanno
brands[].statuspending · approved · rejectedyes
brands[].rejection_reasonstring,nullno
brands[].provider_brand_idstringno
brands[].registrymock · telnyxyes
brands[].created_atstring (date-time)no
brands[].updated_atstring (date-time)no

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 (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. 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 (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 a 10DLC brand with the SMS provider ​

http
POST /v1/messaging/brands

Registers the tenant's brand with The Campaign Registry through the SMS provider and stores its status: pending, approved, or rejected with rejection_reason. The EIN travels to the provider and is not stored; the answer carries ein_last4. The provider's brand fee (and the vetting fee, when vetting_requested) is loaded to the balance at cost as messaging_fee ledger lines. registry says whether the registration was mock (simulated: nothing registered, nothing charged) or telnyx (real, gated by the flag messaging_10dlc_live, off by default). 503 fee_schedule_unset if the real provider has no fee schedule configured; nothing is registered then. 422 vetting_unsupported for vetting_requested: true with the real provider: the external vetting order is not implemented yet, so it is refused before any call or charge (the simulated registry accepts it).

Request body (JSON, required)

FieldTypeRequiredDescription
legal_namestringyes
display_namestringyes
entity_typeprivate_profit · public_profit · non_profit · government · sole_proprietoryes
einstringno9 digits; write-only, never stored in full.
verticalstringyes
websitestringno
emailstringyes
phonestringyes
streetstringyes
citystringyes
statestringyes
postal_codestringyes
countrystringno
vetting_requestedbooleanno

Responses

201 ​

The registered brand.

FieldTypeRequiredDescription
brandobject (MessagingBrand)yes
brand.idstring (uuid)yes
brand.legal_namestringyes
brand.display_namestringyes
brand.entity_typeprivate_profit · public_profit · non_profit · government · sole_proprietoryes
brand.ein_last4stringno
brand.verticalstringno
brand.websitestringno
brand.emailstringno
brand.phonestringno
brand.streetstringno
brand.citystringno
brand.statestringno
brand.postal_codestringno
brand.countrystringno
brand.vetting_requestedbooleanno
brand.statuspending · approved · rejectedyes
brand.rejection_reasonstring,nullno
brand.provider_brand_idstringno
brand.registrymock · telnyxyes
brand.created_atstring (date-time)no
brand.updated_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

422 — Unprocessable ​

Shape is fine, semantics are not (broken domain rule).

json
{
  "error": {
    "code": "unprocessable",
    "message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (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"
  }
}

502 — BadGateway ​

An upstream dependency (the AI runtime sandbox) returned an unexpected/failed response.

json
{
  "error": {
    "code": "bad_gateway",
    "message": "the AI runtime sandbox could not be reached"
  }
}

Get a 10DLC brand ​

http
GET /v1/messaging/brands/{id}

Parameters

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

Responses

200 ​

The brand.

FieldTypeRequiredDescription
brandobject (MessagingBrand)yes
brand.idstring (uuid)yes
brand.legal_namestringyes
brand.display_namestringyes
brand.entity_typeprivate_profit · public_profit · non_profit · government · sole_proprietoryes
brand.ein_last4stringno
brand.verticalstringno
brand.websitestringno
brand.emailstringno
brand.phonestringno
brand.streetstringno
brand.citystringno
brand.statestringno
brand.postal_codestringno
brand.countrystringno
brand.vetting_requestedbooleanno
brand.statuspending · approved · rejectedyes
brand.rejection_reasonstring,nullno
brand.provider_brand_idstringno
brand.registrymock · telnyxyes
brand.created_atstring (date-time)no
brand.updated_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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 (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"
  }
}

Ask the provider for the current status of a pending brand ​

http
POST /v1/messaging/brands/{id}/refresh

A brand that is no longer pending is returned as it is; the provider is not called.

Parameters

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

Responses

200 ​

The brand with its current status.

FieldTypeRequiredDescription
brandobject (MessagingBrand)yes
brand.idstring (uuid)yes
brand.legal_namestringyes
brand.display_namestringyes
brand.entity_typeprivate_profit · public_profit · non_profit · government · sole_proprietoryes
brand.ein_last4stringno
brand.verticalstringno
brand.websitestringno
brand.emailstringno
brand.phonestringno
brand.streetstringno
brand.citystringno
brand.statestringno
brand.postal_codestringno
brand.countrystringno
brand.vetting_requestedbooleanno
brand.statuspending · approved · rejectedyes
brand.rejection_reasonstring,nullno
brand.provider_brand_idstringno
brand.registrymock · telnyxyes
brand.created_atstring (date-time)no
brand.updated_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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 (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"
  }
}

502 — BadGateway ​

An upstream dependency (the AI runtime sandbox) returned an unexpected/failed response.

json
{
  "error": {
    "code": "bad_gateway",
    "message": "the AI runtime sandbox could not be reached"
  }
}

List the tenant's 10DLC campaigns ​

http
GET /v1/messaging/campaigns

Responses

200 ​

The campaigns, newest first.

FieldTypeRequiredDescription
campaignsarray of MessagingCampaignyes
campaigns[].idstring (uuid)yes
campaigns[].brand_idstring (uuid)yes
campaigns[].use_casestringyes
campaigns[].descriptionstringno
campaigns[].message_flowstringno
campaigns[].sample_messagesarray of stringno
campaigns[].help_messagestringno
campaigns[].optin_keywordsstringno
campaigns[].optout_keywordsstringno
campaigns[].statuspending · approved · rejectedyes
campaigns[].rejection_reasonstring,nullno
campaigns[].provider_campaign_idstringno
campaigns[].monthly_fee_usdstringnoDecimal string; the provider's monthly fee, at cost.
campaigns[].registrymock · telnyxyes
campaigns[].created_atstring (date-time)no
campaigns[].updated_atstring (date-time)no

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 (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. 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 (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 a 10DLC campaign for an approved brand ​

http
POST /v1/messaging/campaigns

Registers a campaign for a brand whose status is approved (409 brand_not_approved otherwise) and stores its status. The provider's monthly campaign fee for the first month is loaded to the balance at cost as a messaging_fee line; later months are billed exactly once per month. A text from a US local number leaves only through a number assigned to an approved campaign (422 sms_10dlc_campaign_required on POST /v1/sms/messages otherwise).

Request body (JSON, required)

FieldTypeRequiredDescription
brand_idstring (uuid)yes
use_casestringyes
descriptionstringyes
message_flowstringyes
sample_messagesarray of stringyes
help_messagestringno
optin_keywordsstringno
optout_keywordsstringno

Responses

201 ​

The registered campaign.

FieldTypeRequiredDescription
campaignobject (MessagingCampaign)yes
campaign.idstring (uuid)yes
campaign.brand_idstring (uuid)yes
campaign.use_casestringyes
campaign.descriptionstringno
campaign.message_flowstringno
campaign.sample_messagesarray of stringno
campaign.help_messagestringno
campaign.optin_keywordsstringno
campaign.optout_keywordsstringno
campaign.statuspending · approved · rejectedyes
campaign.rejection_reasonstring,nullno
campaign.provider_campaign_idstringno
campaign.monthly_fee_usdstringnoDecimal string; the provider's monthly fee, at cost.
campaign.registrymock · telnyxyes
campaign.created_atstring (date-time)no
campaign.updated_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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"
  }
}

409 — Conflict ​

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

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

422 — Unprocessable ​

Shape is fine, semantics are not (broken domain rule).

json
{
  "error": {
    "code": "unprocessable",
    "message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (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"
  }
}

502 — BadGateway ​

An upstream dependency (the AI runtime sandbox) returned an unexpected/failed response.

json
{
  "error": {
    "code": "bad_gateway",
    "message": "the AI runtime sandbox could not be reached"
  }
}

Get a 10DLC campaign ​

http
GET /v1/messaging/campaigns/{id}

Parameters

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

Responses

200 ​

The campaign.

FieldTypeRequiredDescription
campaignobject (MessagingCampaign)yes
campaign.idstring (uuid)yes
campaign.brand_idstring (uuid)yes
campaign.use_casestringyes
campaign.descriptionstringno
campaign.message_flowstringno
campaign.sample_messagesarray of stringno
campaign.help_messagestringno
campaign.optin_keywordsstringno
campaign.optout_keywordsstringno
campaign.statuspending · approved · rejectedyes
campaign.rejection_reasonstring,nullno
campaign.provider_campaign_idstringno
campaign.monthly_fee_usdstringnoDecimal string; the provider's monthly fee, at cost.
campaign.registrymock · telnyxyes
campaign.created_atstring (date-time)no
campaign.updated_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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 (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"
  }
}

Ask the provider for the current status of a pending campaign ​

http
POST /v1/messaging/campaigns/{id}/refresh

A campaign that is no longer pending is returned as it is; the provider is not called.

Parameters

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

Responses

200 ​

The campaign with its current status.

FieldTypeRequiredDescription
campaignobject (MessagingCampaign)yes
campaign.idstring (uuid)yes
campaign.brand_idstring (uuid)yes
campaign.use_casestringyes
campaign.descriptionstringno
campaign.message_flowstringno
campaign.sample_messagesarray of stringno
campaign.help_messagestringno
campaign.optin_keywordsstringno
campaign.optout_keywordsstringno
campaign.statuspending · approved · rejectedyes
campaign.rejection_reasonstring,nullno
campaign.provider_campaign_idstringno
campaign.monthly_fee_usdstringnoDecimal string; the provider's monthly fee, at cost.
campaign.registrymock · telnyxyes
campaign.created_atstring (date-time)no
campaign.updated_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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 (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"
  }
}

502 — BadGateway ​

An upstream dependency (the AI runtime sandbox) returned an unexpected/failed response.

json
{
  "error": {
    "code": "bad_gateway",
    "message": "the AI runtime sandbox could not be reached"
  }
}

List the numbers of a campaign ​

http
GET /v1/messaging/campaigns/{id}/numbers

Parameters

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

Responses

200 ​

The numbers.

FieldTypeRequiredDescription
numbersarray of MessagingCampaignNumberyes
numbers[].idstring (uuid)yes
numbers[].campaign_idstring (uuid)yes
numbers[].e164stringyes
numbers[].created_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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 (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"
  }
}

Assign a number to an approved campaign ​

http
POST /v1/messaging/campaigns/{id}/numbers

The number must be an active number of the tenant (422 number_not_owned) and the campaign approved (409 campaign_not_approved). Repeating the same assignment returns the existing row; a number of another campaign is 409 number_assigned_elsewhere.

Parameters

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

Request body (JSON, required)

FieldTypeRequiredDescription
e164stringyes

Responses

201 ​

The assignment.

FieldTypeRequiredDescription
numberobject (MessagingCampaignNumber)yes
number.idstring (uuid)yes
number.campaign_idstring (uuid)yes
number.e164stringyes
number.created_atstring (date-time)no

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 — 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 (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. No payment changes it.

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

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"
  }
}

409 — Conflict ​

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

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

422 — Unprocessable ​

Shape is fine, semantics are not (broken domain rule).

json
{
  "error": {
    "code": "unprocessable",
    "message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (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"
  }
}

502 — BadGateway ​

An upstream dependency (the AI runtime sandbox) returned an unexpected/failed response.

json
{
  "error": {
    "code": "bad_gateway",
    "message": "the AI runtime sandbox could not be reached"
  }
}

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