Apariencia
Rutas entrantes
Generado desde
DialerDigital/corecommitbbb8e373d900— especificaciónopenapi/dialer-v1.yamlv0.151.0. No se edita a mano; consulta Cómo se construye esta referencia. Descargar la especificación OpenAPI.
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.
Enrutamiento de DID entrantes + aprovisionamiento del SBC.
Endpoints
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/inbound-routes | Listar las rutas entrantes |
POST | /v1/inbound-routes | Crear una ruta entrante |
GET | /v1/inbound-routes/{id} | Obtener una ruta entrante |
PATCH | /v1/inbound-routes/{id} | Actualizar una ruta entrante (e164 inmutable) |
POST | /v1/inbound-routes/{id}/provision | Empujar la ruta al borde SIP |
POST | /v1/inbound-routes/{id}/disable | Deshabilitar una ruta entrante |
Listar las rutas entrantes
http
GET /v1/inbound-routesRespuestas
200
Las rutas entrantes del tenant.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
inbound_routes | arreglo de InboundRoute | sí | |
inbound_routes[].id | string (uuid) | no | |
inbound_routes[].tenant_id | string (uuid) | no | |
inbound_routes[].e164 | string | no | Inmutable tras la creación. |
inbound_routes[].dest_type | agent · queue · ivr · optout · ai_agent · webhook | no | ivr ejecuta el menú IVR entrante que nombra dest_ref. Una ruta guardada sin menú se sigue rechazando al conectar con CALL_REJECTED. optout es el número de baja: el número que llama se añade a la lista de no llamar propia del tenant al conectar y FreeSWITCH lo confirma y cuelga; una ruta así siempre lee dest_ref: null, recording: false, ai_enabled: false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
inbound_routes[].dest_ref | string | null | no | |
inbound_routes[].queue_id | string,null (uuid) | no | La cola de una ruta queue; null en cualquier otro tipo, y en una ruta queue guardada sin ella. |
inbound_routes[].ai_agent_id | string,null (uuid) | no | El agente de IA de una ruta ai_agent; null en cualquier otro tipo. |
inbound_routes[].hours | InboundRouteHours | null | no | El horario de la ruta, o null cuando contesta a todas horas. Un mensaje closed se lee de vuelta como el texto que se envió. |
inbound_routes[].webhook_url | string,null | no | El webhook del desarrollador de una ruta webhook; null en cualquier otro tipo. |
inbound_routes[].webhook_secret_hint | string,null | no | Los últimos 4 caracteres del secreto de firma de una ruta webhook; null en cualquier otro tipo. El secreto en sí nunca se puede leer. |
inbound_routes[].webhook_secret | string | no | El secreto de firma de una ruta webhook (whsec_…), presente SOLO en la respuesta de la escritura que lo emitió (crear, una escritura que mueve la ruta a webhook, una escritura que nombra webhook_url); nunca en una lectura ni en un listado. Guárdalo: no se puede volver a mostrar. |
inbound_routes[].recording | boolean | no | |
inbound_routes[].ai_enabled | boolean | no | true si y solo si dest_type es ai_agent (derivado); false en cualquier otro tipo. Una escritura que nombre lo contrario da 400. Las rutas que tenían true antes de que se rechazara el indicador se apagaron con una migración de datos. |
inbound_routes[].status | active · disabled | no | |
inbound_routes[].notes | string | null | no | |
inbound_routes[].created_at | string (date-time) | no | |
inbound_routes[].updated_at | string (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 (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"
}
}Crear una ruta entrante
http
POST /v1/inbound-routese164 es inmutable tras la creación; el router inyecta tenant_id.
Reglas de rol: una clave dd_ o un inicio de sesión account_admin; un inicio de sesión member recibe 403 role_forbidden (opción por tenant telephony_config_admin_only, activada por defecto). El GET de este recurso no se ve afectado.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
e164 | string | sí | E.164 (inmutable tras la creación). Debe ser un número de ESTA cuenta (uno registrado con /v1/dids, o un número house asignado a ella): un número que es de otra cuenta o que nadie registró da 422 unprocessable (e164 not a number of this account). Un PATCH que devuelve una ruta a active se juzga igual; una ruta que ya existe nunca se vuelve a juzgar. |
dest_type | agent · queue · ivr · optout · ai_agent · webhook | sí | ivr ejecuta uno de los menús IVR entrantes del tenant: dest_ref es OBLIGATORIO y debe ser el id de un menú publicado con /v1/inbound-ivr-menus en esta cuenta y no retirado, o la escritura es 400 invalid. Cada llamada oye la versión del menú vigente cuando se conecta; FreeSWITCH la contesta tras el veredicto del plan, core reproduce los avisos, y la tecla (o, tras dos ventanas de silencio, el default del menú) la envía a una cola, a un agente, a la baja o a un colgado. optout es el número de baja (47 CFR 64.1200(b)(3)): el número que llama se añade a la lista de no llamar propia del tenant cuando la llamada se conecta, antes de que nada conteste y sea cual sea el plan del tenant; con un plan activo FreeSWITCH reproduce entonces una confirmación compuesta por la plataforma que nombra al vendedor del tenant (o un tono) y cuelga; con un plan inactivo la llamada se rechaza sin contestar. Un llamante sin número utilizable (oculto o inválido) se rechaza y no se registra nada. Una ruta optout es desnuda: un dest_ref no nulo, recording: true o ai_enabled: true en la misma escritura se rechazan con 400 invalid; si se omiten, quedan en ninguno/false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
dest_ref | string (uuid) | no | Obligatorio para agent (el id del agente) y para ivr (el id de un menú IVR entrante publicado y no retirado de esta cuenta, si no 400 invalid); un valor no nulo se rechaza en una ruta optout. Un valor que no es un UUID canónico se rechaza con 400 invalid (dest_ref must be a UUID). |
queue_id | string (uuid) | no | La cola de una ruta queue o la cola de respaldo de una ruta ai_agent: una cola ACTIVA de esta cuenta creada con /v1/inbound-queues; una retirada, de otra cuenta, inexistente o un id mal formado da 400 invalid. OBLIGATORIA en una ruta ai_agent: la cola a la que recurre la llamada, sujeta a la misma regla. Se rechaza en cualquier otro dest_type. Opcional en una ruta queue: sin ella una ruta queue se acepta, como antes, y rechaza sus llamadas al conectar. Un PATCH que mueve la ruta a otro tipo sin nombrarla la elimina. |
ai_agent_id | string (uuid) | no | OBLIGATORIO en una ruta ai_agent: el id de un agente de IA live de esta cuenta, creado con /v1/ai/agents; uno en borrador, en pausa o retirado, de otra cuenta, inexistente o un id mal formado da 400 invalid. Se rechaza en cualquier otro dest_type; un PATCH que mueve la ruta a otro tipo sin nombrarlo lo elimina. El agente se juzga EN LA ESCRITURA (un PATCH que apunta la ruta a un agente o la devuelve a active): ponerlo en pausa después no toca la ruta, y retirarlo mientras una ruta activa lo nombra da 409 en PATCH /v1/ai/agents/{id} y DELETE /v1/ai/agents/{id}. |
hours | InboundRouteHours | null | no | El horario de la ruta. Ausente o null = el número contesta a todas horas, exactamente como antes; en un PATCH, null ELIMINA el horario y omitir la clave lo conserva. Fuera del horario, o en un festivo, una llamada NUEVA se trata como indica la ruta closed (un mensaje u otro destino); una llamada ya admitida nunca se corta cuando pasa la hora de cierre. Una ruta optout no tiene horario: enviarlo con dest_type: optout, o mover una ruta que lo tiene a optout sin hours: null en la misma escritura, da 422. Una zona que no se resuelve, un festivo mal formado o repetido, más de 366 festivos, un closed que no es exactamente una de sus dos formas, o uno que nombra una cola, menú, agente o agente de IA que no está vivo en esta cuenta da 422 unprocessable. El mensaje closed se convierte en audio cuando se guarda el horario; si no se puede, la escritura da 503 prompt_render_failed y no se guarda nada. Una ruta con horario se aprovisiona anclada en FreeSWITCH (de otro modo, una ruta agent sin recording se entrega directamente al agente y core nunca ve la llamada): tras escribir el horario, haz POST /v1/inbound-routes/{id}/provision para que el borde SIP lo conozca. |
webhook_url | string (uri) | no | OBLIGATORIA en una ruta webhook: la URL https del desarrollador en el puerto 443 cuyo host resuelva solo a direcciones públicas (sin loopback, privadas, link-local ni rango de metadatos), o 400 invalid; se rechaza en cualquier otro dest_type (un PATCH que saca la ruta de webhook sin nombrarla la elimina). El secreto de firma de la ruta (whsec_…) se emite cuando la ruta se crea como webhook, cuando una escritura la mueve a webhook y siempre que una escritura NOMBRA webhook_url (una URL nueva es un receptor nuevo, nunca hereda la clave antigua); se devuelve UNA SOLA VEZ, como webhook_secret en la respuesta de esa escritura, y después solo se puede leer webhook_secret_hint. |
recording | boolean | no | true se rechaza en una ruta optout, en una ruta webhook y en una ruta ai_agent (la pata de IA no se graba); omitido en ellas, es false. Por defecto true en cualquier otra ruta. |
ai_enabled | boolean | no | Derivado del destino, no elegido: true si y solo si dest_type es ai_agent. Nombrar true en cualquier otro destino, o false en una ruta ai_agent, da 400 invalid (ai_enabled is derived from the destination...); omítelo, o envía el valor que implica el destino (por defecto false). |
notes | string | no |
json
{
"e164": "+13125550171",
"dest_type": "queue"
}Respuestas
201
Ruta creada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
inbound_route | object (InboundRoute) | sí | |
inbound_route.id | string (uuid) | no | |
inbound_route.tenant_id | string (uuid) | no | |
inbound_route.e164 | string | no | Inmutable tras la creación. |
inbound_route.dest_type | agent · queue · ivr · optout · ai_agent · webhook | no | ivr ejecuta el menú IVR entrante que nombra dest_ref. Una ruta guardada sin menú se sigue rechazando al conectar con CALL_REJECTED. optout es el número de baja: el número que llama se añade a la lista de no llamar propia del tenant al conectar y FreeSWITCH lo confirma y cuelga; una ruta así siempre lee dest_ref: null, recording: false, ai_enabled: false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
inbound_route.dest_ref | string | null | no | |
inbound_route.queue_id | string,null (uuid) | no | La cola de una ruta queue; null en cualquier otro tipo, y en una ruta queue guardada sin ella. |
inbound_route.ai_agent_id | string,null (uuid) | no | El agente de IA de una ruta ai_agent; null en cualquier otro tipo. |
inbound_route.hours | InboundRouteHours | null | no | El horario de la ruta, o null cuando contesta a todas horas. Un mensaje closed se lee de vuelta como el texto que se envió. |
inbound_route.webhook_url | string,null | no | El webhook del desarrollador de una ruta webhook; null en cualquier otro tipo. |
inbound_route.webhook_secret_hint | string,null | no | Los últimos 4 caracteres del secreto de firma de una ruta webhook; null en cualquier otro tipo. El secreto en sí nunca se puede leer. |
inbound_route.webhook_secret | string | no | El secreto de firma de una ruta webhook (whsec_…), presente SOLO en la respuesta de la escritura que lo emitió (crear, una escritura que mueve la ruta a webhook, una escritura que nombra webhook_url); nunca en una lectura ni en un listado. Guárdalo: no se puede volver a mostrar. |
inbound_route.recording | boolean | no | |
inbound_route.ai_enabled | boolean | no | true si y solo si dest_type es ai_agent (derivado); false en cualquier otro tipo. Una escritura que nombre lo contrario da 400. Las rutas que tenían true antes de que se rechazara el indicador se apagaron con una migración de datos. |
inbound_route.status | active · disabled | no | |
inbound_route.notes | string | null | no | |
inbound_route.created_at | string (date-time) | no | |
inbound_route.updated_at | string (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"
}
}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 (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"
}
}503
El mensaje closed del horario no se pudo convertir en audio (prompt_render_failed); la ruta no se guardó. La petición es correcta y puede funcionar cuando responda el texto a voz.
Obtener una ruta entrante
http
GET /v1/inbound-routes/{id}Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
La ruta.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
inbound_route | object (InboundRoute) | sí | |
inbound_route.id | string (uuid) | no | |
inbound_route.tenant_id | string (uuid) | no | |
inbound_route.e164 | string | no | Inmutable tras la creación. |
inbound_route.dest_type | agent · queue · ivr · optout · ai_agent · webhook | no | ivr ejecuta el menú IVR entrante que nombra dest_ref. Una ruta guardada sin menú se sigue rechazando al conectar con CALL_REJECTED. optout es el número de baja: el número que llama se añade a la lista de no llamar propia del tenant al conectar y FreeSWITCH lo confirma y cuelga; una ruta así siempre lee dest_ref: null, recording: false, ai_enabled: false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
inbound_route.dest_ref | string | null | no | |
inbound_route.queue_id | string,null (uuid) | no | La cola de una ruta queue; null en cualquier otro tipo, y en una ruta queue guardada sin ella. |
inbound_route.ai_agent_id | string,null (uuid) | no | El agente de IA de una ruta ai_agent; null en cualquier otro tipo. |
inbound_route.hours | InboundRouteHours | null | no | El horario de la ruta, o null cuando contesta a todas horas. Un mensaje closed se lee de vuelta como el texto que se envió. |
inbound_route.webhook_url | string,null | no | El webhook del desarrollador de una ruta webhook; null en cualquier otro tipo. |
inbound_route.webhook_secret_hint | string,null | no | Los últimos 4 caracteres del secreto de firma de una ruta webhook; null en cualquier otro tipo. El secreto en sí nunca se puede leer. |
inbound_route.webhook_secret | string | no | El secreto de firma de una ruta webhook (whsec_…), presente SOLO en la respuesta de la escritura que lo emitió (crear, una escritura que mueve la ruta a webhook, una escritura que nombra webhook_url); nunca en una lectura ni en un listado. Guárdalo: no se puede volver a mostrar. |
inbound_route.recording | boolean | no | |
inbound_route.ai_enabled | boolean | no | true si y solo si dest_type es ai_agent (derivado); false en cualquier otro tipo. Una escritura que nombre lo contrario da 400. Las rutas que tenían true antes de que se rechazara el indicador se apagaron con una migración de datos. |
inbound_route.status | active · disabled | no | |
inbound_route.notes | string | null | no | |
inbound_route.created_at | string (date-time) | no | |
inbound_route.updated_at | string (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 (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"
}
}Actualizar una ruta entrante (e164 inmutable)
http
PATCH /v1/inbound-routes/{id}Reglas de rol: una clave dd_ o un inicio de sesión account_admin; un inicio de sesión member recibe 403 role_forbidden (opción por tenant telephony_config_admin_only, activada por defecto). El GET de este recurso no se ve afectado.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
dest_type | agent · queue · ivr · optout · ai_agent · webhook | no | ivr ejecuta uno de los menús IVR entrantes del tenant: dest_ref es OBLIGATORIO y debe ser el id de un menú publicado con /v1/inbound-ivr-menus en esta cuenta y no retirado, o la escritura es 400 invalid. Cada llamada oye la versión del menú vigente cuando se conecta; FreeSWITCH la contesta tras el veredicto del plan, core reproduce los avisos, y la tecla (o, tras dos ventanas de silencio, el default del menú) la envía a una cola, a un agente, a la baja o a un colgado. optout es el número de baja (47 CFR 64.1200(b)(3)): el número que llama se añade a la lista de no llamar propia del tenant cuando la llamada se conecta, antes de que nada conteste y sea cual sea el plan del tenant; con un plan activo FreeSWITCH reproduce entonces una confirmación compuesta por la plataforma que nombra al vendedor del tenant (o un tono) y cuelga; con un plan inactivo la llamada se rechaza sin contestar. Un llamante sin número utilizable (oculto o inválido) se rechaza y no se registra nada. Una ruta optout es desnuda: un dest_ref no nulo, recording: true o ai_enabled: true en la misma escritura se rechazan con 400 invalid; si se omiten, quedan en ninguno/false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
dest_ref | string (uuid) | no | Obligatorio para agent (el id del agente) y para ivr (el id de un menú IVR entrante publicado y no retirado de esta cuenta, si no 400 invalid); un valor no nulo se rechaza en una ruta optout. Un valor que no es un UUID canónico se rechaza con 400 invalid (dest_ref must be a UUID). |
queue_id | string (uuid) | no | La cola de una ruta queue o la cola de respaldo de una ruta ai_agent: una cola ACTIVA de esta cuenta creada con /v1/inbound-queues; una retirada, de otra cuenta, inexistente o un id mal formado da 400 invalid. OBLIGATORIA en una ruta ai_agent: la cola a la que recurre la llamada, sujeta a la misma regla. Se rechaza en cualquier otro dest_type. Opcional en una ruta queue: sin ella una ruta queue se acepta, como antes, y rechaza sus llamadas al conectar. Un PATCH que mueve la ruta a otro tipo sin nombrarla la elimina. |
ai_agent_id | string (uuid) | no | OBLIGATORIO en una ruta ai_agent: el id de un agente de IA live de esta cuenta, creado con /v1/ai/agents; uno en borrador, en pausa o retirado, de otra cuenta, inexistente o un id mal formado da 400 invalid. Se rechaza en cualquier otro dest_type; un PATCH que mueve la ruta a otro tipo sin nombrarlo lo elimina. El agente se juzga EN LA ESCRITURA (un PATCH que apunta la ruta a un agente o la devuelve a active): ponerlo en pausa después no toca la ruta, y retirarlo mientras una ruta activa lo nombra da 409 en PATCH /v1/ai/agents/{id} y DELETE /v1/ai/agents/{id}. |
hours | InboundRouteHours | null | no | El horario de la ruta. Ausente o null = el número contesta a todas horas, exactamente como antes; en un PATCH, null ELIMINA el horario y omitir la clave lo conserva. Fuera del horario, o en un festivo, una llamada NUEVA se trata como indica la ruta closed (un mensaje u otro destino); una llamada ya admitida nunca se corta cuando pasa la hora de cierre. Una ruta optout no tiene horario: enviarlo con dest_type: optout, o mover una ruta que lo tiene a optout sin hours: null en la misma escritura, da 422. Una zona que no se resuelve, un festivo mal formado o repetido, más de 366 festivos, un closed que no es exactamente una de sus dos formas, o uno que nombra una cola, menú, agente o agente de IA que no está vivo en esta cuenta da 422 unprocessable. El mensaje closed se convierte en audio cuando se guarda el horario; si no se puede, la escritura da 503 prompt_render_failed y no se guarda nada. Una ruta con horario se aprovisiona anclada en FreeSWITCH (de otro modo, una ruta agent sin recording se entrega directamente al agente y core nunca ve la llamada): tras escribir el horario, haz POST /v1/inbound-routes/{id}/provision para que el borde SIP lo conozca. |
webhook_url | string (uri) | no | OBLIGATORIA en una ruta webhook: la URL https del desarrollador en el puerto 443 cuyo host resuelva solo a direcciones públicas (sin loopback, privadas, link-local ni rango de metadatos), o 400 invalid; se rechaza en cualquier otro dest_type (un PATCH que saca la ruta de webhook sin nombrarla la elimina). El secreto de firma de la ruta (whsec_…) se emite cuando la ruta se crea como webhook, cuando una escritura la mueve a webhook y siempre que una escritura NOMBRA webhook_url (una URL nueva es un receptor nuevo, nunca hereda la clave antigua); se devuelve UNA SOLA VEZ, como webhook_secret en la respuesta de esa escritura, y después solo se puede leer webhook_secret_hint. |
recording | boolean | no | true se rechaza en una ruta optout, en una ruta webhook y en una ruta ai_agent (la pata de IA no se graba); omitido en ellas, es false. Por defecto true en cualquier otra ruta. |
ai_enabled | boolean | no | Derivado del destino, no elegido: true si y solo si dest_type es ai_agent. Nombrar true en cualquier otro destino, o false en una ruta ai_agent, da 400 invalid (ai_enabled is derived from the destination...); omítelo, o envía el valor que implica el destino (por defecto false). |
status | active · disabled | no | |
notes | string | no |
json
{
"recording": false
}Respuestas
200
Ruta actualizada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
inbound_route | object (InboundRoute) | sí | |
inbound_route.id | string (uuid) | no | |
inbound_route.tenant_id | string (uuid) | no | |
inbound_route.e164 | string | no | Inmutable tras la creación. |
inbound_route.dest_type | agent · queue · ivr · optout · ai_agent · webhook | no | ivr ejecuta el menú IVR entrante que nombra dest_ref. Una ruta guardada sin menú se sigue rechazando al conectar con CALL_REJECTED. optout es el número de baja: el número que llama se añade a la lista de no llamar propia del tenant al conectar y FreeSWITCH lo confirma y cuelga; una ruta así siempre lee dest_ref: null, recording: false, ai_enabled: false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
inbound_route.dest_ref | string | null | no | |
inbound_route.queue_id | string,null (uuid) | no | La cola de una ruta queue; null en cualquier otro tipo, y en una ruta queue guardada sin ella. |
inbound_route.ai_agent_id | string,null (uuid) | no | El agente de IA de una ruta ai_agent; null en cualquier otro tipo. |
inbound_route.hours | InboundRouteHours | null | no | El horario de la ruta, o null cuando contesta a todas horas. Un mensaje closed se lee de vuelta como el texto que se envió. |
inbound_route.webhook_url | string,null | no | El webhook del desarrollador de una ruta webhook; null en cualquier otro tipo. |
inbound_route.webhook_secret_hint | string,null | no | Los últimos 4 caracteres del secreto de firma de una ruta webhook; null en cualquier otro tipo. El secreto en sí nunca se puede leer. |
inbound_route.webhook_secret | string | no | El secreto de firma de una ruta webhook (whsec_…), presente SOLO en la respuesta de la escritura que lo emitió (crear, una escritura que mueve la ruta a webhook, una escritura que nombra webhook_url); nunca en una lectura ni en un listado. Guárdalo: no se puede volver a mostrar. |
inbound_route.recording | boolean | no | |
inbound_route.ai_enabled | boolean | no | true si y solo si dest_type es ai_agent (derivado); false en cualquier otro tipo. Una escritura que nombre lo contrario da 400. Las rutas que tenían true antes de que se rechazara el indicador se apagaron con una migración de datos. |
inbound_route.status | active · disabled | no | |
inbound_route.notes | string | null | no | |
inbound_route.created_at | string (date-time) | no | |
inbound_route.updated_at | string (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"
}
}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 (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"
}
}503
El mensaje closed del horario no se pudo convertir en audio (prompt_render_failed); la ruta no se guardó. La petición es correcta y puede funcionar cuando responda el texto a voz.
Empujar la ruta al borde SIP
http
POST /v1/inbound-routes/{id}/provision409 cuando la ruta está deshabilitada; 422 cuando el agente de destino no se puede resolver para esta sede o no tiene extensión SIP (seat) a la que entregar; 200 {"status": "sbc_disabled"} cuando no hay KAMAILIO_RPC_URL configurado.
Reglas de rol: una clave dd_ o un login account_admin; un login member recibe 403 role_forbidden (knob por tenant telephony_config_admin_only, activado por defecto). El GET de este recurso no se ve afectado.
Una clave de API test se rechaza con 403 test_environment: no crea, modifica, retira ni aprovisiona los troncales, proveedores ni rutas reales del tenant.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
Aprovisionada (o SBC deshabilitado en este entorno).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
status | provisioned · sbc_disabled | sí | 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"
}
}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 (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"
}
}Deshabilitar una ruta entrante
http
POST /v1/inbound-routes/{id}/disableReglas de rol: una clave dd_ o un inicio de sesión account_admin; un inicio de sesión member recibe 403 role_forbidden (opción por tenant telephony_config_admin_only, activada por defecto). El GET de este recurso no se ve afectado.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
La ruta deshabilitada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
inbound_route | object (InboundRoute) | sí | |
inbound_route.id | string (uuid) | no | |
inbound_route.tenant_id | string (uuid) | no | |
inbound_route.e164 | string | no | Inmutable tras la creación. |
inbound_route.dest_type | agent · queue · ivr · optout · ai_agent · webhook | no | ivr ejecuta el menú IVR entrante que nombra dest_ref. Una ruta guardada sin menú se sigue rechazando al conectar con CALL_REJECTED. optout es el número de baja: el número que llama se añade a la lista de no llamar propia del tenant al conectar y FreeSWITCH lo confirma y cuelga; una ruta así siempre lee dest_ref: null, recording: false, ai_enabled: false. agent puentea directamente al agente registrado; queue ancla la pata en FreeSWITCH. Hoy la imagen de producción de FreeSWITCH no trae extensiones de cola (el corral de aparcado y dd_queue_hold son una dependencia pendiente de freeswitch, aún no entregada), así que FreeSWITCH cuelga una pata de cola como UNALLOCATED_NUMBER con independencia del flag. Cuando se entreguen: con el flag inbound_queue_stream_enabled del tenant ACTIVADO y su agent_session_journal_enabled también ACTIVADO (con el diario de puesto DESACTIVADO la llamada se rechaza sin contestar, ya que no habría agente al que ofrecérsela), core admite la llamada en la cola de la ruta (enqueued), la extensión contesta con un tono generado, el propietario la ofrece al agente Ready más antiguo y la puentea; flag DESACTIVADO (por defecto): la pata termina sin contestar en el corral. ai_agent nombra un agente de voz-IA de la cuenta: ai_agent_id (un agente live de esta cuenta) y queue_id (la cola activa a la que cae la llamada) son OBLIGATORIOS, o la escritura es 400 invalid; ai_enabled lee true y recording false, ambos derivados del tipo, no elegidos. La admisión se decide antes de contestar la llamada: con el flag inbound_ai_enabled del tenant desactivado (por defecto) o si falla cualquiera de sus comprobaciones, la llamada se trata como una llamada a la cola queue_id y no la contesta la IA; el motivo queda registrado en la evidencia CDR de la llamada (compliance_snapshot.inbound_ai). Todavía nada entrega una llamada al agente (la bifurcación es una entrega posterior), así que una llamada a este número no la contesta la IA. webhook entrega la llamada al webhook del desarrollador: webhook_url es OBLIGATORIA (https en 443, un host que resuelva solo a direcciones públicas, o 400 invalid) y la ruta es desnuda: recording: true, dest_ref, queue_id, ai_agent_id y ai_enabled: true en la misma escritura son 400 invalid; si se omite, recording es false. La llamada pasa los mismos veredictos de plan, gasto y capacidad que cualquier llamada entrante ANTES de contactar jamás con el webhook; la pata espera entonces en el dd_ivr_hold de FreeSWITCH, que la contesta, y desde la contestación core hace POST de cada paso a webhook_url (call.started, luego gather.completed o gather.timeout) firmado con X-DD-Signature: t=<unix>,v1=<hex(HMAC-SHA256(secret, "<t>.<body>"))>, la misma construcción que la de un endpoint de webhook saliente. El webhook responde 200 con {"actions": [...]}: de 1 a 8 acciones entre say (text, hasta 500 bytes, renderizado a audio por la plataforma), gather (num_digits 1-10, timeout_seconds 1-30, finish_on_key; cierra la lista, su resultado es la SIGUIENTE petición) y los verbos terminales queue (queue_id), agent (agent_id) y hangup; la lista debe acabar en un gather o en un verbo terminal. Fail-closed: ninguna respuesta en 3 segundos, un no-2xx, un cuerpo que no son acciones válidas, un destino que no es de esta cuenta, o más de 20 peticiones por llamada cuelgan la llamada (NORMAL_CLEARING); nunca se enruta nada a una persona ni a una cola que el webhook no haya elegido. Cada paso se registra en un flujo firmado de solo-añadir. Una ruta guardada sin su URL se rechaza al conectar (refused_webhook_unavailable). Una ruta webhook no graba. |
inbound_route.dest_ref | string | null | no | |
inbound_route.queue_id | string,null (uuid) | no | La cola de una ruta queue; null en cualquier otro tipo, y en una ruta queue guardada sin ella. |
inbound_route.ai_agent_id | string,null (uuid) | no | El agente de IA de una ruta ai_agent; null en cualquier otro tipo. |
inbound_route.hours | InboundRouteHours | null | no | El horario de la ruta, o null cuando contesta a todas horas. Un mensaje closed se lee de vuelta como el texto que se envió. |
inbound_route.webhook_url | string,null | no | El webhook del desarrollador de una ruta webhook; null en cualquier otro tipo. |
inbound_route.webhook_secret_hint | string,null | no | Los últimos 4 caracteres del secreto de firma de una ruta webhook; null en cualquier otro tipo. El secreto en sí nunca se puede leer. |
inbound_route.webhook_secret | string | no | El secreto de firma de una ruta webhook (whsec_…), presente SOLO en la respuesta de la escritura que lo emitió (crear, una escritura que mueve la ruta a webhook, una escritura que nombra webhook_url); nunca en una lectura ni en un listado. Guárdalo: no se puede volver a mostrar. |
inbound_route.recording | boolean | no | |
inbound_route.ai_enabled | boolean | no | true si y solo si dest_type es ai_agent (derivado); false en cualquier otro tipo. Una escritura que nombre lo contrario da 400. Las rutas que tenían true antes de que se rechazara el indicador se apagaron con una migración de datos. |
inbound_route.status | active · disabled | no | |
inbound_route.notes | string | null | no | |
inbound_route.created_at | string (date-time) | no | |
inbound_route.updated_at | string (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"
}
}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 (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"
}
}