Skip to content

Carriers (SIP trunks)

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

SIP trunk registry (BYOC/house) + SBC provisioning.

Endpoints

MethodPathSummary
GET/v1/carriersList carriers (trunks)
POST/v1/carriersRegister a SIP trunk
GET/v1/carriers/{id}Fetch one carrier
POST/v1/carriers/{id}/provisionPush the trunk to the SIP edge (Kamailio)
POST/v1/carriers/{id}/revokeRemove the trunk from the SIP edge

List carriers (trunks)

http
GET /v1/carriers

Responses

200

Own trunks + inherited empresa-wide trunks.

FieldTypeRequiredDescription
carriersarray of Carrieryes
carriers[].idstring (uuid)no
carriers[].account_idstring (uuid)no
carriers[].tenant_idstring (uuid) | nullnonull = empresa-wide (shared) trunk inherited by every sede.
carriers[].scopeempresa · sedenoDerived from tenant_id.
carriers[].kindhouse · byocno
carriers[].namestringno
carriers[].sip_proxystring | nullno
carriers[].source_ipsarray of stringno
carriers[].statusactive · disabled · retiredno
carriers[].created_atstring (date-time)no
carriers[].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).

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

Register a SIP trunk

http
POST /v1/carriers

scope: "empresa" creates a shared trunk (tenant_id null) inherited by every sede of the account; scope: "sede" (default) creates a per-sede trunk. account_id/tenant_id are NEVER taken from the body — the router injects them from the session (a sede cannot forge another account's carrier).

scope: "empresa" additionally requires an account_admin user (see User.role) — it overrides the routing of every sibling sede, so a member or a machine key gets 403. The RLS policy enforces it, not only this route.

Since MT-SEC-49 MT.3b an unrecognized key is a 400 naming it. account_id is the one exception and is documented below: it is accepted and ignored. tenant_id is NOT — sending it is a cross-tenant forgery attempt and gets the 400.

Request body (JSON, required)

FieldTypeRequiredDescription
kindhouse · byocyes
namestringyes
sip_proxystringno
source_ipsarray of stringno
statusactive · disabled · retiredno
scopesede · empresanoRequest-only field; responses derive it from tenant_id.
account_idstring (uuid)noAccepted and IGNORED ON PURPOSE: the account always comes from the session, so a forged value 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 trunk round-trip: id, created_at and updated_at are a 400, and tenant_id is a 400 DELIBERATELY — sending it is a cross-tenant forgery attempt and gets named, not dropped in silence.
json
{
  "kind": "byoc",
  "name": "postman-trunk-1751600000",
  "sip_proxy": "sip:pstn.example.test:5060",
  "source_ips": [
    "203.0.113.9"
  ]
}

Responses

201

Registered trunk.

FieldTypeRequiredDescription
carrierobject (Carrier)yes
carrier.idstring (uuid)no
carrier.account_idstring (uuid)no
carrier.tenant_idstring (uuid) | nullnonull = empresa-wide (shared) trunk inherited by every sede.
carrier.scopeempresa · sedenoDerived from tenant_id.
carrier.kindhouse · byocno
carrier.namestringno
carrier.sip_proxystring | nullno
carrier.source_ipsarray of stringno
carrier.statusactive · disabled · retiredno
carrier.created_atstring (date-time)no
carrier.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).

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

403 — Forbidden

The tenant is suspended.

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

Fetch one carrier

http
GET /v1/carriers/{id}

Parameters

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

Responses

200

The carrier.

FieldTypeRequiredDescription
carrierobject (Carrier)yes
carrier.idstring (uuid)no
carrier.account_idstring (uuid)no
carrier.tenant_idstring (uuid) | nullnonull = empresa-wide (shared) trunk inherited by every sede.
carrier.scopeempresa · sedenoDerived from tenant_id.
carrier.kindhouse · byocno
carrier.namestringno
carrier.sip_proxystring | nullno
carrier.source_ipsarray of stringno
carrier.statusactive · disabled · retiredno
carrier.created_atstring (date-time)no
carrier.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).

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

Push the trunk to the SIP edge (Kamailio)

http
POST /v1/carriers/{id}/provision

Only a BYOC carrier can be provisioned (422 otherwise). With no KAMAILIO_RPC_URL configured (dev without the telecom harness) the answer is 200 {"status": "sbc_disabled"} — the row's intent is recorded, it just is not pushed to a live edge.

Parameters

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

Responses

200

Provisioned (or SBC disabled in this environment).

FieldTypeRequiredDescription
statusprovisioned · sbc_disabledyessbc_disabled = no KAMAILIO_RPC_URL in this environment; the row's intent is recorded, not pushed.

provisioned

json
{
  "status": "provisioned"
}

sbc_disabled

json
{
  "status": "sbc_disabled"
}

401 — Unauthorized

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).

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 must be at least 10 minutes out"
  }
}

502 — SbcError

The SIP edge rejected the provisioning request.

json
{
  "error": {
    "code": "sbc_error",
    "message": "the SIP edge rejected the provisioning request"
  }
}

Remove the trunk from the SIP edge

http
POST /v1/carriers/{id}/revoke

Parameters

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

Responses

200

Revoked (or SBC disabled in this environment).

FieldTypeRequiredDescription
statusprovisioned · sbc_disabledyessbc_disabled = no KAMAILIO_RPC_URL in this environment; the row's intent is recorded, not pushed.

401 — Unauthorized

Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).

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 must be at least 10 minutes out"
  }
}

502 — SbcError

The SIP edge rejected the provisioning request.

json
{
  "error": {
    "code": "sbc_error",
    "message": "the SIP edge rejected the provisioning request"
  }
}

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