Skip to content

Agentes y puestos ​

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.

Lista de puestos, presencia, check-in/check-out del softphone.

Endpoints ​

MétodoRutaResumen
GET/v1/agents/{id}/monitoring-acknowledgementLa aceptación más reciente de la grabación y supervisión de un agente
GET/v1/agent/queueLo que el dialer tiene frente al PROPIO puesto de quien llama
GET/v1/agentsLista de puestos + presencia en vivo del puesto
POST/v1/agentsCrear un puesto
GET/v1/agents/occupancyTiempo derivado por estado y puesto para una ventana explícita
GET/v1/agents/handle-timeTiempo de atención por agente, acreditando a cada agente solo sus piernas de una llamada transferida
GET/v1/agents/{id}Obtener un agente + presencia
PATCH/v1/agents/{id}Editar un puesto o liberarlo (estado inactive)
GET/v1/agents/{id}/skillsLas habilidades de enrutamiento de un agente
PUT/v1/agents/{id}/skillsReemplazar las habilidades de enrutamiento de un agente
POST/v1/agents/{id}/credentialHabilitar o rotar la contraseña de acceso al widget de un agente
DELETE/v1/agents/{id}/credentialDeshabilitar el acceso al widget de un agente
POST/v1/agents/checkinPuesto en línea + arranque del softphone
POST/v1/agents/checkoutPuesto fuera de línea (revoca la credencial SIP efímera)
POST/v1/agents/{id}/pauseSacar de la rotación un puesto disponible (en pausa)
POST/v1/agents/{id}/resumeDevolver a la rotación un puesto en pausa (disponible)
POST/v1/agents/{id}/wrap-upTerminar el trabajo posterior a la llamada (wrap_up -> available)
PATCH/v1/agents/{id}/statusEstablecer la presencia de un puesto por id de la lista (una FACHADA sobre los verbos de la FSM del puesto)
GET/v1/planned-shiftsListar los turnos planificados del tenant
POST/v1/planned-shiftsCrear un turno planificado

La aceptación más reciente de la grabación y supervisión de un agente ​

http
GET /v1/agents/{id}/monitoring-acknowledgement

La versión vigente del aviso, si ESTE agente la ha aceptado (current) y la última aceptación del agente de cualquier versión (null si no hay ninguna). Un puesto lee la suya; un puesto cuyo rol de plantilla es supervisor o admin y una clave dd_ del tenant leen a cualquier agente del tenant; otro puesto que lea el de un compañero recibe 403 forbidden. Un id de otro tenant es 404. Un token de widget ddw_ recibe 403 antes del handler.

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 estado del aviso de monitoreo del agente.

CampoTipoObligatorioDescripción
monitoringobject (AgentMonitoringStatus)síLa situación de un agente frente al aviso vigente.
monitoring.notice_versioninteger | nullsíLa versión vigente; null mientras no haya ninguna en vigor.
monitoring.currentbooleansíVerdadero solo cuando el agente ha aceptado la versión VIGENTE.
monitoring.acknowledgementAgentMonitoringAck | nullsíLa última aceptación del agente de cualquier versión; null si no hay ninguna.

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

Lo que el dialer tiene frente al PROPIO puesto de quien llama ​

http
GET /v1/agent/queue

Una LECTURA compuesta a partir de cuatro fuentes que ya existen; no hay tabla de cola por agente y no se inventa ninguna. Para el puesto que el BEARER identifica — ningún parámetro puede nombrar a un agente, por lo que no se puede leer la cola de otro agente:

  • presence: el estado de la FSM del puesto, igual que GET /v1/agents.
  • live_call: la llamada en curso del puesto (CDR), o null.
  • callbacks: rellamadas pendientes que el agente PROGRAMÓ, la más próxima primero (callbacks.agent_id es el creador, no una asignación: el dialer no asigna rellamadas por agente). Máximo 50.
  • inbound_offers: ofertas de la cola entrante en vivo dirigidas a este puesto, derivadas del stream durable de la cola; [] cuando el flag inbound_queue_stream_enabled del tenant está desactivado.

NO está presente, porque el modelo no tiene ese estado: un lead de "vista previa pendiente" por agente (en modo preview la cuenta que se muestra se decide al marcarla). Las llamadas reservadas/sonando aparecen como presence.

Quién obtiene un puesto: un inicio de sesión de dashboard ddu_ con un puesto vinculado. Una clave de máquina dd_ no tiene a ninguna persona detrás y recibe 403; un ddu_ sin un puesto activo recibe 403 seat_not_linked; un token de widget ddw_ recibe 403 antes del handler (la ruta no está en la lista de permitidos del widget).

Ausentes deliberadamente (mismo contrato de proyección que /v1/debts): números telefónicos, la note de la rellamada y la instantánea de cumplimiento del intento — solo ids, marcas de tiempo y prioridad.

Respuestas

200 ​

La cola propia de quien llama.

CampoTipoObligatorioDescripción
queueobject (AgentQueue)síLo que el dialer tiene frente a un puesto (GET /v1/agent/queue). Solo ids, marcas de tiempo y prioridad — sin PII.
queue.agent_idstring (uuid)sí
queue.presenceoffline · available · ringing · on_call · wrap_up · pausedsí
queue.live_callobject,nullsí
queue.live_call.attempt_idstring (uuid)sí
queue.live_call.campaign_idstring,null (uuid)sí
queue.live_call.debt_idstring,null (uuid)sí
queue.live_call.started_atstring (date-time)sí
queue.live_call.answered_atstring,null (date-time)sí
queue.callbacksarreglo de objectsí
queue.callbacks[].idstring (uuid)sí
queue.callbacks[].debt_idstring,null (uuid)sí
queue.callbacks[].campaign_idstring,null (uuid)sí
queue.callbacks[].scheduled_atstring (date-time)sí
queue.callbacks[].priorityintegersí
queue.inbound_offersarreglo de objectsí
queue.inbound_offers[].queue_idstring (uuid)sí
queue.inbound_offers[].admission_idstring (uuid)sí
queue.inbound_offers[].call_idstring (uuid)sí
queue.inbound_offers[].enqueued_atstring (date-time)sí

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

