Skip to content

DIDs (caller IDs) ​

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

DID registry, CDR-computed health, launch checklist and carrier tickets.

Endpoints ​

MethodPathSummary
GET/v1/didsDID registry
POST/v1/didsRegister a tenant-owned number
GET/v1/dids/{id}Fetch one DID
PATCH/v1/dids/{id}Update metadata/status (e164 immutable)
DELETE/v1/dids/{id}Retire a DID (rows never delete)
GET/v1/dids/{id}/healthCDR-computed DID health
GET/v1/dids/{id}/launch-checklistLaunch checklist of a number
POST/v1/dids/{id}/launch-checklist/{step}/confirmationsConfirm (or un-confirm) a manual launch step
GET/v1/dids/{id}/carrier-ticketsCarrier tickets of a number
POST/v1/dids/{id}/carrier-ticketsOpen a carrier ticket for a number
POST/v1/dids/{id}/carrier-tickets/{ticket_id}/eventsAdd a note and/or move a carrier ticket's status

DID registry ​

http
GET /v1/dids

Parameters

NameInTypeRequiredDescription
statusqueryactive · quarantine · retiredno

Responses

200 ​

Registry rows ordered by e164.

FieldTypeRequiredDescription
didsarray of DIDyes
dids[].idstring (uuid)no
dids[].tenant_idstring (uuid)no
dids[].e164stringnoImmutable after creation.
dids[].npastring | nullno
dids[].us_statestring | nullno
dids[].attestationA · B · C · unknownnoSTIR/SHAKEN level the carrier signs. Read-only for the tenant: only the platform admin sets it (core#2415).
dids[].statusactive · quarantine · retiredno
dids[].verified_atstring (date-time) | nullnoProof of ownership (core#2415, F-03). null = self-declared, never proved; house DIDs are born verified.
dids[].verification_methodhouse · otp · admin | nullnoHow the ownership was proved; null while verified_at is null.
dids[].originhouse · customernohouse rows are admin-minted platform inventory; the tenant surface only creates customer.
dids[].labelsarray of stringno
dids[].notesstring | nullno
dids[].created_atstring (date-time)no
dids[].updated_atstring (date-time)no
truncatedbooleanyestrue when there are more rows than the 2 000 numbers the listing returns: the response is a prefix in the documented order, not the whole set.

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

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 a tenant-owned number ​

http
POST /v1/dids

e164 unique per tenant → 409 conflict on duplicates; npa is derived from +1 numbers when omitted. The tenant surface can only ever create origin: customer rows — house inventory is admin-minted (POST /v1/admin/tenants/{id}/dids).

Request body (JSON, required)

FieldTypeRequiredDescription
e164stringyesE.164 (immutable after creation).
npastringno
us_statestringno
attestationA · B · C · unknownnoAccepted and IGNORED (core#2415, F-03): the STIR/SHAKEN attestation is a claim only the platform admin records (POST /v1/admin/tenants/{id}/dids/{did_id}/verify); it never changes through this route, which answers as if the key were absent.
statusactive · quarantine · retiredno
labelsarray of stringno
notesstringno
originhouse · customernoAccepted and IGNORED FOR SAFETY: a tenant can never mint origin: house, so any value here is overridden with customer. The enum mirrors the response schema because a GET of a house DID returns house — sending that value back does not fail, it has no effect. Declared so that THIS key does not become a 400 now that unknown keys are. It does NOT make the whole rendered object round-trip: id, tenant_id, created_at and updated_at are not accepted and DO get the 400.
json
{
  "e164": "+13125550142",
  "us_state": "IL"
}

Responses

201 ​

Registered DID.

FieldTypeRequiredDescription
didobject (DID)yes
did.idstring (uuid)no
did.tenant_idstring (uuid)no
did.e164stringnoImmutable after creation.
did.npastring | nullno
did.us_statestring | nullno
did.attestationA · B · C · unknownnoSTIR/SHAKEN level the carrier signs. Read-only for the tenant: only the platform admin sets it (core#2415).
did.statusactive · quarantine · retiredno
did.verified_atstring (date-time) | nullnoProof of ownership (core#2415, F-03). null = self-declared, never proved; house DIDs are born verified.
did.verification_methodhouse · otp · admin | nullnoHow the ownership was proved; null while verified_at is null.
did.originhouse · customernohouse rows are admin-minted platform inventory; the tenant surface only creates customer.
did.labelsarray of stringno
did.notesstring | nullno
did.created_atstring (date-time)no
did.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"
  }
}

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

Fetch one DID ​

http
GET /v1/dids/{id}

Parameters

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

Responses

200 ​

The DID.

FieldTypeRequiredDescription
didobject (DID)yes
did.idstring (uuid)no
did.tenant_idstring (uuid)no
did.e164stringnoImmutable after creation.
did.npastring | nullno
did.us_statestring | nullno
did.attestationA · B · C · unknownnoSTIR/SHAKEN level the carrier signs. Read-only for the tenant: only the platform admin sets it (core#2415).
did.statusactive · quarantine · retiredno
did.verified_atstring (date-time) | nullnoProof of ownership (core#2415, F-03). null = self-declared, never proved; house DIDs are born verified.
did.verification_methodhouse · otp · admin | nullnoHow the ownership was proved; null while verified_at is null.
did.originhouse · customernohouse rows are admin-minted platform inventory; the tenant surface only creates customer.
did.labelsarray of stringno
did.notesstring | nullno
did.created_atstring (date-time)no
did.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"
  }
}

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

Update metadata/status (e164 immutable) ​

http
PATCH /v1/dids/{id}

A different number is a different DID; CDR history is never re-attributed.

Parameters

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

Request body (JSON, required)

FieldTypeRequiredDescription
npastringno
us_statestringno
attestationA · B · C · unknownnoAccepted and IGNORED (core#2415, F-03): the STIR/SHAKEN attestation is a claim only the platform admin records (POST /v1/admin/tenants/{id}/dids/{did_id}/verify); it never changes through this route, which answers as if the key were absent.
statusactive · quarantine · retiredno
labelsarray of stringno
notesstringno
originhouse · customernoAccepted and IGNORED FOR SAFETY, same as on POST /v1/dids: origin is immutable and never cast, so any value here has no effect. Sending back the value a GET returned does not fail; the rest of the rendered object still does (id, tenant_id, e164, created_at, updated_at are a 400).
json
{
  "attestation": "A",
  "labels": [
    "postman"
  ],
  "notes": "postman smoke"
}

Responses

200 ​

Updated DID.

FieldTypeRequiredDescription
didobject (DID)yes
did.idstring (uuid)no
did.tenant_idstring (uuid)no
did.e164stringnoImmutable after creation.
did.npastring | nullno
did.us_statestring | nullno
did.attestationA · B · C · unknownnoSTIR/SHAKEN level the carrier signs. Read-only for the tenant: only the platform admin sets it (core#2415).
did.statusactive · quarantine · retiredno
did.verified_atstring (date-time) | nullnoProof of ownership (core#2415, F-03). null = self-declared, never proved; house DIDs are born verified.
did.verification_methodhouse · otp · admin | nullnoHow the ownership was proved; null while verified_at is null.
did.originhouse · customernohouse rows are admin-minted platform inventory; the tenant surface only creates customer.
did.labelsarray of stringno
did.notesstring | nullno
did.created_atstring (date-time)no
did.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"
  }
}

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 a DID (rows never delete) ​

http
DELETE /v1/dids/{id}

Sets status retired, idempotent. Rows never leave the registry (DELETE is revoked in Postgres): CDR from_number provenance must keep resolving for the Defense Packet.

Parameters

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

Responses

200 ​

The retired DID.

FieldTypeRequiredDescription
didobject (DID)yes
did.idstring (uuid)no
did.tenant_idstring (uuid)no
did.e164stringnoImmutable after creation.
did.npastring | nullno
did.us_statestring | nullno
did.attestationA · B · C · unknownnoSTIR/SHAKEN level the carrier signs. Read-only for the tenant: only the platform admin sets it (core#2415).
did.statusactive · quarantine · retiredno
did.verified_atstring (date-time) | nullnoProof of ownership (core#2415, F-03). null = self-declared, never proved; house DIDs are born verified.
did.verification_methodhouse · otp · admin | nullnoHow the ownership was proved; null while verified_at is null.
did.originhouse · customernohouse rows are admin-minted platform inventory; the tenant surface only creates customer.
did.labelsarray of stringno
did.notesstring | nullno
did.created_atstring (date-time)no
did.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"
  }
}

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

CDR-computed DID health ​

http
GET /v1/dids/{id}/health

Health is COMPUTED from the append-only CDR at read time — every number is a row count, never a cached score. short_call_rate (answered calls ended in under 15 s / answered) is the strongest CDR-side "spam likely" signal. Rates are null when the denominator is zero. reputation.hiya/reputation.tns are explicit nulls until an external feed is contracted. A retired DID still serves health (provenance outlives rotation).

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.
fromquerystring (date-time)noRFC 3339 lower bound (endpoint-specific field; default trailing 30 UTC days where noted).
toquerystring (date-time)noRFC 3339 upper bound.

Responses

200 ​

Computed health for the period (default trailing 30 UTC days).

FieldTypeRequiredDescription
did_idstring (uuid)no
e164stringno
statusactive · quarantine · retiredno
periodobjectno
period.fromstring (date-time)no
period.tostring (date-time)no
dialsintegerno
answeredintegerno
answer_ratenumber | nullno
short_callsintegerno
short_call_ratenumber | nullno
short_call_secondsintegerno
dailyarray of objectno
daily[].datestring (date)no
daily[].dialsintegerno
daily[].answeredintegerno
daily[].short_callsintegerno
reputationobjectnoExplicit nulls until an external feed is contracted — render as pending, not zero.
reputation.hiyanumber | nullno
reputation.tnsnumber | nullno
reputation.notestringno
json
{
  "did_id": "4e3d2c1b-0a9f-4e8d-7c6b-5a4f3e2d1c0b",
  "e164": "+13125550184",
  "status": "active",
  "period": {
    "from": "2026-06-05T00:00:00Z",
    "to": "2026-07-05T00:00:00Z"
  },
  "dials": 412,
  "answered": 67,
  "answer_rate": 0.1626,
  "short_calls": 24,
  "short_call_rate": 0.3582,
  "short_call_seconds": 15,
  "daily": [
    {
      "date": "2026-06-01",
      "dials": 80,
      "answered": 14,
      "short_calls": 5
    }
  ],
  "reputation": {
    "hiya": null,
    "tns": null,
    "note": "external reputation feeds not integrated yet"
  }
}

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

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

Launch checklist of a number ​

http
GET /v1/dids/{id}/launch-checklist

Four steps, each stating its source. attestation is derived from the registry (A/B/C = done; the value is what the tenant declared, not a carrier verdict). warmup is derived from the number's own CDR: done once it has dialled on warmup_min_active_days distinct UTC days of the trailing 30. registration and cnam are manual: the platform has no data for them, so they are what the operator confirmed. No external reputation feed is consulted.

Parameters

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

Responses

200 ​

The checklist.

FieldTypeRequiredDescription
did_idstring (uuid)yes
e164stringyes
completebooleanyesAll four steps are done.
warmup_min_active_daysintegeryes
stepsarray of objectyes
steps[].stepregistration · cnam · attestation · warmupyes
steps[].sourcederived · manualyesWhere the state comes from; manual is the operator's word.
steps[].statedone · pendingyes
steps[].evidenceobjectyesderived: {origin, ...} with the values behind the state (dids.attestation + value; cdr + active_days, required_days, dials). manual: the latest {confirmed_at, note}, empty if never confirmed.

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

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

Confirm (or un-confirm) a manual launch step ​

http
POST /v1/dids/{id}/launch-checklist/{step}/confirmations

Appends a confirmation for a manual step (registration or cnam); the latest one is the step's state and none is ever edited or deleted. A derived step (attestation, warmup) is 422: the platform computes it. An unknown step is 404. Returns the fresh checklist.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.
steppathregistration · cnam · attestation · warmupyes

Request body (JSON, required)

FieldTypeRequiredDescription
confirmedbooleanyes
notestringno

Responses

201 ​

The checklist after the confirmation.

FieldTypeRequiredDescription
did_idstring (uuid)yes
e164stringyes
completebooleanyesAll four steps are done.
warmup_min_active_daysintegeryes
stepsarray of objectyes
steps[].stepregistration · cnam · attestation · warmupyes
steps[].sourcederived · manualyesWhere the state comes from; manual is the operator's word.
steps[].statedone · pendingyes
steps[].evidenceobjectyesderived: {origin, ...} with the values behind the state (dids.attestation + value; cdr + active_days, required_days, dials). manual: the latest {confirmed_at, note}, empty if never confirmed.

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

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

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

Carrier tickets of a number ​

http
GET /v1/dids/{id}/carrier-tickets

Tickets the tenant keeps about this number with its carrier, most recently active first, each with its full append-only thread. A retired number still serves its tickets.

Parameters

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

Responses

200 ​

The tickets.

FieldTypeRequiredDescription
ticketsarray of DidCarrierTicketyes
tickets[].idstring (uuid)yes
tickets[].did_idstring (uuid)yes
tickets[].carrierstringyes
tickets[].subjectstringyes
tickets[].referencestringyes
tickets[].statusopen · in_progress · resolved · closedyes
tickets[].opened_atstring (date-time)yes
tickets[].updated_atstring (date-time)yes
tickets[].eventsarray of objectyesThe append-only thread, oldest first; the first has kind opened.
tickets[].events[].idstring (uuid)yes
tickets[].events[].kindopened · updateyes
tickets[].events[].statusopen · in_progress · resolved · closedyes
tickets[].events[].notestringyes
tickets[].events[].created_atstring (date-time)yes

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

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

Open a carrier ticket for a number ​

http
POST /v1/dids/{id}/carrier-tickets

Parameters

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

Request body (JSON, required)

FieldTypeRequiredDescription
carrierstringyes
subjectstringyes
referencestringnoThe carrier's own ticket reference, if it gave one.
notestringno

Responses

201 ​

The opened ticket (status open).

FieldTypeRequiredDescription
ticketobject (DidCarrierTicket)yes
ticket.idstring (uuid)yes
ticket.did_idstring (uuid)yes
ticket.carrierstringyes
ticket.subjectstringyes
ticket.referencestringyes
ticket.statusopen · in_progress · resolved · closedyes
ticket.opened_atstring (date-time)yes
ticket.updated_atstring (date-time)yes
ticket.eventsarray of objectyesThe append-only thread, oldest first; the first has kind opened.
ticket.events[].idstring (uuid)yes
ticket.events[].kindopened · updateyes
ticket.events[].statusopen · in_progress · resolved · closedyes
ticket.events[].notestringyes
ticket.events[].created_atstring (date-time)yes

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

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

Add a note and/or move a carrier ticket's status ​

http
POST /v1/dids/{id}/carrier-tickets/{ticket_id}/events

Appends to the ticket's thread; nothing is edited or deleted. status defaults to the current one. An event with neither a note nor a status change is 400.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.
ticket_idpathstring (uuid)yesTicket UUID. It must belong to the number in the path, else 404.

Request body (JSON, required)

FieldTypeRequiredDescription
statusopen · in_progress · resolved · closedno
notestringno

Responses

201 ​

The ticket with the new event appended.

FieldTypeRequiredDescription
ticketobject (DidCarrierTicket)yes
ticket.idstring (uuid)yes
ticket.did_idstring (uuid)yes
ticket.carrierstringyes
ticket.subjectstringyes
ticket.referencestringyes
ticket.statusopen · in_progress · resolved · closedyes
ticket.opened_atstring (date-time)yes
ticket.updated_atstring (date-time)yes
ticket.eventsarray of objectyesThe append-only thread, oldest first; the first has kind opened.
ticket.events[].idstring (uuid)yes
ticket.events[].kindopened · updateyes
ticket.events[].statusopen · in_progress · resolved · closedyes
ticket.events[].notestringyes
ticket.events[].created_atstring (date-time)yes

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

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

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