Saltar al contenido

Agentes y puestos ​

Generado desde DialerDigital/core commit bbb8e373d900 — especificación openapi/dialer-v1.yaml v0.151.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
POST/v1/agent/preview/nextLa siguiente cuenta de una campaña PREVIEW para el PROPIO puesto de quien llama
POST/v1/agent/preview/dialMarcar la cuenta ofrecida (preview) o una elegida (manual)
POST/v1/agent/preview/skipSaltar la cuenta ofrecida al 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 (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. 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: un lead de "vista previa pendiente" por agente. La cuenta que una campaña preview ofrece a un puesto se pide, y se reserva, con POST /v1/agent/preview/next (derivado de un stream de solo anexado), no se lee de esta cola. 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 (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. Ningún pago lo cambia.

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

La siguiente cuenta de una campaña PREVIEW para el PROPIO puesto de quien llama ​

http
POST /v1/agent/preview/next

Pide al dialer la siguiente cuenta marcable de una campaña preview en curso y la RESERVA para el puesto de quien llama. El puesto sale del bearer — ningún campo nombra a un agente — por lo que se aplica el mismo contrato de identidad que en GET /v1/agent/queue: un inicio de sesión ddu_ con un puesto vinculado; una clave de máquina dd_ o un ddu_ sin puesto recibe 403; un ddw_ recibe 403 antes del handler.

Dos puestos nunca reciben la misma cuenta. La reserva no es una columna de la cuenta: se deriva del stream de solo anexado agent_preview_events (decide el último hecho de la cuenta) y caduca por sí sola en expires_at; una oferta caducada se vuelve a ofrecer. La llamada es idempotente: mientras el puesto conserve una oferta viva en la campaña, se devuelve la misma oferta con su expires_at original.

offer: null significa que ahora no se puede ofrecer ninguna cuenta. El puesto debe estar available (409 agent_not_available con current_state). Una campaña que no es preview recibe 422; una que no está en curso, 409.

Aquí no se ejecuta ningún control de cumplimiento; todos los controles (DNC, horario de silencio, consentimiento, frecuencia, ...) se evalúan al marcar la oferta, con fallo cerrado.

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
campaign_idstring (uuid)sí

Respuestas

200 ​

La oferta reservada, o null cuando no hay nada que ofrecer.

CampoTipoObligatorioDescripción
offerPreviewOffer | nullsí

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 (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. 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 — 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)"
  }
}

Marcar la cuenta ofrecida (preview) o una elegida (manual) ​

http
POST /v1/agent/preview/dial

Marca como el PROPIO puesto de quien llama, por el MISMO pipeline con controles que POST /v1/campaigns/{id}/dial (Runner.manual_dial/4): puesto, comprobación del softphone, pool de caller-ID y los controles de cumplimiento, evaluados AHORA y con fallo cerrado. Los resultados son los de ese verbo: 200 dialing con call_id; 200 blocked con el reason del control (no se hizo ninguna llamada y la cuenta queda aparcada para todos durante un tiempo); 409, 429, 503.

Envía exactamente uno de:

  • offer_id — una campaña preview: la oferta que POST /v1/agent/preview/next devolvió a ESTE puesto y que sigue viva. Una oferta de otro puesto, o una desconocida, recibe 404; una caducada o sustituida recibe 409 offer_expired.
  • debt_id — una campaña manual: la cuenta que el agente eligió de la lista de la campaña. Si otro puesto la tiene ahora mismo, la respuesta es 409 account_held.

Escribir el hecho dialing va primero: si no se puede registrar, la respuesta es 503 audit_unavailable y no se marca nada. Cada resultado deja su propio hecho (dialed, blocked, failed).

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
campaign_idstring (uuid)sí
offer_idstring (uuid)no
debt_idstring (uuid)no

Respuestas

200 ​

Resultado de la marcación: marcando, o bloqueada por una compuerta de cumplimiento.

CampoTipoObligatorioDescripción
statusdialing · blockedsí
call_idstring (uuid)noPresente cuando status es dialing.
reasontcpa_no_consent · quiet_hours · regf_7in7 · regf_post_contact · state_limit · dnc_listed · consent_revoked · cease_and_desist · number_reassigned · tenant_policy · artificial_voice_no_consentnoEl vocabulario de códigos de motivo del motor de cumplimiento.

dialing

json
{
  "status": "dialing",
  "call_id": "6f0a1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c"
}

blocked

json
{
  "status": "blocked",
  "reason": "quiet_hours"
}

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 (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. 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 — 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 — Throttled ​

Freno de ritmo de marcación (reintentable más tarde con espera creciente). No se envía la cabecera Retry-After: el limitador no sabe cuándo se recupera la capacidad. Por encima de la cuota por clave esta operación responde rate_limited como en ApiKeyRateLimited.

json
{
  "error": {
    "code": "throttled",
    "message": "dial throttled (rate)"
  }
}

502 — DialError ​

El dialer no pudo realizar la llamada (el switch rechazó el originate).

json
{
  "error": {
    "code": "dial_error",
    "message": "the dialer could not place the call"
  }
}

503 ​

Sin evidencia no hay llamada (audit_unavailable), o no se pudo verificar el registro del softphone (registration_unverifiable); no se marcó nada (reintentable).

json
{
  "error": {
    "code": "audit_unavailable",
    "message": "no evidence, no call: retry"
  }
}

Saltar la cuenta ofrecida al puesto de quien llama ​

http
POST /v1/agent/preview/skip

Libera la oferta y APARCA la cuenta para todos durante un tiempo (un salto no es un intento de contacto). El salto es un hecho de solo anexado, por lo que deja un rastro auditable: quién saltó qué cuenta, cuándo y el reason opcional de vocabulario cerrado (el texto libre se rechaza: podría contener datos personales). Una oferta que no está viva y es del propio puesto recibe 409 offer_expired; la de otro puesto o una desconocida, 404.

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
campaign_idstring (uuid)sí
offer_idstring (uuid)sí
reasonnot_now · bad_data · otherno

Respuestas

200 ​

El hecho del salto.

CampoTipoObligatorioDescripción
skippedobject (PreviewSkip)síEl hecho de solo anexado que deja un salto (POST /v1/agent/preview/skip).
skipped.offer_idstring (uuid)sí
skipped.campaign_idstring (uuid)sí
skipped.debt_idstring (uuid)sí
skipped.reason`` · not_now · bad_data · othersí
skipped.skipped_atstring (date-time)sí
skipped.parked_untilstring (date-time)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 (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. 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 — 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)"
  }
}

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 (MT-ROAD-R11).
agents[].pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agents[].state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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": "postman+1751600000@example.test",
  "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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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 (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. 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 (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): 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 (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. 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 (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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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. 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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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. 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 (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 (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. 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. 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 (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: 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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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. 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 (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: 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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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. 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 (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); 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.

QUÉ puesto se DERIVA DEL TOKEN, no del cuerpo. 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. 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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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. 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": "postman@example.test",
    "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. 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 (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. 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: 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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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. 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 (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, 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.

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.
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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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. 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 (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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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. 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 (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. 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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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. 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 (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 (MT-ROAD-R11).
agent.pause_reasonmanual · break · lunch · training · meeting | nullnoPor qué el asiento está en pausa; null salvo que presence sea paused.
agent.state_sincestring (date-time) | nullnoCuándo el asiento entró en su presence actual (RFC 3339); null cuando el agente no tiene asiento.
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 (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. 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 (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 (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. 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 (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. 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 (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. 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 (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"
  }
}

Nada de esta documentación es asesoría legal: confirma siempre tu postura de cumplimiento con tu propio abogado.