Lista de puestos + presencia en vivo del puesto ​

http
GET /v1/agents

Lista duradera combinada con la presencia en vivo (offline|available|ringing|on_call).

Respuestas

200 ​

Todos los agentes con presencia.

CampoTipoObligatorioDescripción
agentsarreglo de Agentsí
agents[].idstring (uuid)no
agents[].tenant_idstring (uuid)no
agents[].namestringno
agents[].emailstringno
agents[].roleagent · supervisor · adminno
agents[].statusactive · inactivenoIndicador de plantilla — NO es presencia.
agents[].sip_extensionstring | nullno
agents[].device_modebrowser · externalno
agents[].login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agents[].presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agents[].pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agents[].state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agents[].pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agents[].created_atstring (date-time)no
agents[].updated_atstring (date-time)no
truncatedbooleansítrue cuando hay más filas que los 2 000 agentes que devuelve el listado: la respuesta es un prefijo en el orden documentado, no el conjunto completo.

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

Crear un puesto ​

http
POST /v1/agents

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
namestringsí
emailstring (email)sí
roleagent · supervisor · adminno
sip_extensionstringnoLa identidad de registro SIP a la que se puentean las llamadas. Solo caracteres inertes (dígitos, letras, punto, guion bajo, guion): el valor es a la vez la identidad SIP y la mitad de la dialstring de medios, por lo que cualquier otro se rechaza con 422 en lugar de divergir entre ambas.
json
{
  "name": "Postman Seat",
  "email": "[email protected]",
  "role": "agent",
  "sip_extension": "1009"
}

Respuestas

201 ​

Puesto creado.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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"
  }
}

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 ​

El puesto se rechaza y no se escribe ninguna fila. Ramifica según code:

  • seat_limit_reached — el tenant ya tiene los max_seats puestos activos del plan (tier) en vigor. Un puesto por encima del tope nunca se factura como extra: libera un puesto (uno inactive no cuenta) o pasa a un plan mayor. El tope sigue al cambio de plan de inmediato. Un tenant que pasó a un plan menor conserva todos los puestos que tenía, por lo que active_seats puede estar por encima de max_seats; solo se rechazan los puestos nuevos hasta que vuelva a quedar por debajo. enterprise no tiene tope.
  • unpriceable_tier — el plan del tenant no tiene fila de catálogo con precio, por lo que no se puede leer su tope. El puesto se rechaza en lugar de crearse sin tope.

seat_limit_reached

json
{
  "error": {
    "code": "seat_limit_reached",
    "message": "tier starter allows 5 active seats and the tenant already has them",
    "max_seats": 5,
    "tier": "starter",
    "active_seats": 5
  }
}

unpriceable_tier

json
{
  "error": {
    "code": "unpriceable_tier",
    "message": "the tenant's billing tier has no priced catalog row (tier_not_in_catalog)"
  }
}

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

Tiempo derivado por estado y puesto para una ventana explícita ​

http
GET /v1/agents/occupancy

Superficie de gestión del tenant (dd_ o ddu_; un ddw_ de agente recibe 403). Devuelve Dialer.Agents.Occupancy.by_agent/4 sin unir con la lista de puestos ni recalcular del lado del cliente: un puesto sin evidencia de intervalos en la ventana está ausente en lugar de aparecer como un cero fabricado.

La ventana semiabierta es entered_at ∈ [from, to). Un intervalo contado aporta toda su duration_ms monotónica; los intervalos abiertos y sintéticos se excluyen y se informan en excluded. Cada puesto lleva un ratio occupancy bajo una definición con nombre y versión (v1): (on_call + wrap_up) / (available + ringing + on_call + wrap_up), es decir, el tiempo contado menos paused. Una fórmula posterior se publica como una nueva definición junto a v1; v1 nunca cambia de significado.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)síInicio inclusivo de la ventana de entrada de intervalos (RFC 3339).
toquerystring (date-time)síFin exclusivo de la ventana de entrada de intervalos (RFC 3339); debe ser posterior a from.
agent_idquerystring (uuid)noFiltro opcional de puesto, siempre dentro del tenant resuelto a partir del bearer.

Respuestas

200 ​

La derivación de ocupación para los puestos con evidencia en la ventana.

CampoTipoObligatorioDescripción
windowobjectsí
window.fromstring (date-time)sí
window.tostring (date-time)sí
agentsobjectsíMapa indexado por UUID de agente; los puestos sin evidencia de intervalos están ausentes.

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

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

Tiempo de atención por agente, acreditando a cada agente solo sus piernas de una llamada transferida ​

http
GET /v1/agents/handle-time

