Skip to content

Carriers (troncales SIP) ​

Generado desde DialerDigital/core commit 0fc18008b3c8 — especificación openapi/dialer-v1.yaml v0.134.0. No se edita a mano; consulta Cómo se construye esta referencia. Descargar la especificación OpenAPI.

English version

Traducción en curso

Esta referencia se genera desde la especificación OpenAPI. Las cadenas que todavía no tienen traducción aparecen en inglés.

Registro de troncales SIP (BYOC/house) + aprovisionamiento del SBC.

Endpoints ​

MétodoRutaResumen
GET/v1/carriersListar carriers (troncales)
POST/v1/carriersRegistrar una troncal SIP
GET/v1/carriers/{id}Obtener un carrier
PATCH/v1/carriers/{id}Reemplazar la lista de destinos permitidos y los límites contractuales de una troncal BYOC
GET/v1/carriers/{id}/healthSalud de una troncal BYOC (up | down | unknown)
GET/v1/carriers/{id}/health/eventsHistorial de las transiciones de salud de una troncal BYOC
POST/v1/carriers/{id}/provisionEmpujar la troncal al borde SIP (Kamailio)
POST/v1/carriers/{id}/revokeQuitar la troncal del borde SIP

Listar carriers (troncales) ​

http
GET /v1/carriers

Respuestas

200 ​

Troncales propias + troncales heredadas de toda la empresa.