Superficie de gestión del tenant (dd_ o ddu_; un ddw_ de agente recibe 403). Derivado del rastro de control de llamadas de solo anexado y del reloj del CDR (Dialer.Calls.TransferChain.handle_time/4, core#2403): una llamada transferida A -> B -> C se acredita a A hasta el traspaso, a B desde el traspaso hasta el siguiente, y a C desde ahí hasta el final — no completa al agente que nombra la fila del CDR. control_ms es el tiempo con la llamada en control, consult_ms el tiempo en una pierna de consulta, occupied_ms su suma y aht_ms es control_ms / calls (null cuando el agente no tuvo ninguna, nunca un cero fabricado). Las llamadas son las que INICIARON en la ventana semiabierta [from, to). Una pierna cuya duración aún no es medible (la llamada está en curso) se deja fuera y se cuenta en excluded.open_legs. El control que salió de la población de agentes (una transferencia externa) no acredita a ningún agente.

La ventana puede abarcar como máximo 31 días (400 en caso contrario) y como máximo 10.000 llamadas (422: se rechaza, nunca se trunca). definition: "v1" nunca cambia de significado; una fórmula posterior se publica como v2.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)síInicio inclusivo de la ventana de inicio de llamada (RFC 3339).
toquerystring (date-time)síFin exclusivo de la ventana de inicio de llamada (RFC 3339); debe ser posterior a from.
agent_idquerystring (uuid)noFiltro opcional de puesto, siempre dentro del tenant resuelto a partir del bearer.

Respuestas

200 ​

Tiempo de atención de los agentes que tuvieron el control o consultaron en una llamada de la ventana.

CampoTipoObligatorioDescripción
definitionv1sí
fromstring (date-time)sí
tostring (date-time)sí
agentsarreglo de objectsí
agents[].agent_idstring (uuid)sí
agents[].callsintegersíLlamadas en las que el agente tuvo el control durante un tiempo medible.
agents[].control_msintegersí
agents[].consult_msintegersí
agents[].occupied_msintegersí
agents[].aht_msinteger | nullsí
excludedobjectsí
excluded.open_legsintegersí

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

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

503 ​

No se pudo leer la evidencia; reintenta. Falla de forma cerrada — nunca una respuesta vacía.

Obtener un agente + presencia ​

http
GET /v1/agents/{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 agente.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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"
  }
}

Editar un puesto o liberarlo (estado inactive) ​

http
PATCH /v1/agents/{id}

Corrige un puesto creado con POST /v1/agents y es el verbo que lo libera (core#2527). Solo administración: una clave dd_ o un login de dashboard con account_admin; cualquier otro login recibe 403 role_forbidden. El cuerpo está cerrado: una clave desconocida da 400 que la nombra. Envía solo lo que cambia.

  • status: "inactive" libera el puesto: deja de contar contra el tope del plan y la factura (inactive no ocupa puesto), se revocan los tokens ddw_ vivos del puesto, se le hace check-out y se expulsa su credencial SIP (sip_teardown: ok | skipped | failed). La fila nunca se borra; deactivated_at lo estampa la base de datos.
  • status: "active" sobre un puesto inactivo aplica el mismo tope que la creación: 422 seat_limit_reached (atómico bajo el candado del tenant) o 422 unpriceable_tier.
  • 409 last_admin_seat: el puesto es el único puesto admin activo y la edición lo degradaría o lo desactivaría.
  • 409 seat_linked_to_user: el puesto está vinculado a un usuario de la consola y la edición cambia su email. El vínculo se conserva, nunca se reasigna, así que la dirección no puede cambiar aquí.
  • 409 conflict: el email o la extensión ya están en uso.

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
namestringno
emailstringno
roleagent · supervisor · adminno
sip_extensionstringno
statusactive · inactiveno
json
{
  "status": "inactive"
}

Respuestas

200 ​

El puesto tras la edición.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.updated_atstring (date-time)no
sip_teardownok · skipped · failednoPresente cuando el puesto se liberó o su extensión cambió; nombra el resultado de expulsar la extensión que el puesto TENÍA.

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

422 ​

Reactivación rechazada: seat_limit_reached o unpriceable_tier (véase POST /v1/agents). No se escribe nada.

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

Las habilidades de enrutamiento de un agente ​

http
GET /v1/agents/{id}/skills

Las habilidades del agente, ordenadas; [] cuando no tiene ninguna. Un agente que no está en la lista de puestos de este tenant es 404. Solo admin, igual que la escritura.

Parámetros

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

Respuestas

200 ​

Las habilidades del agente.

CampoTipoObligatorioDescripción
agent_idstring (uuid)sí
skillsarreglo de stringsí

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 ​

role_forbidden: un inicio de sesión de dashboard sin el rol account_admin; forbidden: un token de widget o un tenant suspendido.

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 las habilidades de enrutamiento de un agente ​

http
PUT /v1/agents/{id}/skills

Declara qué habilidades tiene el agente (ADR #216 D3). El cuerpo REEMPLAZA el conjunto completo: una habilidad no listada se elimina, una faltante se agrega y una repetida cuenta una sola vez. Una habilidad es una etiqueta del tenant ^[a-z0-9][a-z0-9_-]{0,39}$ — sin catálogo global, sin niveles. 422 unprocessable cuando alguna habilidad está mal formada, skills no es un arreglo de cadenas, o hay más de 50; entonces no se escribe nada. Un agente que no está en la lista de puestos de este tenant es 404. La escritura funciona con inbound_queue_skill_routing_enabled DESACTIVADO: lo almacenado permanece inerte hasta que el tenant lo active. Una habilidad es una preferencia de enrutamiento, no una autorización: ninguna compuerta de cumplimiento la lee (ADR #216 D7). Solo admin: una clave dd_ o un inicio de sesión de dashboard con account_admin.

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
skillsarreglo de stringsí
json
{
  "skills": [
    "idioma_es",
    "cobranza_tardia"
  ]
}

Respuestas

200 ​

Las habilidades del agente después de la escritura, ordenadas.

CampoTipoObligatorioDescripción
agent_idstring (uuid)sí
skillsarreglo de stringsí

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 ​

role_forbidden: un inicio de sesión de dashboard sin el rol account_admin; forbidden: un token de widget o un tenant suspendido.

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

Habilitar o rotar la contraseña de acceso al widget de un agente ​

http
POST /v1/agents/{id}/credential

Establece (o rota) la credencial de acceso al widget del agente para que pueda iniciar sesión en el widget con email + contraseña (POST /v1/auth/agent-login). Alcance de tenant (una clave dd_ o una sesión ddu_; un ddw_ recibe 403). Reglas de rol (core#2404): la contraseña abre un ddw_ vinculado al puesto, así que una clave dd_ y un inicio de sesión account_admin pasan, un ddu_ pasa solo en el puesto al que lo vincula users_agents.user_id, y cualquier otro inicio de sesión recibe 403 role_forbidden (parámetro por tenant agent_credential_admin_only, activado por defecto). La contraseña en texto plano va solo en el cuerpo — nunca se persiste (PBKDF2-SHA512 en reposo) ni se devuelve. Una ROTACIÓN revoca el ddw_ vivo del agente (la expulsión por cambio de clave) Y desmonta el endpoint SIP creado bajo esa sesión (NR-02), informado en sip_teardown.

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
passwordstring (password)sí

Respuestas

200 ​

El agente (ahora con login_enabled true), más el resultado del desmontaje SIP.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.updated_atstring (date-time)no
sip_teardownok · skipped · failedsíResultado del desmontaje SIP que acompaña a una revocación (NR-02): el borrado de la credencial efímera agentcred del agente y de su registro usrloc vivo en Kamailio. * ok: credencial y registro eliminados; el softphone ya no puede registrarse. * skipped: no había nada que eliminar: el agente no tiene extensión SIP, el aprovisionamiento del SBC no está configurado en este entorno, o el agente aún conserva una sesión VIGENTE (véase abajo), por lo que esta revocación no dejó ningún acceso abierto. * failed: la revocación en la base de datos se mantiene, pero la credencial SIP SIGUE ACTIVA y lo seguirá hasta su caducidad automática a las 24 h. Trátalo como una vía de acceso abierta: reintenta o expulsa al agente por otros medios. Es un campo y no un código de estado a propósito: la revocación en sí es duradera y sí ocurrió, así que un 5xx mentiría en el otro sentido. Lo que nunca debe pasar es un 200 silencioso que implique una expulsión que no ocurrió. El desmontaje sigue a la TRANSICIÓN, no al agente. Revocar un id de token que ya estaba revocado (un id que un cliente puede conservar legítimamente, ya que GET /v1/widget_tokens lista las filas revocadas, y ya que emitir un token sustituye al anterior) NO debe expulsar, porque se cortaría la sesión SUCESORA del agente. Por tanto, el desmontaje solo se ejecuta cuando el agente se queda sin ningún token vigente; en caso contrario informa skipped. Esto es lo que mantiene útil el consejo de reintento de failed: un token ya revocado y sin sucesor vivo sí expulsa en un DELETE reintentado, de modo que reintentar es un remedio real y no una operación vacía.

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 — AgentCredentialForbidden ​

Se rechazó establecer o borrar la contraseña de acceso al widget de un puesto (core#2404, adenda del ADR #126). coderole_forbidden: quien llama es un inicio de sesión de dashboard que no es ni account_admin ni el inicio de sesión vinculado a ese puesto (la ruta admite una clave dd_, un inicio de sesión account_admin o el inicio de sesión propio del puesto); el parámetro por tenant agent_credential_admin_only (activado por defecto) lo desactiva. Cualquier otro 403 es el rechazo simple de alcance (forbidden: un ddw_ o una clave de sitio público).

json
{
  "error": {
    "code": "role_forbidden",
    "message": "setting or clearing a seat's widget password takes a dd_ key, a dashboard login with the account_admin role, or the seat's own login"
  }
}

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

Deshabilitar el acceso al widget de un agente ​

http
DELETE /v1/agents/{id}/credential

Borra la contraseña de acceso al widget del agente (vuelve a login_enabled: false) y revoca cualquier ddw_ vivo que tenga el agente, de modo que la deshabilitación surte efecto de inmediato — INCLUIDO el endpoint SIP (NR-02), informado en sip_teardown. Sin esa mitad, un agente deshabilitado seguía marcando por SIP hasta la autoexpiración de 24 h de la credencial. El puesto en sí no se toca. Idempotente. Alcance de tenant (un ddw_ recibe 403). Mismas reglas de rol que el POST (core#2404): un inicio de sesión member recibe 403 role_forbidden en cualquier puesto que no sea el suyo.

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 agente (ahora con login_enabled false), más el resultado del desmontaje SIP.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.updated_atstring (date-time)no
sip_teardownok · skipped · failedsíResultado del desmontaje SIP que acompaña a una revocación (NR-02): el borrado de la credencial efímera agentcred del agente y de su registro usrloc vivo en Kamailio. * ok: credencial y registro eliminados; el softphone ya no puede registrarse. * skipped: no había nada que eliminar: el agente no tiene extensión SIP, el aprovisionamiento del SBC no está configurado en este entorno, o el agente aún conserva una sesión VIGENTE (véase abajo), por lo que esta revocación no dejó ningún acceso abierto. * failed: la revocación en la base de datos se mantiene, pero la credencial SIP SIGUE ACTIVA y lo seguirá hasta su caducidad automática a las 24 h. Trátalo como una vía de acceso abierta: reintenta o expulsa al agente por otros medios. Es un campo y no un código de estado a propósito: la revocación en sí es duradera y sí ocurrió, así que un 5xx mentiría en el otro sentido. Lo que nunca debe pasar es un 200 silencioso que implique una expulsión que no ocurrió. El desmontaje sigue a la TRANSICIÓN, no al agente. Revocar un id de token que ya estaba revocado (un id que un cliente puede conservar legítimamente, ya que GET /v1/widget_tokens lista las filas revocadas, y ya que emitir un token sustituye al anterior) NO debe expulsar, porque se cortaría la sesión SUCESORA del agente. Por tanto, el desmontaje solo se ejecuta cuando el agente se queda sin ningún token vigente; en caso contrario informa skipped. Esto es lo que mantiene útil el consejo de reintento de failed: un token ya revocado y sin sucesor vivo sí expulsa en un DELETE reintentado, de modo que reintentar es un remedio real y no una operación vacía.

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 — AgentCredentialForbidden ​

Se rechazó establecer o borrar la contraseña de acceso al widget de un puesto (core#2404, adenda del ADR #126). coderole_forbidden: quien llama es un inicio de sesión de dashboard que no es ni account_admin ni el inicio de sesión vinculado a ese puesto (la ruta admite una clave dd_, un inicio de sesión account_admin o el inicio de sesión propio del puesto); el parámetro por tenant agent_credential_admin_only (activado por defecto) lo desactiva. Cualquier otro 403 es el rechazo simple de alcance (forbidden: un ddw_ o una clave de sitio público).

json
{
  "error": {
    "code": "role_forbidden",
    "message": "setting or clearing a seat's widget password takes a dd_ key, a dashboard login with the account_admin role, or the seat's own login"
  }
}

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

Puesto en línea + arranque del softphone ​

http
POST /v1/agents/checkin

El puesto pasa a available. El device_mode opcional (browser|external) persiste en el puesto y afina la búsqueda de puente de la campaña; si se omite se conserva el valor almacenado (el re-checkin sigue siendo idempotente). Cuando el aprovisionamiento del SBC está habilitado (KAMAILIO_RPC_URL; siempre activo en prod) la respuesta lleva el arranque COMPLETO del softphone — una contraseña SIP efímera generada en cada checkin, aprovisionada en el htable agentcred de Kamailio (el ÚNICO lugar donde existe; autoexpira a las 24 h). El re-checkin la reemite. Con el aprovisionamiento deshabilitado la clave sip simplemente está ausente.

Cuando la celda ejecuta su propio relay TURN, sip.turn lleva una credencial de relay efímera con el mismo horizonte (TurnIceServer, core#1076); ausente en caso contrario.

Dos fallas DESPUÉS de abrir el puesto responden 503 y lo revierten, de modo que el puesto nunca queda reclamando a un agente al que quien llama no puede llegar. Ramifica según code: sbc_unavailable cuando falla el RPC del SBC — nunca se devuelven credenciales que no puedan registrarse — y presence_unavailable cuando falla la escritura duradera de presencia del puesto (core#557).

QUÉ puesto se DERIVA DEL TOKEN, no del cuerpo (core#1033, ADR #131). Un inicio de sesión de dashboard (ddu_) hace check-in del puesto al que users_agents.user_id vincula a esa persona. Un token de navegador de agente (ddw_) recibe 403 en esta ruta por la lista de permitidos del widget (Dialer.API.WidgetAllowlist), igual que en el resto de /v1/agents. Por lo tanto sip_extension es OPCIONAL para una sesión ddu_, y enviar uno que no sea el puesto del propio token da 403 seat_mismatch — nunca se acepta en silencio. Una clave de tenant dd_ no representa a ninguna persona, así que sigue nombrando el puesto mediante sip_extension (el principal de toda la cuenta ya puede crear y eliminar cualquier puesto).

LA COMPUERTA DEL AVISO (core#2323, ADR #289). Con el flag del tenant gobernado por el founder agent_monitoring_ack_enforced activado y al menos una campaña que graba (record_calls) y no está completada ni archivada, un puesto que no ha aceptado el aviso de grabación y supervisión VIGENTE (POST /v1/agent-monitoring-acknowledgements) recibe 403 monitoring_ack_required ANTES de que cambie nada: el puesto permanece fuera de línea y no se genera ninguna credencial SIP. El cuerpo lleva error.notice_version, la versión que se debe aceptar (null cuando no hay ninguna publicada, lo que también cierra la compuerta: no hay nada que aceptar). Nace desactivado.

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
sip_extensionstringnoOBLIGATORIO con una clave de tenant dd_; opcional con una sesión ddu_, donde el puesto sale del token y un valor que no coincida con él da 403.
device_modebrowser · externalnobrowser = softphone WSS que pasa primero por kamailio; external = directo al registrar de FS.
json
{
  "sip_extension": "1001",
  "device_mode": "external"
}

Respuestas

200 ​

Puesto en línea (+ arranque del softphone cuando el aprovisionamiento del SBC está activado).

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.updated_atstring (date-time)no
sipobject (SipBootstrap)noArranque del softphone emitido en el checkin cuando el aprovisionamiento del SBC está habilitado. La contraseña efímera vive SOLO en el htable agentcred de Kamailio — nunca en Postgres, nunca en logs. Ausente por completo cuando el aprovisionamiento está deshabilitado.
sip.extensionstringnoUsuario de digest (AOR simple).
sip.passwordstringnoEfímera, generada en cada checkin, NUNCA se persiste.
sip.wss_urlstringnoSOFTPHONE_WSS_URL.
sip.domainstringnoSOFTPHONE_SIP_DOMAIN (URI From).
sip.expires_atstring (date-time)noCuándo deja de funcionar esta credencial. El horizonte de autoexpiración del htable (24 h) LIMITADO por la sesión de widget del agente, cuando ESA sesión aún la gobierna: el tope absoluto de esa sesión es de 12 h como máximo y el servidor expulsa la credencial en el momento en que caduca, de modo que el más temprano de los dos es el único horizonte contra el que un cliente puede planificar una renovación. Siempre en el futuro — una sesión cuyo tope ya caducó informa el horizonte plano, nunca un instante pasado para una credencial recién emitida.
sip.turnobject (TurnIceServer)noEl relay TURN propio de la celda (coturn) para agentes cuya red bloquea UDP (MT-CTI-16, core#1076). Una entrada RTCIceServer de WebRTC: el softphone la agrega a la lista STUN que lee de GET /v1/softphone/ice. Viaja en ESTA respuesta autenticada precisamente porque ese endpoint no está autenticado — una credencial de relay allí llegaría a todos los navegadores. Efímera, esquema use-auth-secret de coturn: username es <unix expiry>:<agent id> y credential es Base64(HMAC-SHA1(shared secret, username)); no se almacena nada por agente. Caduca en el MISMO instante que la contraseña SIP (expires_at), de modo que el re-checkin que renueva una renueva ambas. Ausente cuando la celda no tiene TURN configurado (SOFTPHONE_TURN_URLS / SOFTPHONE_TURN_SECRET sin definir): el softphone entonces negocia sin relay, como hacía antes de que existiera esta clave.
sip.turn.urlsarreglo de stringsíURLs turn: / turns: (:3478 UDP/TCP, :5349 TLS).
sip.turn.usernamestringsí<unix expiry>:<agent id>.
sip.turn.credentialstringsíEfímera; NUNCA se persiste.
json
{
  "agent": {
    "id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "tenant_id": "0d4f4f9e-1f2a-4b53-9d3c-8a5e2f7b1c10",
    "name": "Postman Seat",
    "email": "[email protected]",
    "role": "agent",
    "status": "active",
    "sip_extension": "1001",
    "device_mode": "external",
    "presence": "available",
    "created_at": "2026-06-01T12:00:00Z",
    "updated_at": "2026-07-05T10:00:00Z"
  },
  "sip": {
    "extension": "1001",
    "password": "ephemeral-example-not-real",
    "wss_url": "wss://127.0.0.1:8443",
    "domain": "sip.usa.dialerdigital.com",
    "expires_at": "2026-07-06T10:00:00Z",
    "turn": {
      "urls": [
        "turn:turn.example.test:3478?transport=udp",
        "turns:turn.example.test:5349?transport=tcp"
      ],
      "username": "1783332000:1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
      "credential": "ephemeral-example-not-real"
    }
  }
}

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 — SeatFromToken ​

No se pudo derivar el puesto del token, o el cuerpo lo contradijo (core#1033, ADR #131). code seat_not_linked: este inicio de sesión no tiene ningún puesto de agente activo vinculado (users_agents.user_id), o el puesto al que se vinculó el token ya no está activo — un administrador vincula el puesto, quien llama no puede elegir uno. code seat_mismatch: sip_extension nombró un puesto que no es el propio del token; el mensaje deliberadamente NO revela cuál lo es, para que el rechazo no sea un oráculo de las extensiones de los compañeros. Un puesto derivado que AÚN NO tiene sip_extension es 409 en su lugar (el token y el puesto son ambos correctos; lo que falta es un campo de la lista que solo un administrador puede completar).

json
{
  "error": {
    "code": "seat_mismatch",
    "message": "sip_extension does not match the seat this token owns"
  }
}

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

503 — CheckinUnavailable ​

Checkin rechazado de forma cerrada DESPUÉS de abrir el puesto, por lo que el puesto se revirtió y no queda nada durable que afirme que el agente está disponible. Reintenta. DOS causas, que se distinguen por code (ErrorEnvelope indica ramificar según él, y un cliente que solo conoce una de ellas no hará nada, en silencio, ante la otra):

  • sbc_unavailable — falló el RPC del SBC. Nunca se devuelven credenciales que no puedan registrarse.
  • presence_unavailable — falló la escritura duradera de presencia del puesto (core#557). No es cosmético: esa columna es lo que SessionCapSweeper lee para distinguir esta sesión de la que está desmontando, de modo que un checkin que respondiera 200 sin ella entrega una credencial SIP viva sobre un puesto que el sweeper puede recuperar.

Esta respuesta reemplazó a la antigua SbcUnavailable, cuyo nombre prometía la única causa que tenía cuando el checkin solo tenía una.

sbc_unavailable

json
{
  "error": {
    "code": "sbc_unavailable",
    "message": "softphone provisioning unavailable, retry"
  }
}

presence_unavailable

json
{
  "error": {
    "code": "presence_unavailable",
    "message": "seat presence could not be recorded, retry"
  }
}

Puesto fuera de línea (revoca la credencial SIP efímera) ​

http
POST /v1/agents/checkout

Solo un puesto available puede salir (409 en caso contrario, falla de forma cerrada — un puesto en una llamada no puede desaparecer). Revoca la credencial SIP efímera (borrado idempotente del htable; la autoexpiración de 24 h es el respaldo ante una revocación omitida).

Misma derivación del puesto que agentCheckin (core#1033, ADR #131): el puesto sale del token en una sesión ddu_, de modo que nadie puede hacer check-out de un compañero nombrando su extensión. Una clave de tenant dd_ sigue nombrándolo; un ddw_ recibe 403 aquí por la lista de permitidos del widget.

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
sip_extensionstringnoOBLIGATORIO con una clave de tenant dd_; opcional con una sesión ddu_, donde el puesto sale del token y un valor que no coincida con él da 403.
json
{
  "sip_extension": "1001"
}

Respuestas

200 ​

Puesto fuera de línea.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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 — SeatFromToken ​

No se pudo derivar el puesto del token, o el cuerpo lo contradijo (core#1033, ADR #131). code seat_not_linked: este inicio de sesión no tiene ningún puesto de agente activo vinculado (users_agents.user_id), o el puesto al que se vinculó el token ya no está activo — un administrador vincula el puesto, quien llama no puede elegir uno. code seat_mismatch: sip_extension nombró un puesto que no es el propio del token; el mensaje deliberadamente NO revela cuál lo es, para que el rechazo no sea un oráculo de las extensiones de los compañeros. Un puesto derivado que AÚN NO tiene sip_extension es 409 en su lugar (el token y el puesto son ambos correctos; lo que falta es un campo de la lista que solo un administrador puede completar).

json
{
  "error": {
    "code": "seat_mismatch",
    "message": "sip_extension does not match the seat this token owns"
  }
}

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

Sacar de la rotación un puesto disponible (en pausa) ​

http
POST /v1/agents/{id}/pause

Mueve un puesto available (o wrap_up) a paused con un motivo de un conjunto CERRADO; a un puesto en pausa nunca se le entrega una llamada y aún puede hacer check-out. Solo alcance de tenant (un token de agente ddw_ recibe 403 por la lista de permitidos del widget). 409 cuando el agente no ha hecho check-in, está reservado / en una llamada, o ya está en pausa (falla de forma cerrada: no se fuerza nada) — salvo que se envíe on_release: true: en un puesto ringing / on_call eso ARMA la pausa (ADR #101 D5, core#2542), el puesto sigue en su llamada y se responde con pause_armed: true; cuando la llamada se libera (contestada o no) el puesto pasa directamente a paused con ese motivo, sin pasar nunca por available. Armarla de nuevo es idempotente (gana el último motivo); POST /v1/agents/{id}/resume la desarma; una baja armada por el barrido del tope de sesión gana y la descarta. on_release en un puesto sin llamada se comporta como la pausa simple. El puesto es estado en memoria; que el puesto siga fuera de la rotación tras un reinicio depende del flag agent_session_journal_enabled del tenant (ADR #101 D10).

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
reasonmanual · break · lunch · training · meetingsí
on_releasebooleannoArma la pausa para cuando termine la llamada actual en lugar de rechazar con 409 (ADR #101 D5).
json
{
  "reason": "lunch"
}

Respuestas

200 ​

Puesto en pausa; agent.presence se lee de vuelta del puesto.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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"
  }
}

Devolver a la rotación un puesto en pausa (disponible) ​

http
POST /v1/agents/{id}/resume

Devuelve un puesto paused a available, al FINAL de la cola de reservas. Solo alcance de tenant. 409 cuando el agente no ha hecho check-in o el puesto no está en pausa. Salir de la pausa es volver a Ready, así que pasa por la misma compuerta del aviso que el check-in: 403 monitoring_ack_required cuando la compuerta está activada y el agente no ha aceptado el aviso vigente (ver POST /v1/agents/checkin).

Parámetros

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

Respuestas

200 ​

Puesto disponible de nuevo; agent.presence se lee de vuelta del puesto.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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"
  }
}

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

Terminar el trabajo posterior a la llamada (wrap_up -> available) ​

http
POST /v1/agents/{id}/wrap-up

El agente terminó el trabajo posterior a la llamada: mueve un puesto wrap_up de vuelta a available, al FINAL de la cola de reservas, y descarta el token de reserva que la llamada dejó en el puesto. Solo alcance de tenant. 409 cuando el agente no ha hecho check-in o el puesto no está en wrap_up.

NO hay ningún verbo que ENTRE en wrap_up: la FSM del puesto entra en él solo al colgar una llamada CONTESTADA, y solo cuando el tope agent_wrap_up_seconds del tenant es mayor que 0 (ADR #101 D5/D8). El tope es la otra salida — el puesto vuelve a available por sí solo cuando expira — y POST /v1/agents/{id}/pause es la tercera (un agente puede cerrar su ACW con una pausa).

Parámetros

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

Respuestas

200 ​

Trabajo posterior a la llamada terminado; agent.presence se lee de vuelta del puesto.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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"
  }
}

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

Establecer la presencia de un puesto por id de la lista (una FACHADA sobre los verbos de la FSM del puesto) ​

http
PATCH /v1/agents/{id}/status

NO es un segundo modelo de presencia y NO es una escritura de estado: el status solicitado se traduce al verbo que la FSM del puesto ya tiene, y la FSM decide. Todo lo que rechace es 409 con el current_state del puesto; no se fuerza nada. Destino → verbo (el puesto se lee en el momento de la llamada):

statusdesde pauseddesde wrap_upen otro caso
availablePOST /v1/agents/{id}/resumePOST /v1/agents/{id}/wrap-up409
paused (requiere reason)409POST /v1/agents/{id}/pausePOST /v1/agents/{id}/pause
offlinePOST /v1/agents/checkoutPOST /v1/agents/checkoutPOST /v1/agents/checkout

available desde offline es 409 a propósito: el check-in deriva el puesto del token y genera una credencial SIP efímera (POST /v1/agents/checkin), que una llamada por id no debe entregar a quien nombre el puesto. ringing y on_call nunca son un destino: la FSM entra en ellos solo con una llamada. Los eventos de presencia en el piso en vivo (agent.presence) son los MISMOS frames que emiten los verbos anteriores, porque se ejecuta el mismo verbo.

Solo alcance de tenant (un token de agente ddw_ recibe 403 por la lista de permitidos del widget). available desde paused pasa por la misma compuerta del aviso que POST /v1/agents/{id}/resume (403 monitoring_ack_required). Un id de otro tenant es 404. offline además sigue la regla de autor del check-out: un inicio de sesión ddu_ CON un puesto vinculado solo puede establecer su PROPIO puesto (403 en caso contrario).

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
statusavailable · paused · offlinesí
reasonmanual · break · lunch · training · meetingnoObligatorio cuando status es paused; se ignora en otro caso.
json
{
  "status": "paused",
  "reason": "lunch"
}

Respuestas

200 ​

Estado aplicado; agent.presence se lee de vuelta del puesto.

CampoTipoObligatorioDescripción
agentobject (Agent)sí
agent.idstring (uuid)no
agent.tenant_idstring (uuid)no
agent.namestringno
agent.emailstringno
agent.roleagent · supervisor · adminno
agent.statusactive · inactivenoIndicador de plantilla — NO es presencia.
agent.sip_extensionstring | nullno
agent.device_modebrowser · externalno
agent.login_enabledbooleannoEstado de autoinicio de sesión del widget — verdadero si y solo si hay una credencial de contraseña establecida. El hash de la contraseña nunca se expone.
agent.presenceoffline · available · ringing · on_call · wrap_up · pausednoPresencia en vivo del asiento desde el AgentFSM en memoria. wrap_up es el trabajo posterior a la llamada (se entra al colgar cuando el tope agent_wrap_up_seconds del tenant es superior a 0); paused es un asiento retirado de la rotación con un motivo (ADR #101 D1, MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused (core#2542).
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento (core#2542).
agent.pause_armedbooleannoHay una pausa armada para cuando termine la llamada actual (POST /v1/agents/{id}/pause con on_release: true).
agent.created_atstring (date-time)no
agent.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 ​

La FSM del puesto no admite ese movimiento desde el estado actual del puesto.

json
{
  "error": {
    "code": "conflict",
    "message": "seat is on_call; the FSM has no transition to that status from here",
    "current_state": "on_call"
  }
}

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

Listar los turnos planificados del tenant ​

http
GET /v1/planned-shifts

Sin from/to: los 500 turnos con el start_time más temprano, y truncated: true cuando hay más. Los turnos son de solo anexar, así que pasado el 500.º un turno más nuevo NO está en esta lista: pide su ventana.

Con from y to (RFC 3339, ambos o ninguno, from anterior a to): todos los turnos que se solapan con [from, to), sin tope, ordenados por start_time; truncated es false.

Parámetros

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)noInicio de la ventana (RFC 3339). Requiere to.
toquerystring (date-time)noFin de la ventana (RFC 3339), exclusivo. Requiere from.

Respuestas

200 ​

Lista de turnos planificados

CampoTipoObligatorioDescripción
shiftsarreglo de PlannedShiftsí
shifts[].idstring (uuid)sí
shifts[].agent_idstring (uuid)sí
shifts[].start_timestring (date-time)sí
shifts[].end_timestring (date-time)sí
shifts[].planned_stateavailable · ringing · on_call · wrap_up · pausedsí
truncatedbooleansítrue cuando, sin ventana, hay más turnos que los 500 devueltos. Siempre false con from/to.

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

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

Crear un turno planificado ​

http
POST /v1/planned-shifts

Planifica un turno para un agente (agent_id, start_time, end_time, planned_state opcional). Un agent_id ausente es 400 invalid.

Quién puede nombrar a quién (ADR #133, ADR #134). El agent_id queda acotado por el PRINCIPAL, con la misma regla en cada verbo que lee uno: una clave de máquina dd_ nombra a cualquier agente de su tenant; un ddw_ solo al propio; una sesión de inicio de sesión ddu_ vinculada a un puesto de agente actúa como ESE puesto — agent_id puede omitirse (se completa con el puesto) o repetirlo, y nombrar a otro agente es 403 forbidden; una sesión ddu_ SIN puesto vinculado puede nombrar a otro agente solo con users.role = account_admin — un member recibe 403 role_forbidden — una vez que la clave de catálogo del tenant ddu_author_role_enforced está activada (nace desactivada: hasta entonces la sesión sin puesto conserva el contrato dd_ en este verbo).

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
agent_idstring (uuid)noEl agente para el que se planifica el turno. Obligatorio con una clave dd_; una sesión ddu_ vinculada a un puesto puede omitirlo (su puesto) y no puede nombrar a otro.
start_timestring (date-time)sí
end_timestring (date-time)sí
planned_stateavailable · ringing · on_call · wrap_up · pausedno

Respuestas

201 ​

Turno planificado creado.

CampoTipoObligatorioDescripción
idstring (uuid)sí

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

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

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