CampoTipoObligatorioDescripción
carriersarreglo de Carriersí
carriers[].idstring (uuid)no
carriers[].account_idstring (uuid)no
carriers[].tenant_idstring (uuid) | nullnonull = troncal de toda la empresa (compartida), heredada por cada sede.
carriers[].scopeempresa · sedenoDerivado de tenant_id.
carriers[].kindhouse · byocno
carriers[].namestringno
carriers[].sip_proxystring | nullno
carriers[].source_ipsarreglo de stringno
carriers[].allowed_destinationsarreglo de stringnoLa lista de destinos permitidos de una troncal BYOC (core#1281, ADR #172): los números E.164 exactos a los que puede marcar. [] = sin restricción. Se almacena cifrada bajo la clave de alcance de la troncal.
carriers[].max_concurrent_callsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): las llamadas simultáneas que admite. Una marcación que encuentra tantas llamadas vivas en la troncal se rechaza con egress.carrier_channels_full.v1. null = sin límite.
carriers[].max_daily_attemptsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): intentos de marcación por día UTC (de 00:00 a 24:00 UTC). Una marcación que encuentra el día agotado se rechaza con egress.daily_attempts_exhausted.v1. null = sin límite.
carriers[].allowed_dial_modesarreglo de manual · preview · progressive · predictive | nullnoLímite contractual de una troncal BYOC (core#2176): los modos de marcación que permite. Una marcación en otro modo (incluida la marcación masiva sin agente) se rechaza con egress.dial_mode_not_allowed.v1. null = sin restricción; una lista vacía es un 400.
carriers[].srtpoff · offer · requirenoPolítica SRTP del tramo de salida de la troncal (core#2333, SBC kamailio#189). off (por defecto) es RTP plano, exactamente como antes. offer ofrece SRTP y acepta RTP plano en la respuesta; require rechaza una respuesta que no sea SRTP. offer y require necesitan un sip_proxy TLS (sips: o ;transport=tls) y un sip_proxy sin parámetro srtp propio: cualquier otra cosa es un 400. Core escribe ;srtp=<mode> en el borde solo para offer/require.
carriers[].statusactive · disabled · retiredno
carriers[].created_atstring (date-time)no
carriers[].updated_atstring (date-time)no

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

Registrar una troncal SIP ​

http
POST /v1/carriers

scope: "empresa" crea una troncal compartida (tenant_id null) heredada por cada sede de la cuenta; scope: "sede" (por defecto) crea una troncal por sede. account_id/tenant_id NUNCA se toman del cuerpo: el router los inyecta desde la sesión (una sede no puede falsificar el carrier de otra cuenta).

scope: "empresa" además requiere un usuario account_admin (véase User.role): anula el enrutamiento de todas las sedes hermanas, así que un member o una clave de máquina recibe 403. La política de RLS lo hace cumplir, no solo esta ruta.

Desde MT-SEC-49 MT.3b, una clave no reconocida es un 400 que la nombra. account_id es la única excepción y se documenta abajo: se acepta y se ignora. tenant_id NO: enviarlo es un intento de falsificación entre tenants y recibe el 400.

source_ips se acepta con ambos alcances. Con scope: "empresa" la troncal cursa también tráfico ENTRANTE (ADR #203): las IP identifican a la cuenta en el borde SIP (byoctenant[<ip>] = acct:<account>), y el destino lo decide el número marcado, un DID de cualquier sede de la cuenta, no la IP. Una dirección pertenece a UNA sola troncal en toda la plataforma: una ya asignada a otra troncal (de cualquier sede o de empresa) es un 400 (already assigned to another trunk). Se acepta una lista vacía: la troncal de toda la empresa cursa entonces solo tráfico SALIENTE.

allowed_destinations (core#1281, ADR #172) es la lista de destinos permitidos de una troncal BYOC: números E.164 exactos, 100 como máximo, sin duplicados. Mientras cualquier troncal BYOC activa por la que sale una sede declare una, la plataforma no cursa ninguna llamada de esa sede (campaña, marcación manual, rellamada o tramo de transferencia externa) hacia un número fuera de todas las listas declaradas; cada rechazo es una fila tenant_policy en las estadísticas de bloqueos con la regla egress.destination_not_allowed.v1. Una lista que no se puede leer también rechaza (egress.destination_allowlist_unavailable.v1). Ausente o [] = sin restricción. En una troncal house, una lista no vacía es un 400.

max_concurrent_calls, max_daily_attempts y allowed_dial_modes (core#2176) son los límites contractuales de una troncal BYOC: las condiciones que el cliente tiene con el carrier. Los tres son opcionales y null/ausente significa sin límite, que es como se comportaba una troncal antes de que existieran. La plataforma los aplica en cada marcación que sale por la troncal, en modo fail-closed: una marcación cuyo modo está fuera de allowed_dial_modes, o que encuentra la troncal con max_concurrent_calls llamadas vivas o con max_daily_attempts intentos del día UTC, no se cursa y se registra como una fila tenant_policy en las estadísticas de bloqueos con la regla egress.dial_mode_not_allowed.v1, egress.carrier_channels_full.v1 o egress.daily_attempts_exhausted.v1. Un límite que no se puede leer o contar también rechaza (egress.trunk_limits_unavailable.v1). Ambos recuentos se derivan de los registros de llamadas, y una troncal de toda la empresa cuenta las llamadas de todas las sedes de la cuenta. En una troncal house, un límite es un 400.

El destino de una troncal BYOC (sip_proxy, core#2345) es el siguiente salto del borde SIP tal como está escrito, así que tiene que ser un endpoint sip:/sips: público. Es un 422 cuando está mal formado o apunta a la red propia de la plataforma: una dirección de loopback, privada (RFC 1918), de enlace local, CGNAT (100.64/10), multicast o no especificada, un nombre interno de la celda (localhost, una sola etiqueta, .local, .internal, .svc, ...) o un rango que la celda declara como propio. Es un 409 con el código trunk_endpoint_shared cuando su host y puerto ya son el endpoint de un carrier de la plataforma o de una troncal BYOC activa de otra cuenta: dos cuentas no pueden compartir un destino. Dos sedes de la MISMA cuenta sí pueden. Una troncal house y una troncal BYOC sin sip_proxy no se comprueban, y no se escribe nada cuando la respuesta es un error.

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
kindhouse · byocsí
namestringsí
sip_proxystringno
source_ipsarreglo de stringnoDirecciones IPv4/IPv6 desnudas de los pares de la troncal (ACL de entrada), almacenadas canonicalizadas y únicas en toda la plataforma, con cualquiera de los dos alcances. Con scope: "empresa" identifican a la cuenta, no a una sede: el número marcado decide qué sede recibe la llamada (ADR #203).
statusactive · disabled · retiredno
allowed_destinationsarreglo de stringnoLa lista de destinos permitidos de una troncal BYOC (core#1281, ADR #172): los números E.164 exactos a los que puede marcar. [] = sin restricción. Se almacena cifrada bajo la clave de alcance de la troncal.
max_concurrent_callsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): las llamadas simultáneas que admite. Una marcación que encuentra tantas llamadas vivas en la troncal se rechaza con egress.carrier_channels_full.v1. null = sin límite.
max_daily_attemptsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): intentos de marcación por día UTC (de 00:00 a 24:00 UTC). Una marcación que encuentra el día agotado se rechaza con egress.daily_attempts_exhausted.v1. null = sin límite.
allowed_dial_modesarreglo de manual · preview · progressive · predictive | nullnoLímite contractual de una troncal BYOC (core#2176): los modos de marcación que permite. Una marcación en otro modo (incluida la marcación masiva sin agente) se rechaza con egress.dial_mode_not_allowed.v1. null = sin restricción; una lista vacía es un 400.
srtpoff · offer · requirenoPolítica SRTP del tramo de salida de la troncal (core#2333, SBC kamailio#189). off (por defecto) es RTP plano, exactamente como antes. offer ofrece SRTP y acepta RTP plano en la respuesta; require rechaza una respuesta que no sea SRTP. offer y require necesitan un sip_proxy TLS (sips: o ;transport=tls) y un sip_proxy sin parámetro srtp propio: cualquier otra cosa es un 400. Core escribe ;srtp=<mode> en el borde solo para offer/require.
scopesede · empresanoCampo solo de petición; las respuestas lo derivan de tenant_id.
account_idstring (uuid)noSe acepta e IGNORA A PROPÓSITO: la cuenta sale siempre de la sesión, así que un valor falsificado no tiene efecto. Se declara para que ESTA clave no pase a ser un 400 ahora que las claves desconocidas lo son. NO hace que toda la troncal renderizada sea reenviable tal cual: id, created_at y updated_at dan un 400, y tenant_id da un 400 DELIBERADAMENTE: enviarlo es un intento de falsificación entre tenants y se nombra, no se descarta en silencio.
json
{
  "kind": "byoc",
  "name": "postman-trunk-1751600000",
  "sip_proxy": "sip:pstn.example.test:5060",
  "source_ips": [
    "203.0.113.9"
  ]
}

Respuestas

201 ​

Troncal registrada.

CampoTipoObligatorioDescripción
carrierobject (Carrier)sí
carrier.idstring (uuid)no
carrier.account_idstring (uuid)no
carrier.tenant_idstring (uuid) | nullnonull = troncal de toda la empresa (compartida), heredada por cada sede.
carrier.scopeempresa · sedenoDerivado de tenant_id.
carrier.kindhouse · byocno
carrier.namestringno
carrier.sip_proxystring | nullno
carrier.source_ipsarreglo de stringno
carrier.allowed_destinationsarreglo de stringnoLa lista de destinos permitidos de una troncal BYOC (core#1281, ADR #172): los números E.164 exactos a los que puede marcar. [] = sin restricción. Se almacena cifrada bajo la clave de alcance de la troncal.
carrier.max_concurrent_callsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): las llamadas simultáneas que admite. Una marcación que encuentra tantas llamadas vivas en la troncal se rechaza con egress.carrier_channels_full.v1. null = sin límite.
carrier.max_daily_attemptsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): intentos de marcación por día UTC (de 00:00 a 24:00 UTC). Una marcación que encuentra el día agotado se rechaza con egress.daily_attempts_exhausted.v1. null = sin límite.
carrier.allowed_dial_modesarreglo de manual · preview · progressive · predictive | nullnoLímite contractual de una troncal BYOC (core#2176): los modos de marcación que permite. Una marcación en otro modo (incluida la marcación masiva sin agente) se rechaza con egress.dial_mode_not_allowed.v1. null = sin restricción; una lista vacía es un 400.
carrier.srtpoff · offer · requirenoPolítica SRTP del tramo de salida de la troncal (core#2333, SBC kamailio#189). off (por defecto) es RTP plano, exactamente como antes. offer ofrece SRTP y acepta RTP plano en la respuesta; require rechaza una respuesta que no sea SRTP. offer y require necesitan un sip_proxy TLS (sips: o ;transport=tls) y un sip_proxy sin parámetro srtp propio: cualquier otra cosa es un 400. Core escribe ;srtp=<mode> en el borde solo para offer/require.
carrier.statusactive · disabled · retiredno
carrier.created_atstring (date-time)no
carrier.updated_atstring (date-time)no

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

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

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

403 — Forbidden ​

El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.

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

409 ​

trunk_endpoint_shared: el host y el puerto de sip_proxy ya son el endpoint de un carrier de la plataforma o de una troncal BYOC activa de otra cuenta. La respuesta no dice de quién.

json
{
  "error": {
    "code": "trunk_endpoint_shared",
    "message": "sip_proxy is already the endpoint of another trunk and cannot be used for this one"
  }
}

422 ​

El sip_proxy de una troncal BYOC no es un endpoint SIP público (core#2345): está mal formado, o es una dirección de loopback, privada, de enlace local, CGNAT, multicast o no especificada, un nombre interno, o un rango que la celda declara como propio. El mensaje nombra el motivo cerrado.

json
{
  "error": {
    "code": "unprocessable",
    "message": "sip_proxy is not an allowed destination for a BYOC trunk (private): it must be a public sip:/sips: endpoint"
  }
}

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

Obtener un carrier ​

http
GET /v1/carriers/{id}

Parámetros

NombreEnTipoObligatorioDescripción
idpathstring (uuid)síUUID del recurso. Los ids mal formados o de otro tenant se leen como 404.

Respuestas

200 ​

El carrier.

CampoTipoObligatorioDescripción
carrierobject (Carrier)sí
carrier.idstring (uuid)no
carrier.account_idstring (uuid)no
carrier.tenant_idstring (uuid) | nullnonull = troncal de toda la empresa (compartida), heredada por cada sede.
carrier.scopeempresa · sedenoDerivado de tenant_id.
carrier.kindhouse · byocno
carrier.namestringno
carrier.sip_proxystring | nullno
carrier.source_ipsarreglo de stringno
carrier.allowed_destinationsarreglo de stringnoLa lista de destinos permitidos de una troncal BYOC (core#1281, ADR #172): los números E.164 exactos a los que puede marcar. [] = sin restricción. Se almacena cifrada bajo la clave de alcance de la troncal.
carrier.max_concurrent_callsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): las llamadas simultáneas que admite. Una marcación que encuentra tantas llamadas vivas en la troncal se rechaza con egress.carrier_channels_full.v1. null = sin límite.
carrier.max_daily_attemptsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): intentos de marcación por día UTC (de 00:00 a 24:00 UTC). Una marcación que encuentra el día agotado se rechaza con egress.daily_attempts_exhausted.v1. null = sin límite.
carrier.allowed_dial_modesarreglo de manual · preview · progressive · predictive | nullnoLímite contractual de una troncal BYOC (core#2176): los modos de marcación que permite. Una marcación en otro modo (incluida la marcación masiva sin agente) se rechaza con egress.dial_mode_not_allowed.v1. null = sin restricción; una lista vacía es un 400.
carrier.srtpoff · offer · requirenoPolítica SRTP del tramo de salida de la troncal (core#2333, SBC kamailio#189). off (por defecto) es RTP plano, exactamente como antes. offer ofrece SRTP y acepta RTP plano en la respuesta; require rechaza una respuesta que no sea SRTP. offer y require necesitan un sip_proxy TLS (sips: o ;transport=tls) y un sip_proxy sin parámetro srtp propio: cualquier otra cosa es un 400. Core escribe ;srtp=<mode> en el borde solo para offer/require.
carrier.statusactive · disabled · retiredno
carrier.created_atstring (date-time)no
carrier.updated_atstring (date-time)no

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

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

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

Reemplazar la lista de destinos permitidos y los límites contractuales de una troncal BYOC ​

http
PATCH /v1/carriers/{id}

core#1281 (ADR #172) y core#2176. Lo que una troncal cambia después de su alta: la lista de destinos permitidos (allowed_destinations, se reemplaza la lista entera; [] o null levanta la restricción) y sus límites contractuales (max_concurrent_calls, max_daily_attempts, allowed_dial_modes; null levanta cada uno; véase POST /v1/carriers). Se exige al menos una de las cuatro claves; una clave presente reemplaza el valor almacenado y una ausente se deja como estaba. Cualquier otra clave es un 400 que la nombra: sip_proxy, source_ips y el alcance se aprovisionan en el borde SIP y no se editan en el sitio.

Una troncal de toda la empresa restringe a todas las sedes que la heredan, así que cambiar su lista requiere un usuario account_admin (403 en caso contrario), igual que crearla. La troncal de otra empresa, o la troncal house de la plataforma, da 404; una troncal retirada da 409. En una troncal house, una lista no vacía es un 400.

Parámetros

NombreEnTipoObligatorioDescripción
idpathstring (uuid)síUUID del recurso. Los ids mal formados o de otro tenant se leen como 404.

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
allowed_destinationsCarrierAllowedDestinations | nullno
max_concurrent_callsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): las llamadas simultáneas que admite. Una marcación que encuentra tantas llamadas vivas en la troncal se rechaza con egress.carrier_channels_full.v1. null = sin límite.
max_daily_attemptsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): intentos de marcación por día UTC (de 00:00 a 24:00 UTC). Una marcación que encuentra el día agotado se rechaza con egress.daily_attempts_exhausted.v1. null = sin límite.
allowed_dial_modesarreglo de manual · preview · progressive · predictive | nullnoLímite contractual de una troncal BYOC (core#2176): los modos de marcación que permite. Una marcación en otro modo (incluida la marcación masiva sin agente) se rechaza con egress.dial_mode_not_allowed.v1. null = sin restricción; una lista vacía es un 400.
json
{
  "allowed_destinations": [
    "+13125550100",
    "+13125550101"
  ],
  "max_concurrent_calls": 1,
  "max_daily_attempts": 20,
  "allowed_dial_modes": [
    "manual",
    "preview"
  ]
}

Respuestas

200 ​

La troncal con su nueva lista y sus límites.

CampoTipoObligatorioDescripción
carrierobject (Carrier)sí
carrier.idstring (uuid)no
carrier.account_idstring (uuid)no
carrier.tenant_idstring (uuid) | nullnonull = troncal de toda la empresa (compartida), heredada por cada sede.
carrier.scopeempresa · sedenoDerivado de tenant_id.
carrier.kindhouse · byocno
carrier.namestringno
carrier.sip_proxystring | nullno
carrier.source_ipsarreglo de stringno
carrier.allowed_destinationsarreglo de stringnoLa lista de destinos permitidos de una troncal BYOC (core#1281, ADR #172): los números E.164 exactos a los que puede marcar. [] = sin restricción. Se almacena cifrada bajo la clave de alcance de la troncal.
carrier.max_concurrent_callsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): las llamadas simultáneas que admite. Una marcación que encuentra tantas llamadas vivas en la troncal se rechaza con egress.carrier_channels_full.v1. null = sin límite.
carrier.max_daily_attemptsinteger | nullnoLímite contractual de una troncal BYOC (core#2176): intentos de marcación por día UTC (de 00:00 a 24:00 UTC). Una marcación que encuentra el día agotado se rechaza con egress.daily_attempts_exhausted.v1. null = sin límite.
carrier.allowed_dial_modesarreglo de manual · preview · progressive · predictive | nullnoLímite contractual de una troncal BYOC (core#2176): los modos de marcación que permite. Una marcación en otro modo (incluida la marcación masiva sin agente) se rechaza con egress.dial_mode_not_allowed.v1. null = sin restricción; una lista vacía es un 400.
carrier.srtpoff · offer · requirenoPolítica SRTP del tramo de salida de la troncal (core#2333, SBC kamailio#189). off (por defecto) es RTP plano, exactamente como antes. offer ofrece SRTP y acepta RTP plano en la respuesta; require rechaza una respuesta que no sea SRTP. offer y require necesitan un sip_proxy TLS (sips: o ;transport=tls) y un sip_proxy sin parámetro srtp propio: cualquier otra cosa es un 400. Core escribe ;srtp=<mode> en el borde solo para offer/require.
carrier.statusactive · disabled · retiredno
carrier.created_atstring (date-time)no
carrier.updated_atstring (date-time)no

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

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

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

403 — Forbidden ​

El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.

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

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

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

409 — Conflict ​

Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.

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

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

Salud de una troncal BYOC (up | down | unknown) ​

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

core#1522 (ADR #196). El estado AHORA de una troncal BYOC: el último evento de un flujo de solo anexar, alimentado por la sonda OPTIONS del borde SIP (alcanzabilidad, funciona sin tráfico) y corroborado por el CDR (calidad: una tasa de respuestas en los últimos 10 minutos, ignorada por debajo de 30 intentos).

unknown es un estado de primera clase, nunca un up por cortesía: una troncal sin ninguna medición todavía, una que el evaluador no pudo leer durante dos ticks, y una cuyo último evento tiene más de 15 minutos (fresh: false, reason: stale, nadie pudo escribir) se leen todas como unknown. Una troncal disabled o retired se lee como unknown con reason: carrier_inactive y signals: null. La salud es informativa: no reescribe el enrutamiento.

La troncal de otra sede o cuenta, un id inventado, un id mal formado y la troncal house de la plataforma dan todos 404. La respuesta nunca lleva el sip_proxy de la troncal.

Parámetros

NombreEnTipoObligatorioDescripción
idpathstring (uuid)síUUID del recurso. Los ids mal formados o de otro tenant se leen como 404.

Respuestas

200 ​

La salud de la troncal ahora.

CampoTipoObligatorioDescripción
carrier_idstring (uuid)sí
stateup · down · unknownsí
reasonstringsíPor qué la troncal está en state: probe_active, probe_inactive, cdr_failure_ratio, cdr_success_ratio, no_fresh_samples, inconclusive (de la última transición), o, calculados por la lectura, no_data (aún sin evento), stale (último evento de más de 15 minutos) y carrier_inactive (disabled/retired).
sincestring (date-time) | nullsíCuándo entró la troncal en state; null sin datos o cuando está inactiva.
observed_atstring (date-time) | nullsíCuándo se observó el último evento (confirmaciones incluidas).
freshbooleansífalse cuando el último evento tiene más de 15 minutos, no hay ninguno, o la troncal está inactiva.
signalsTrunkHealthSignals | nullsíLo que vio el último evento; null sin evento o cuando está inactiva.
json
{
  "carrier_id": "6f1c2d4e-8a3b-4c5d-9e7f-0a1b2c3d4e5f",
  "state": "up",
  "reason": "probe_active",
  "since": "2026-10-05T00:41:00Z",
  "observed_at": "2026-10-05T00:51:00Z",
  "fresh": true,
  "signals": {
    "probe": {
      "replicas": {
        "up": 2,
        "down": 0,
        "fresh": 2
      },
      "latency_ms": 38.5
    },
    "cdr": {
      "window_s": 600,
      "attempts": 120,
      "failures": 3
    }
  }
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

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

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

Historial de las transiciones de salud de una troncal BYOC ​

http
GET /v1/carriers/{id}/health/events

core#1522 (ADR #196). Las filas del flujo de salud de la troncal, las más recientes primero, paginadas por keyset como /v1/call_attempts. Por defecto solo las TRANSICIONES: las filas confirm (mismo estado, una cada 5 minutos, que solo prueban frescura) se omiten salvo que se indique include_confirm=true. Misma regla de 404 que GET /v1/carriers/{id}/health; una troncal retirada conserva su historial.

Parámetros

NombreEnTipoObligatorioDescripción
idpathstring (uuid)síUUID del recurso. Los ids mal formados o de otro tenant se leen como 404.
include_confirmquerybooleannoLista también las filas confirm de 5 minutos (por defecto false).
limitqueryintegernoTamaño de página (por defecto 100, máximo 1000).
cursorquerystringnoCursor keyset opaco del next_cursor de la página anterior.

Respuestas

200 ​

Una página de eventos de salud.

CampoTipoObligatorioDescripción
eventsarreglo de TrunkHealthEventsí
events[].idstring (uuid)sí
events[].carrier_idstring (uuid)sí
events[].stateup · down · unknownsí
events[].prev_stateup · down · unknown | nullsínull en el primer evento; igual a state en una fila confirm.
events[].reasonstringsíEl motivo del evaluador, o confirm para una fila de frescura.
events[].evidenceobjectsíLo que vio el evaluador (probe, cdr, consecutive).
events[].observed_atstring (date-time)sí
next_cursorstring | nullsíCursor de la página siguiente; null en la última.

400 — BadRequest ​

Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).

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

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

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

429 — ApiKeyRateLimited ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

Empujar la troncal al borde SIP (Kamailio) ​

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

Un carrier BYOC empuja su enrutamiento al borde. Una troncal por sede escribe su salida y su ACL de entrada (source_ips). Una troncal de toda la empresa escribe la salida de cada sede de la cuenta que no tiene troncal BYOC activa propia (la troncal propia de una sede gana) y su ACL de entrada (source_ips) UNA vez para toda la cuenta, no por sede.

Un carrier HOUSE es un no-op 200 {"status": "provisioned"}: las llamadas house salen por el enrutamiento por defecto de la plataforma, así que no hay nada que empujar.

Un carrier BYOC revocado da 409: revocar lo retira para siempre, y solo una troncal activa puede ser aquella por la que sale una sede.

El sip_proxy almacenado se comprueba de nuevo antes de empujar nada (core#2345), con la misma regla que el alta: un destino que no es un endpoint SIP público da 422, y uno que ya es el endpoint de un carrier de la plataforma o de una troncal BYOC activa más antigua de otra cuenta da 409 con el código trunk_endpoint_shared. En ninguno de los dos casos se empuja nada.

Sin KAMAILIO_RPC_URL configurada (desarrollo sin el arnés de telecom) la respuesta es 200 {"status": "sbc_disabled"}: la intención de la fila queda registrada, simplemente no se empuja a un borde vivo.

Parámetros

NombreEnTipoObligatorioDescripción
idpathstring (uuid)síUUID del recurso. Los ids mal formados o de otro tenant se leen como 404.

Respuestas

200 ​

Aprovisionada (house incluida, como no-op), o SBC deshabilitado en este entorno.

CampoTipoObligatorioDescripción
statusprovisioned · sbc_disabledsísbc_disabled = no hay KAMAILIO_RPC_URL en este entorno; la intención de la fila queda registrada, no empujada.

provisioned

json
{
  "status": "provisioned"
}

sbc_disabled

json
{
  "status": "sbc_disabled"
}

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

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

409 — Conflict ​

Transición de estado no válida, conflicto de unicidad, o un asiento que está ocupado/reservado.

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

422 — Unprocessable ​

La forma es correcta, la semántica no (regla de dominio incumplida).

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 ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

502 — SbcError ​

El borde SIP rechazó la petición de aprovisionamiento.

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

Quitar la troncal del borde SIP ​

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

Solo se puede revocar un carrier BYOC: un carrier HOUSE da 422. Revocar retira el carrier para siempre (ya no se puede aprovisionar) y libera sus source_ips para otra troncal. Idempotente: revocarlo de nuevo solo resincroniza el borde.

Cada sede afectada sale entonces por la troncal que le queda, la misma a la que se atribuyen sus llamadas: su propia troncal activa más reciente, si no la troncal de toda la empresa más reciente de la cuenta, si no la ruta house. Revocar una troncal por la que una sede no estaba saliendo deja sin cambios la salida de esa sede. Las sedes afectadas son la propia sede de la troncal o, para una troncal de toda la empresa, las mismas sedes que escribe el aprovisionamiento (las que no tienen troncal BYOC activa propia).

Revocar una troncal de toda la empresa cambia el enrutamiento de todas las sedes hermanas, así que requiere un account_admin (403 en caso contrario), igual que crearla.

La ACL de entrada de una troncal de toda la empresa (source_ips, escrita como entradas acct:<account>) se elimina primero, una vez para toda la cuenta, y solo después se resincroniza la salida de las sedes.

Parámetros

NombreEnTipoObligatorioDescripción
idpathstring (uuid)síUUID del recurso. Los ids mal formados o de otro tenant se leen como 404.

Respuestas

200 ​

Revocada (o SBC deshabilitado en este entorno).

CampoTipoObligatorioDescripción
statusprovisioned · sbc_disabledsísbc_disabled = no hay KAMAILIO_RPC_URL en este entorno; la intención de la fila queda registrada, no empujada.

401 — Unauthorized ​

Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.

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

403 — Forbidden ​

El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.

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

404 — NotFound ​

Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).

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

422 — Unprocessable ​

La forma es correcta, la semántica no (regla de dominio incumplida).

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 ​

Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.

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

502 — SbcError ​

El borde SIP rechazó la petición de aprovisionamiento.

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.