Appearance
Identidad
Generado desde
DialerDigital/corecommit0fc18008b3c8— especificaciónopenapi/dialer-v1.yamlv0.134.0. No se edita a mano; consulta Cómo se construye esta referencia. Descargar la especificación OpenAPI.
Traducción en curso
Esta referencia se genera desde la especificación OpenAPI. Las cadenas que todavía no tienen traducción aparecen en inglés.
Tenant autenticado, ajustes de la cuenta.
Endpoints
Tenant autenticado, usuario y ASIENTO derivado
http
GET /v1/meDevuelve el tenant resuelto a partir del bearer. Cuando el bearer es una sesión de inicio de sesión ddu_ (autenticación del dashboard con email y contraseña), la respuesta también incluye el user que está detrás de la consola; para una clave de máquina dd_ la forma solo con el tenant no cambia.
También responde a "¿qué asiento soy?" (seat + seat_source, core#1059, ADR #137). Un cliente NO DEBE dejar que el operador ELIJA un asiento: desde core#1034 (ADR #131) POST /v1/agents/checkin DERIVA el asiento del bearer y responde 403 seat_mismatch a cualquier sip_extension del cuerpo que no coincida con él. El asiento aquí se deriva por el mismo único punto de derivación que usan el check-in y GET /v1/me/scorecard (ADR #132), de modo que las dos respuestas no pueden discrepar.
seat_source está SIEMPRE presente: un cliente nunca tiene que interpretar un campo ausente:
token—seates la fila de agente propia de quien llama, en la misma proyección que devuelveGET /v1/agents/{id}.no_identity— una clave de máquinadd_: no hay ninguna persona detrás, así que no existe un asiento "propio".seatesnull; NO se consulta el cuerpo.seat_not_linked— una personaddu_sin asiento activo (p. ej. un supervisor sin posición de marcación).seatesnull.seat_gone— el bearer apunta a una fila de agente eliminada. Solo es alcanzable por el carrilddw_, que esta ruta deniega antes del handler (no está en la lista de permitidos del widget); se lista por completitud.
Las cuatro son 200. Una persona sin asiento es un estado legítimo y esta ruta hace de validador de bearer de la SPA, así que responder 403 ahí rompería su inicio de sesión; esa es la diferencia deliberada con GET /v1/me/scorecard, que SÍ es 403 sin asiento porque no hay ningún marcador que mostrar.
Una sesión de soporte dds_ (ADR #225) responde seat: null, seat_source: no_identity y un bloque support (read_only es siempre true, con el expires_at de la sesión y el operador que está detrás) para que una consola pueda mostrar que la sesión es de solo lectura y cuándo termina.
Respuestas
200
El tenant autenticado, el usuario (ddu_) y el asiento derivado.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenant | object (Tenant) | sí | |
tenant.id | string (uuid) | no | |
tenant.name | string | no | |
tenant.account_id | string (uuid) | no | La EMPRESA a la que pertenece este tenant (sede) (DA-2). |
tenant.billing_account_id | string (uuid) | no | La cuenta de facturación bajo la que se factura la sede (DA-3). Compartida por todas las sedes de una empresa consolidated; una por sede cuando es per_site. |
tenant.status | active · suspended | no | |
tenant.synthetic | boolean | no | true para un tenant de demostración. No origina tráfico: la marcación manual, el envío de SMS, la pierna externa de una transferencia y el sandbox responden 403 synthetic_tenant. Siempre presente, nunca nulo, para que una consola pueda avisar ANTES de que la acción falle (core#2512). |
tenant.retention_months | integer | no | |
tenant.seats | integer | no | |
tenant.tier | starter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shield | no | |
tenant.voice_ai_enabled | boolean | no | |
tenant.ai_monthly_budget_usd | number | null | no | Techo de integridad de costes (lo fija el admin; null = sin tope). |
tenant.ai_max_call_seconds | integer | null | no | Techo estricto de duración por llamada de IA (segundos) fijado por el admin. null significa que se aplica el valor por defecto de la PLATAFORMA, nunca "sin techo" (core#922, ADR #115). |
tenant.ai_max_call_seconds_effective | integer | no | El techo que realmente se entrega a FreeSWITCH para las piernas de IA de este tenant: ai_max_call_seconds cuando está definido; si no, el valor por defecto de plataforma de la celda (AI_MAX_CALL_SECONDS_DEFAULT, valor de reserva compilado 900). Siempre presente, nunca nulo. Solo lectura (derivado). |
tenant.sms_monthly_budget_usd | number | null | no | Tope de gasto en SMS acumulado en el mes hasta la fecha. |
tenant.human_initiated_enabled | boolean | no | Postura de consentimiento D17 de iniciativa humana (la fija el admin; por defecto false). Cuando es true, una marcación de voz EN VIVO de vista previa/manual/CTI sin voz artificial y sin voz de IA no requiere consentimiento expreso previo (sin ATDS). Todas las demás compuertas (DNC, horas de silencio, Reg F, matriz estatal, voz artificial) siguen aplicándose sin cambios. |
tenant.max_cps | integer | null | no | Techo de CPS por tenant (lo fija el admin; null = sin techo). ADR #12. |
tenant.max_concurrent_channels | integer | null | no | Techo de llamadas concurrentes por tenant, una unidad por llamada con o sin agente (lo fija el admin; null = sin techo). ADR #12, ADR #185. |
tenant.abandon_seller_name | string | null | no | Nombre del vendedor para el mensaje de identificación de llamada abandonada de la FTC TSR (lo fija el admin; null = no configurado, el sobremarcado predictivo permanece apagado). ADR #14. |
tenant.abandon_seller_phone | string | null | no | Número de rellamada E.164 para el mensaje de identificación de llamada abandonada (lo fija el admin; null = no configurado). ADR #14. |
tenant.tax_country | string | null | no | País ISO-3166-1 alpha-2 de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_region | string | null | no | Código de estado/provincia de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_postal_code | string | null | no | Código postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.kyc_verified_at | string (date-time) | null | no | Cuándo verificó la plataforma a este cliente (lo fija el admin; null = aún no). Una cuenta de 1 asiento que no está verificada no puede crear ni iniciar una campaña que se ejecute sin agente (403 verification_required, ADR #279). |
tenant.created_at | string (date-time) | no | |
tenant.updated_at | string (date-time) | no | |
user | object (User) | no | Un usuario del dashboard; el hash de la contraseña nunca se expone. |
user.id | string (uuid) | no | |
user.email | string (email) | no | Identificador de inicio de sesión global, almacenado en minúsculas. |
user.name | string | no | |
user.status | active · disabled | no | |
user.role | member · account_admin | no | Privilegio dentro de la cuenta. Solo un account_admin puede crear o modificar las filas de toda la empresa que heredan todas las sedes hermanas (scope: "empresa" en carriers y proveedores de SMS); un member recibe 403. Solo lectura aquí: la promoción es un gesto del operador, nunca una petición. |
user.password_updated_at | string (date-time) | no | |
user.last_login_at | string (date-time) | null | no | |
user.mfa_enabled | boolean | no | Si la persona tiene un segundo factor ACTIVO (una inscripción pendiente de confirmación es false). Se emite SOLO cuando quien llama es un inicio de sesión de dashboard account_admin, el que puede actuar sobre él con POST /v1/users/{id}/mfa/remove; para un member, una clave dd_, o cuando no se puede determinar el rol de quien llama, el campo está AUSENTE (no es false). Dondequiera que se renderice un User rige la misma regla: GET /v1/users, GET /v1/me, el usuario devuelto por DELETE /v1/users/{id} y POST /v1/users/{id}/enable, y el usuario en las respuestas de sesión (POST /v1/auth/login, POST /v1/auth/sso/exchange y las dos rutas de finalización de MFA); en ellas, para el account_admin que acaba de iniciar sesión. |
user.created_at | string (date-time) | no | |
user.updated_at | string (date-time) | no | |
support | object | no | Solo para una sesión de soporte dds_ (ADR #225): la sesión es de solo lectura y termina en expires_at. |
support.read_only | true | sí | |
support.session_id | string (uuid) | sí | |
support.expires_at | string (date-time) | sí | |
support.operator | string | sí | El operador de la plataforma que la abrió, operator:<id> (<name>). |
seat | Agent | null | sí | El asiento propio de quien llama, o null cuando seat_source no es token. |
seat_source | token · no_identity · seat_not_linked · seat_gone | sí | Cómo se resolvió el asiento (o por qué no hay ninguno). Siempre presente. |
capabilities | object | sí | Capacidades de UI por tenant (core#1806), una lista cerrada. Una capacidad que no se puede resolver se lee como false. |
capabilities.conference | boolean | sí | true cuando las rutas internas de conferencia (POST /v1/calls/{call_id}/conference/add|remove) están habilitadas para este tenant; false significa que responden 404. |
capabilities.email_channel | boolean | no | true cuando el indicador email_channel_enabled del tenant está activado (core#1925): el emisor de email y el runner email_blast pueden actuar. false (el valor por defecto) significa que el canal está apagado: no escribe ninguna fila en el libro y no llama a ningún proveedor. NO significa "listo para enviar": siguen aplicándose un proveedor activo, el presupuesto, la procedencia de la dirección y todas las compuertas, y core responde con sus errores tipados. Está ausente en un core anterior a este cambio, así que un cliente DEBE leer una clave ausente como "aún no publicada" y nunca como false. |
screen_monitor | object (ScreenMonitorStatus) | sí | Lo que GET /v1/me informa sobre el monitoreo de pantalla para el asiento de quien llama (core#1989). Cierre por defecto ante fallo: un alcance solo está autorizado cuando enabled es true Y scopes.<scope>.ack_current es true. enabled: false (el indicador está apagado o no se pudo resolver) hace que todos los demás campos se lean como vacíos. notice_ack_required es true siempre que la función esté habilitada y el asiento carezca de un acuse de recibo de la versión VIGENTE para CUALQUIER alcance, también mientras aún no se haya publicado ningún aviso (entonces no hay nada que acusar: sigue cerrado). Quien llama sin asiento (clave dd_, ddu_ sin asiento, dds_) ve notice_ack_required: false y todos los ack_current: false. |
screen_monitor.enabled | boolean | sí | |
screen_monitor.notice_ack_required | boolean | sí | |
screen_monitor.notice_version | integer | null | sí | La versión vigente del aviso, o null cuando no hay ninguna publicada o la función está apagada. |
screen_monitor.scopes | object | sí | Por alcance, si el asiento tiene un acuse de recibo de la versión VIGENTE. El acuse de un alcance nunca hace vigente el del otro. |
screen_monitor.scopes.live_monitoring | object (ScreenMonitorScopeStatus) | sí | |
screen_monitor.scopes.live_monitoring.ack_current | boolean | sí | |
screen_monitor.scopes.live_monitoring.acked_at | string (date-time) | null | sí | Cuándo acusó recibo el asiento de la versión vigente para este alcance, o null. |
screen_monitor.scopes.screen_recording | object (ScreenMonitorScopeStatus) | sí | |
screen_monitor.scopes.screen_recording.ack_current | boolean | sí | |
screen_monitor.scopes.screen_recording.acked_at | string (date-time) | null | sí | Cuándo acusó recibo el asiento de la versión vigente para este alcance, o null. |
json
{
"tenant": {
"id": "0d4f4f9e-1f2a-4b53-9d3c-8a5e2f7b1c10",
"name": "Acme Collections",
"status": "active",
"synthetic": false,
"retention_months": 84,
"seats": 10,
"tier": "scale",
"voice_ai_enabled": true,
"ai_monthly_budget_usd": 500,
"ai_max_call_seconds": 600,
"ai_max_call_seconds_effective": 600,
"sms_monthly_budget_usd": 250,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-20T09:30:00Z"
},
"seat": null,
"seat_source": "no_identity",
"capabilities": {
"conference": false,
"email_channel": false
},
"screen_monitor": {
"enabled": false,
"notice_ack_required": false,
"notice_version": null,
"scopes": {
"live_monitoring": {
"ack_current": false,
"acked_at": null
},
"screen_recording": {
"ack_current": false,
"acked_at": null
}
}
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}El cuadro de mando del turno PROPIO de quien llama (asiento derivado del token)
http
GET /v1/me/scorecardEl marcador personal del gestor de cobranza: sus llamadas en la ventana y las promesas que tomó, para el asiento que identifica el BEARER. Ningún parámetro puede nombrar a un agente: agent_id se rechaza por su nombre con 400, nunca se ignora, de modo que un cliente nunca pueda creer que leyó las cifras de un colega.
Quién tiene un asiento: un inicio de sesión de dashboard ddu_, mediante el enlace users_agents.user_id (403 seat_not_linked cuando la persona no tiene asiento activo). Una clave de máquina dd_ no tiene ninguna persona detrás y es 403. Un token de widget ddw_ es 403 antes del handler: esta ruta no está en la lista de permitidos del widget.
Ventana: semiabierta [from, to). Ambos son opcionales y to es por defecto ahora. from es por defecto el check-in ACTIVO del asiento (window.source: checkin); cuando el asiento no tiene un check-in activo (el estado normal justo después del checkout, ya que checked_in_at es presencia, no historial) es por defecto now - 12h (window.source: fallback_12h). Doce horas cubren un turno completo de cobranza sin arrastrar el de ayer. Un cliente DEBE mostrar source: una cifra de una ventana estimada se muestra como tal, nunca como "el turno". calls cuenta INTENTOS, no filas de CDR (el original más sus correcciones son una sola llamada), y no lleva ratios ni minutos: la ocupación derivada es GET /v1/agents/occupancy, y calcular dos veces lo mismo es justo lo que aquí se evita.
Ausentes deliberadamente: la note de la promesa (texto libre, cifrado en reposo bajo GLBA), los números de teléfono y la comisión.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
from | query | string (date-time) | no | Inicio inclusivo (RFC 3339). Por defecto, el checked_in_at activo del asiento, o now - 12h cuando no hay ninguno; véase window.source. |
to | query | string (date-time) | no | Fin exclusivo (RFC 3339); debe ser posterior a from. Por defecto, ahora. |
Respuestas
200
El cuadro de mando propio de quien llama para la ventana resuelta.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
agent | object | sí | El asiento DERIVADO del bearer, devuelto en eco para que el cliente pueda mostrar de quién es el marcador, nunca para dejar que el cliente lo elija. |
agent.id | string (uuid) | sí | |
agent.name | string | sí | |
agent.sip_extension | string | null | sí | |
window | object | sí | |
window.from | string (date-time) | sí | |
window.to | string (date-time) | sí | |
window.source | checkin · explicit · fallback_12h | sí | De dónde vino from: el check-in ACTIVO del asiento (checkin, el turno), el parámetro del propio cliente (explicit), o la ventana estimada now - 12h usada cuando el asiento no tiene un check-in activo (fallback_12h, el estado normal justo después del checkout). Un cliente DEBE mostrar el caso estimado como tal. |
calls | object | sí | Contado por INTENTO (la fila original más cada corrección añadida a ella es UNA llamada), sobre los intentos cuyo started_at está en la ventana y que se atribuyen a este asiento, incluido el intento entrante cuyo agente fue nombrado por una asignación posterior. Invariante: total == in_flight + sum(by_disposition). |
calls.total | integer | sí | |
calls.in_flight | integer | sí | Intentos que aún no tienen una corrección que los cierre. Se cuentan aparte en lugar de asignarles una disposición inventada. |
calls.by_disposition | object | sí | Conteos de intentos indexados por la disposición de la ÚLTIMA corrección de cierre escrita para el intento. |
promises | object | sí | Promesas que este asiento REGISTRÓ en la ventana (inserted_at, no promised_date: un turno contiene promesas fechadas con semanas de antelación). |
promises.count | integer | sí | |
promises.by_status | object | sí | |
promises.by_status.pending | integer | sí | |
promises.by_status.kept | integer | sí | |
promises.by_status.broken | integer | sí | |
promises.by_status.cancelled | integer | sí | |
promises.amount_cents | object | sí | Sumado por moneda (USD y MXN nunca se suman juntos) y EXCLUYENDO las promesas cancelled: una cancelación es una retirada del operador, así que la fila sobrevive como evidencia pero el compromiso no. Las canceladas siguen siendo visibles en by_status. |
promises.amount_cents.USD | integer | sí | |
promises.amount_cents.MXN | integer | sí |
400
Ventana mal formada, o un parámetro agent_id: se rechaza por su nombre porque este cuadro de mando es siempre del asiento propio de quien llama.
json
{
"error": {
"code": "bad_request",
"message": "agent_id not accepted here: this scorecard is always the caller's own seat, derived from the token — use /v1/agents/occupancy or /v1/calls for another seat"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}El aviso vigente de monitoreo de pantalla
http
GET /v1/screen-monitor/noticeLa versión VIGENTE del aviso que un asiento debe acusar antes de que su pantalla pueda ser observada (core#1989, FASE 0 de dashboard#696). El aviso es un catálogo global (sin tenant) y de solo anexar: publicar una version más nueva invalida, sin tocarlos, todos los acuses de las anteriores.
404 not_found cuando el indicador screen_monitor_enabled del tenant, gobernado por el founder, está apagado, y también cuando aún no se ha publicado ninguna versión. Alcanzable con una clave dd_ y con un inicio de sesión ddu_; un token de widget ddw_ es 403 antes del handler.
Respuestas
200
El aviso vigente.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
notice | object (ScreenMonitorNotice) | sí | Una versión del aviso de monitoreo de pantalla (GET /v1/screen-monitor/notice). Global, de solo anexar. |
notice.version | integer | sí | |
notice.lang | string | sí | Idioma del texto, p. ej. es. |
notice.title | string | sí | |
notice.body | string | sí | |
notice.text_sha256 | string | sí | SHA-256 (hex) de body en UTF-8; un acuse de recibo lo copia. |
notice.effective_at | string (date-time) | sí | |
notice.published_at | string (date-time) | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Acuses de recibo del tenant (asiento de supervisor/admin)
http
GET /v1/screen-monitor/acksLos acuses de recibo del tenant, los más recientes primero, paginados por cursor. Permitido solo a un asiento cuyo rol de plantilla sea supervisor o admin (403 role_forbidden en caso contrario; una clave dd_ y un ddu_ sin asiento son 403). El rol se evalúa siempre; no depende de supervision_role_enforced.
NO consulta screen_monitor_enabled: es el registro de lo que se aceptó, y apagar la función ni lo borra ni lo oculta. Nada cruza entre tenants.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
limit | query | integer | no | Tamaño de página (un valor por encima del techo se recorta, no se rechaza). |
cursor | query | string | no | Cursor keyset opaco: el next_cursor de la página anterior. |
agent_id | query | string (uuid) | no | Solo los acuses de recibo de este asiento. |
Respuestas
200
Una página de acuses de recibo, los más recientes primero.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
acks | arreglo de ScreenMonitorAck | sí | |
acks[].id | string (uuid) | sí | |
acks[].agent_id | string (uuid) | sí | |
acks[].notice_version | integer | sí | |
acks[].scope | live_monitoring · screen_recording | sí | El alcance aceptado; una fila por (asiento, versión del aviso, alcance). |
acks[].text_sha256 | string | sí | El hash del texto que vio el asiento (copiado de la versión del aviso). |
acks[].channel | dashboard_checkin | sí | |
acks[].acked_at | string (date-time) | sí | |
next_cursor | string | null | sí | Cursor de la página siguiente; null en la última. |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}Acusar recibo del aviso vigente de monitoreo de pantalla desde el asiento PROPIO
http
POST /v1/screen-monitor/acksRegistra que el asiento PROPIO de quien llama acepta notice_version, que debe ser la versión VIGENTE, PARA UN scope: live_monitoring o screen_recording. Los alcances se acusan por separado: aceptar el monitoreo en vivo no autoriza la grabación, y una nueva versión del aviso invalida ambos. Idempotente: un segundo acuse de la misma versión y alcance devuelve el primero (200, sin fila nueva). El libro es de solo anexar (DELETE revocado y un trigger rechaza UPDATE/DELETE).
El asiento se deriva del bearer, nunca del cuerpo. Solo un inicio de sesión ddu_ con un asiento vinculado puede acusar recibo: una clave de máquina dd_ no tiene ninguna persona detrás (403 forbidden), un ddu_ sin asiento activo es 403 seat_not_linked, y un agent_id en el cuerpo que no es el asiento propio de quien llama es 403 forbidden: un tercero nunca acusa recibo en nombre de otro. Un token de widget ddw_ es 403 antes del handler.
404 not_found mientras el indicador screen_monitor_enabled del tenant esté apagado. Con el indicador encendido, 409 no_notice_published cuando no existe ninguna versión y 409 stale_notice cuando notice_version no es la vigente.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
notice_version | integer | sí | La versión que se acepta; debe ser la vigente. |
scope | live_monitoring · screen_recording | sí | Lo que el asiento acepta con este acuse de recibo. |
agent_id | string (uuid) | no | Opcional. Si está presente, debe ser el asiento propio de quien llama; cualquier otro es 403. |
Respuestas
200
El acuse de recibo (el existente cuando se repite).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
ack | object (ScreenMonitorAck) | sí | Un acuse de recibo de una versión del aviso por un asiento. De solo anexar. |
ack.id | string (uuid) | sí | |
ack.agent_id | string (uuid) | sí | |
ack.notice_version | integer | sí | |
ack.scope | live_monitoring · screen_recording | sí | El alcance aceptado; una fila por (asiento, versión del aviso, alcance). |
ack.text_sha256 | string | sí | El hash del texto que vio el asiento (copiado de la versión del aviso). |
ack.channel | dashboard_checkin | sí | |
ack.acked_at | string (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 (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}404 — NotFound
Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).
json
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}409
no_notice_published: aún no existe ninguna versión del aviso, así que nadie puede acusar recibo. stale_notice: notice_version no es la vigente.
json
{
"error": {
"code": "stale_notice",
"message": "that notice version is not the current one"
}
}El aviso vigente de grabación y supervisión que acepta un agente
http
GET /v1/agent-monitoring-noticeLos textos (uno por idioma) de la versión VIGENTE del aviso que un agente acepta en el check-in (core#2323, MT-CLI-37; ADR #289). Donde ambas partes deben consentir, la aceptación por agente y fecha es la prueba que exige la ley. El aviso es un catálogo global (sin tenant) y de solo anexar: publicar una version más nueva invalida, sin tocarlas, todas las aceptaciones de las anteriores. Core NO publica ningún texto legal propio: el catálogo nace vacío y lo llena quien migra, con la referencia de la aprobación del texto.
404 not_found mientras no haya ninguna versión en vigor. Alcanzable con una clave dd_ y con un inicio de sesión ddu_; un token de widget ddw_ es 403 antes del handler.
Respuestas
200
La versión vigente y sus textos.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
notice | object (AgentMonitoringNotice) | sí | La versión VIGENTE del aviso de grabación y supervisión, un texto por idioma (GET /v1/agent-monitoring-notice). Global, de solo anexar. |
notice.version | integer | sí | |
notice.effective_at | string (date-time) | sí | |
notice.texts | arreglo de object | sí | |
notice.texts[].lang | string | sí | Idioma del texto, p. ej. es. |
notice.texts[].title | string | sí | |
notice.texts[].body | string | sí | |
notice.texts[].text_sha256 | string | sí | SHA-256 (hex) de body en UTF-8; una aceptación lo copia. |
notice.texts[].published_at | string (date-time) | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Aceptar el aviso vigente de grabación y supervisión desde el asiento PROPIO
http
POST /v1/agent-monitoring-acknowledgementsRegistra que el asiento PROPIO de quien llama acepta notice_version, que debe ser la versión VIGENTE, tal como se leyó en lang (un idioma en el que la versión vigente tiene un texto). La fila conserva la versión, el idioma, el hash del texto que vio el agente, la fecha y el origen (channel). Idempotente: una segunda aceptación de la misma versión devuelve la primera (200, sin fila nueva), incluso en otro idioma. El libro es de solo anexar: no se puede editar ni borrar (DELETE revocado y un trigger rechaza UPDATE/DELETE); una versión más nueva del aviso deja la aceptación antigua registrada y simplemente deja de cubrir al agente.
El asiento se deriva del bearer, nunca del cuerpo. Solo un inicio de sesión ddu_ con un asiento vinculado puede aceptar: una clave de máquina dd_ no tiene ninguna persona detrás (403 forbidden), un ddu_ sin asiento activo es 403 seat_not_linked, y un agent_id en el cuerpo que no es el asiento propio de quien llama es 403 forbidden. Un token de widget ddw_ es 403 antes del handler.
409 no_notice_published cuando no hay ninguna versión en vigor y 409 stale_notice cuando notice_version no es la vigente; 422 cuando la versión vigente no tiene texto en lang.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
notice_version | integer | sí | La versión que se acepta; debe ser la vigente. |
lang | string | sí | El idioma del texto que se mostró al agente, p. ej. es o en. |
agent_id | string (uuid) | no | Opcional. Si está presente, debe ser el asiento propio de quien llama; cualquier otro es 403. |
Respuestas
200
La aceptación (la existente cuando se repite).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
acknowledgement | object (AgentMonitoringAck) | sí | Una aceptación de una versión del aviso por un agente. De solo anexar. |
acknowledgement.id | string (uuid) | sí | |
acknowledgement.agent_id | string (uuid) | sí | |
acknowledgement.notice_version | integer | sí | |
acknowledgement.lang | string | sí | El idioma del texto que vio el agente. |
acknowledgement.text_sha256 | string | sí | El hash del texto que vio el agente (copiado de la versión del aviso). |
acknowledgement.channel | dashboard_checkin | sí | El origen de la aceptación. |
acknowledgement.acked_at | string (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 (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}409
no_notice_published: no hay ninguna versión del aviso en vigor, así que nadie puede aceptar. stale_notice: notice_version no es la vigente.
json
{
"error": {
"code": "stale_notice",
"message": "that notice version is not the current one"
}
}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)"
}
}Armar o desarmar la captura de pantalla que mantiene una pestaña del navegador del asiento PROPIO
http
PUT /v1/screen-monitor/armedEl dashboard del agente llama a esto una vez que mantiene una captura de pantalla completa retenida (no se envía nada a ningún lado hasta que un supervisor abre una sesión) y de nuevo, con armed: false, cuando la libera. Solo se puede observar un asiento armado. Armar requiere que el asiento haya hecho check-in (409 not_checked_in) y que tenga un acuse de recibo del aviso VIGENTE para live_monitoring (409 no_ack).
Una pestaña por asiento: armar con un tab_id distinto DESPLAZA a la anterior. A la pestaña anterior se le avisa con un frame screen.disarmed (reason: displaced) en el tópico privado del asiento, y una sesión en vivo alimentada por ella se cierra como ended / agent_stopped. Armar de nuevo con el MISMO tab_id no cambia nada. Desarmar con un tab_id que no mantiene el armado es una operación nula; la respuesta indica si el asiento sigue armado.
El estado armado vive en el proceso, no en el libro: un reinicio desarma a todos (cierre por defecto ante fallo). Un checkout también desarma el asiento (screen.disarmed, reason: checkout), y también lo hace perder su último socket.
Solo un inicio de sesión ddu_ con un asiento vinculado puede llamarla (clave dd_: 403; un token de widget ddw_ es 403 antes del handler). 404 not_found mientras el indicador screen_monitor_enabled del tenant esté apagado.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
armed | boolean | sí | |
tab_id | string | sí | Un id opaco que el dashboard asigna a la pestaña que mantiene la captura. |
Respuestas
200
El estado del asiento tras la llamada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
armed | boolean | sí | |
tab_id | string | no | Presente al armar; la pestaña que mantiene ahora el armado. |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}404 — NotFound
Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).
json
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}409
not_checked_in: el asiento no está de turno. no_ack: no hay acuse de recibo del aviso vigente para live_monitoring.
json
{
"error": {
"code": "no_ack",
"message": "acknowledge the current screen-monitor notice before arming"
}
}Listar el libro de sesiones de monitoreo de pantalla (asiento de supervisor o admin)
http
GET /v1/screen-monitor/sessionsLas sesiones de monitoreo del tenant, las más recientes primero, paginadas por cursor: las abiertas, las rechazadas y las que cerró el sistema. Permitido solo a un asiento cuyo rol de plantilla sea supervisor o admin (403 role_forbidden en caso contrario; una clave dd_ y un ddu_ sin asiento son 403). El rol se evalúa siempre; no depende de supervision_role_enforced.
NO consulta screen_monitor_enabled: es la evidencia de quién observó a quién, y apagar la función ni la borra ni la oculta. Nunca lleva payload de señalización, y nada cruza entre tenants.
Una fila started que sigue en vivo se cierra sola: cuando el agente o el supervisor pierde el socket, hace checkout, o el indicador, el rol o el acuse de recibo dejan de cumplirse, la fila pasa a ended con el motivo (flag_off, role, no_ack, checkout, agent_stopped, credential_lapsed o peer_failed).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
limit | query | integer | no | Tamaño de página (un valor por encima del techo se recorta, no se rechaza). |
cursor | query | string | no | Cursor keyset opaco: el next_cursor de la página anterior. |
agent_id | query | string (uuid) | no | Solo las sesiones sobre este asiento. |
result | query | started · ended · failed · refused | no | Solo las filas con este resultado. |
Respuestas
200
Una página de sesiones, las más recientes primero.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
sessions | arreglo de ScreenMonitorSession | sí | |
sessions[].id | string (uuid) | sí | |
sessions[].supervisor_user_id | string (uuid) | sí | El inicio de sesión que lo solicitó. |
sessions[].supervisor_id | string (uuid) | null | sí | El asiento de quien solicita; null cuando el inicio de sesión no tiene asiento. |
sessions[].agent_id | string (uuid) | null | sí | El asiento observado; null cuando no se resolvió para el tenant o el rol fue rechazado primero. |
sessions[].notice_version | integer | null | sí | La versión del aviso en vigor cuando se abrió la sesión, cuando el agente tenía una. |
sessions[].result | started · ended · failed · refused | sí | |
sessions[].reason | flag_off · role · agent_not_found · no_ack · not_armed · agent_busy · agent_stopped · credential_lapsed · checkout · peer_failed | null | sí | Por qué una sesión fue rechazada, falló o fue terminada por el sistema; null para un cierre normal o mientras está started. |
sessions[].started_at | string (date-time) | sí | |
sessions[].ended_at | string (date-time) | null | sí | |
next_cursor | string | null | sí | Cursor de la página siguiente; null en la última. |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}Solicitar ver la pantalla de un agente (asiento de supervisor o admin)
http
POST /v1/screen-monitor/sessionsAbre una sesión de monitoreo sobre un agente. Core no ve el vídeo: valida, escribe la fila del libro y avisa al agente (screen.view_requested en el tópico privado del agente); luego los navegadores negocian el par de medios de igual a igual mediante POST /v1/screen-monitor/sessions/{id}/signal.
EVIDENCIA PRIMERO: todo resultado escribe una fila de screen_monitor_sessions, incluso los rechazados. Las compuertas se ejecutan en este orden y la primera que falla es el reason de la fila: role (el asiento de quien llama debe ser supervisor o admin; se evalúa SIEMPRE, no depende de supervision_role_enforced; un inicio de sesión sin asiento también es role), luego agent_not_found, flag_off, no_ack (el agente no tiene acuse de recibo del aviso vigente para live_monitoring), not_armed y agent_busy (el agente ya tiene una sesión en vivo). Un id de otro tenant, un id que no existe y un agente que no está activo son 404 con el MISMO cuerpo y dejan la misma fila (agent_not_found, agent_id nulo): nada aquí indica si un agente existe en otro tenant. flag_off es el mismo 404 que el de cualquier otra operación de la función.
Si el agente no tiene ningún socket en vivo al que avisar, la sesión se escribe como failed / peer_failed y la respuesta es 409 agent_unreachable.
Solo un inicio de sesión ddu_ puede llamarla; una clave dd_ no tiene ninguna persona detrás (403 forbidden, sin fila). Un ddw_ es 403 antes del handler.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
agent_id | string (uuid) | sí | El asiento a observar. |
Respuestas
201
La sesión se abrió; la fila está started.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
session | object (ScreenMonitorSession) | sí | Una fila del libro de sesiones de monitoreo de pantalla (core#1990). De solo anexar; nunca lleva payload de señalización. |
session.id | string (uuid) | sí | |
session.supervisor_user_id | string (uuid) | sí | El inicio de sesión que lo solicitó. |
session.supervisor_id | string (uuid) | null | sí | El asiento de quien solicita; null cuando el inicio de sesión no tiene asiento. |
session.agent_id | string (uuid) | null | sí | El asiento observado; null cuando no se resolvió para el tenant o el rol fue rechazado primero. |
session.notice_version | integer | null | sí | La versión del aviso en vigor cuando se abrió la sesión, cuando el agente tenía una. |
session.result | started · ended · failed · refused | sí | |
session.reason | flag_off · role · agent_not_found · no_ack · not_armed · agent_busy · agent_stopped · credential_lapsed · checkout · peer_failed | null | sí | Por qué una sesión fue rechazada, falló o fue terminada por el sistema; null para un cierre normal o mientras está started. |
session.started_at | string (date-time) | sí | |
session.ended_at | string (date-time) | null | 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
role_forbidden: el asiento de quien llama no es supervisor ni admin (o el inicio de sesión no tiene asiento). forbidden: una clave dd_.
json
{
"error": {
"code": "role_forbidden",
"message": "watching a screen takes a supervisor or admin seat"
}
}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
no_ack, not_armed, agent_busy o agent_unreachable (véase más arriba).
json
{
"error": {
"code": "not_armed",
"message": "the agent is not sharing their screen"
}
}Enviar un mensaje de señalización WebRTC a la otra parte de una sesión en vivo
http
POST /v1/screen-monitor/sessions/{id}/signalReenvía payload a la OTRA parte de la sesión como un frame privado (screen.offer, screen.answer o screen.candidate) y lo olvida: el payload lleva direcciones de red y NUNCA se almacena. Quien llama debe ser una de las dos partes de una sesión started; offer solo puede venir del agente y answer solo del supervisor (403), candidate de cualquiera de los dos.
Quien llama sin ser parte, un id que no existe en el tenant y un id que no es un UUID responden el MISMO 404. Una sesión cerrada es 409 session_closed.
Acotado: el JSON de payload es de como máximo 16384 bytes (413 payload_too_large) y una sesión acepta como máximo 60 mensajes cada 10 segundos (429 rate_limited con Retry-After). 202 indica que el mensaje se entregó al destino; delivered es false cuando la otra parte no tenía ningún socket en vivo.
404 not_found mientras el indicador screen_monitor_enabled del tenant esté apagado.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí |
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
kind | offer · answer · candidate | sí | |
payload | object | sí | El objeto SDP o candidato ICE, pasado sin modificar. |
Respuestas
202
Entregado al tópico privado de la otra parte.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
delivered | boolean | sí |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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
session_closed: la sesión ya no está started.
json
{
"error": {
"code": "session_closed",
"message": "that session is already closed"
}
}413
payload_too_large: el JSON de payload supera los 16384 bytes.
json
{
"error": {
"code": "payload_too_large",
"message": "payload is over 16384 bytes"
}
}429
rate_limited: más de 60 mensajes en 10 segundos para esta sesión.
json
{
"error": {
"code": "rate_limited",
"message": "too many signalling messages for this session"
}
}Credencial TURN efímera para UNA sesión de monitoreo (cualquiera de las dos partes)
http
GET /v1/screen-monitor/sessions/{id}/iceEl relay que un navegador necesita para transportar el vídeo de pantalla cuando un NAT simétrico bloquea la ruta directa. Solo lo obtienen las dos partes de una sesión que sigue started, con el indicador screen_monitor_enabled del tenant encendido. Quien llama sin ser parte, un id que no existe en el tenant y un id que no es un UUID responden el MISMO 404.
La credencial es el coturn propio de la celda con su esquema use-auth-secret, el mismo secreto compartido que el del softphone (sin secreto ni variable nuevos): username es <unix expiry>:scr.<session_id>.<agent|viewer> (un :), credential es Base64(HMAC-SHA1(secret, username)), y caduca una hora después de la llamada (expires_at). La forma es la del RTCIceServer de WebRTC, para añadirla tal cual a iceServers. La respuesta lleva Cache-Control: no-store; el secreto compartido nunca forma parte de ninguna respuesta ni log.
Cierre por defecto ante fallo: una celda sin TURN configurado responde 503 turn_unavailable, nunca una lista ICE que parezca válida y no lleve relay.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí |
Respuestas
200
La credencial de relay para el lado de la sesión de quien llama.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
ice_server | object | sí | |
ice_server.urls | arreglo de string | sí | URLs turn: / turns: del relay de la celda. |
ice_server.username | string | sí | <unix expiry>:scr.<session_id>.<agent|viewer> |
ice_server.credential | string | sí | Base64 de HMAC-SHA1(secreto compartido, username). |
expires_at | string (date-time) | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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
session_closed: la sesión ya no está started.
json
{
"error": {
"code": "session_closed",
"message": "that session is already closed"
}
}503
turn_unavailable: esta celda no tiene ningún relay TURN configurado.
json
{
"error": {
"code": "turn_unavailable",
"message": "this cell has no TURN relay configured for screen monitoring"
}
}Cerrar una sesión de monitoreo (cualquiera de las dos partes)
http
POST /v1/screen-monitor/sessions/{id}/endCierra una sesión started. Sin reason es un cierre normal: ended, con reason agent_stopped cuando lo pide el agente y sin motivo cuando lo pide el supervisor. reason: peer_failed informa de que la conexión de medios nunca se estableció: la fila pasa a failed. La otra parte recibe un frame screen.ended en su tópico privado.
Idempotente: cerrar una sesión que ya está cerrada devuelve la fila tal como está y no escribe nada. Quien llama sin ser parte, un id que no existe en el tenant y un id que no es un UUID responden el MISMO 404.
NO consulta el indicador del tenant: una sesión siempre se puede cerrar.
El sistema también cierra por sí mismo una sesión en vivo y avisa a ambas partes con screen.ended (result, reason): en el checkout del agente (checkout), cuando se pierde el último socket de una parte (agent_stopped para el asiento observado, peer_failed para el supervisor), cuando caduca la credencial de un socket (credential_lapsed), y en el heartbeat de un socket cuando el indicador, el rol del supervisor o el acuse de recibo del agente dejaron de cumplirse (flag_off, role, no_ack).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí |
Cuerpo de la petición (JSON)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
reason | peer_failed | no |
Respuestas
200
La sesión tras la llamada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
session | object (ScreenMonitorSession) | sí | Una fila del libro de sesiones de monitoreo de pantalla (core#1990). De solo anexar; nunca lleva payload de señalización. |
session.id | string (uuid) | sí | |
session.supervisor_user_id | string (uuid) | sí | El inicio de sesión que lo solicitó. |
session.supervisor_id | string (uuid) | null | sí | El asiento de quien solicita; null cuando el inicio de sesión no tiene asiento. |
session.agent_id | string (uuid) | null | sí | El asiento observado; null cuando no se resolvió para el tenant o el rol fue rechazado primero. |
session.notice_version | integer | null | sí | La versión del aviso en vigor cuando se abrió la sesión, cuando el agente tenía una. |
session.result | started · ended · failed · refused | sí | |
session.reason | flag_off · role · agent_not_found · no_ack · not_armed · agent_busy · agent_stopped · credential_lapsed · checkout · peer_failed | null | sí | Por qué una sesión fue rechazada, falló o fue terminada por el sistema; null para un cierre normal o mientras está started. |
session.started_at | string (date-time) | sí | |
session.ended_at | string (date-time) | null | sí |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}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"
}
}Ajustes de la cuenta de autoservicio
http
PATCH /v1/tenantSolo name es editable por el tenant. Los campos que afectan a la facturación (tier, voice_ai_enabled, seats, topes de gasto) viven en la superficie de admin (PATCH /v1/admin/tenants/{id}): dejar que un tenant los edite con su propio bearer es un vector directo de pago insuficiente. Las demás claves del cuerpo se ignoran en silencio.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | no |
json
{
"name": "Acme Collections LLC"
}Respuestas
200
Tenant actualizado.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenant | object (Tenant) | sí | |
tenant.id | string (uuid) | no | |
tenant.name | string | no | |
tenant.account_id | string (uuid) | no | La EMPRESA a la que pertenece este tenant (sede) (DA-2). |
tenant.billing_account_id | string (uuid) | no | La cuenta de facturación bajo la que se factura la sede (DA-3). Compartida por todas las sedes de una empresa consolidated; una por sede cuando es per_site. |
tenant.status | active · suspended | no | |
tenant.synthetic | boolean | no | true para un tenant de demostración. No origina tráfico: la marcación manual, el envío de SMS, la pierna externa de una transferencia y el sandbox responden 403 synthetic_tenant. Siempre presente, nunca nulo, para que una consola pueda avisar ANTES de que la acción falle (core#2512). |
tenant.retention_months | integer | no | |
tenant.seats | integer | no | |
tenant.tier | starter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shield | no | |
tenant.voice_ai_enabled | boolean | no | |
tenant.ai_monthly_budget_usd | number | null | no | Techo de integridad de costes (lo fija el admin; null = sin tope). |
tenant.ai_max_call_seconds | integer | null | no | Techo estricto de duración por llamada de IA (segundos) fijado por el admin. null significa que se aplica el valor por defecto de la PLATAFORMA, nunca "sin techo" (core#922, ADR #115). |
tenant.ai_max_call_seconds_effective | integer | no | El techo que realmente se entrega a FreeSWITCH para las piernas de IA de este tenant: ai_max_call_seconds cuando está definido; si no, el valor por defecto de plataforma de la celda (AI_MAX_CALL_SECONDS_DEFAULT, valor de reserva compilado 900). Siempre presente, nunca nulo. Solo lectura (derivado). |
tenant.sms_monthly_budget_usd | number | null | no | Tope de gasto en SMS acumulado en el mes hasta la fecha. |
tenant.human_initiated_enabled | boolean | no | Postura de consentimiento D17 de iniciativa humana (la fija el admin; por defecto false). Cuando es true, una marcación de voz EN VIVO de vista previa/manual/CTI sin voz artificial y sin voz de IA no requiere consentimiento expreso previo (sin ATDS). Todas las demás compuertas (DNC, horas de silencio, Reg F, matriz estatal, voz artificial) siguen aplicándose sin cambios. |
tenant.max_cps | integer | null | no | Techo de CPS por tenant (lo fija el admin; null = sin techo). ADR #12. |
tenant.max_concurrent_channels | integer | null | no | Techo de llamadas concurrentes por tenant, una unidad por llamada con o sin agente (lo fija el admin; null = sin techo). ADR #12, ADR #185. |
tenant.abandon_seller_name | string | null | no | Nombre del vendedor para el mensaje de identificación de llamada abandonada de la FTC TSR (lo fija el admin; null = no configurado, el sobremarcado predictivo permanece apagado). ADR #14. |
tenant.abandon_seller_phone | string | null | no | Número de rellamada E.164 para el mensaje de identificación de llamada abandonada (lo fija el admin; null = no configurado). ADR #14. |
tenant.tax_country | string | null | no | País ISO-3166-1 alpha-2 de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_region | string | null | no | Código de estado/provincia de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_postal_code | string | null | no | Código postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.kyc_verified_at | string (date-time) | null | no | Cuándo verificó la plataforma a este cliente (lo fija el admin; null = aún no). Una cuenta de 1 asiento que no está verificada no puede crear ni iniciar una campaña que se ejecute sin agente (403 verification_required, ADR #279). |
tenant.created_at | string (date-time) | no | |
tenant.updated_at | string (date-time) | no |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}El horario de llamadas propio del tenant
http
GET /v1/tenant/calling-windowLa ventana de llamadas de todo el tenant, o null cuando el tenant no tiene ninguna (la ruta de marcación es entonces la que era antes de que existiera esta clave). Solo RESTRINGE: una llamada sale cuando la ventana legal (R18, por destino), la ventana propia de la campaña Y esta están todas abiertas. Legible con una clave dd_ y con cualquier inicio de sesión de dashboard.
Respuestas
200
La ventana almacenada, o null.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
calling_window | TenantCallingWindow | null | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}503
No se pudo leer la ventana almacenada (nunca se responde como "sin ventana").
Establecer el horario de llamadas del tenant
http
PUT /v1/tenant/calling-windowReemplaza la ventana del tenant. Solo gestión del tenant: una clave dd_ o un inicio de sesión account_admin; un inicio de sesión member recibe 403 role_forbidden. El cuerpo se valida antes de almacenar nada: una ventana mal formada es 422 con el motivo y la ventana anterior se mantiene. El valor almacenado es el normalizado (HH:MM pasa a HH:MM:SS). La escritura queda auditada.
Un valor almacenado que ya no se puede evaluar RECHAZA la marcación (rule_id tenant.calling_window_unavailable.v1): solo la ausencia de una ventana abre.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
timezone | string | sí | Un nombre de zona IANA. Una zona que no se resuelve es 422. |
weekly | object (CampaignWeeklyWindow) | sí | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
weekly.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.mon.start | string | sí | |
weekly.mon.end | string | sí | |
weekly.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.tue.start | string | sí | |
weekly.tue.end | string | sí | |
weekly.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.wed.start | string | sí | |
weekly.wed.end | string | sí | |
weekly.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.thu.start | string | sí | |
weekly.thu.end | string | sí | |
weekly.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.fri.start | string | sí | |
weekly.fri.end | string | sí | |
weekly.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.sat.start | string | sí | |
weekly.sat.end | string | sí | |
weekly.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
weekly.sun.start | string | sí | |
weekly.sun.end | string | sí |
json
{
"timezone": "America/Chicago",
"weekly": {
"mon": {
"start": "09:00",
"end": "20:00"
},
"tue": {
"start": "09:00",
"end": "20:00"
}
}
}Respuestas
200
Almacenada (normalizada).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
calling_window | object (TenantCallingWindow) | sí | El horario de llamadas propio del tenant (core#1527, clave R19 tenant_calling_window). Solo RESTRINGE: R18 (por destino) y la ventana de la campaña siguen aplicándose. Las horas se leen en timezone (la del tenant, NO la del destino). |
calling_window.timezone | string | sí | Un nombre de zona IANA. Una zona que no se resuelve es 422. |
calling_window.weekly | object (CampaignWeeklyWindow) | sí | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
calling_window.weekly.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.mon.start | string | sí | |
calling_window.weekly.mon.end | string | sí | |
calling_window.weekly.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.tue.start | string | sí | |
calling_window.weekly.tue.end | string | sí | |
calling_window.weekly.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.wed.start | string | sí | |
calling_window.weekly.wed.end | string | sí | |
calling_window.weekly.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.thu.start | string | sí | |
calling_window.weekly.thu.end | string | sí | |
calling_window.weekly.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.fri.start | string | sí | |
calling_window.weekly.fri.end | string | sí | |
calling_window.weekly.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.sat.start | string | sí | |
calling_window.weekly.sat.end | string | sí | |
calling_window.weekly.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
calling_window.weekly.sun.start | string | sí | |
calling_window.weekly.sun.end | string | 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
role_forbidden: un inicio de sesión de dashboard sin el rol account_admin; forbidden: un token de widget o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "a dashboard login needs the account_admin role to change the tenant calling window"
}
}422
Ventana mal formada; el mensaje nombra el motivo. No se almacena nada.
429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}Borrar el horario de llamadas del tenant
http
DELETE /v1/tenant/calling-windowVuelve a no tener ventana de tenant (idempotente). Los mismos roles que PUT. Las ventanas de la campaña y de R18 no se tocan.
Respuestas
200
Borrada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
calling_window | null | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (core#2406, indicador widget_login_honors_policy). Un tenant sintético (demo) responde 403 con el código synthetic_tenant en toda ruta que llegaría a un tercero — marcación manual, pierna de transferencia externa o de conferencia, envío de SMS (core#2251, adenda del ADR #280). Ningún pago lo cambia.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}La política de inicio de sesión del tenant
http
GET /v1/tenant/login-policyLa política de inicio de sesión vigente (ADR 227, core#1760), o null cuando nunca se ha escrito ninguna (el inicio de sesión es entonces el que era antes de que existiera la política). Es la lista de permitidos de direcciones de cliente y las horas por rol que el inicio de sesión del dashboard (ddu_) comprueba DESPUÉS de probar la credencial. client_address_measured indica si esta celda mide la dirección del cliente: cuando es false no se puede armar un ip_allow aquí (el PUT responde 409 client_address_not_measured) y solo se puede armar windows. Legible con una clave dd_ y con un inicio de sesión account_admin; un inicio de sesión member recibe 403 role_forbidden.
Respuestas
200
La versión vigente, o null.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
login_policy | TenantLoginPolicyVersion | null | sí | |
client_address_measured | boolean | 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
role_forbidden: un inicio de sesión de dashboard sin el rol account_admin; forbidden: un token de widget o un tenant suspendido.
429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}503
No se pudo leer la política (nunca se responde como "sin política").
Publicar una nueva versión de la política de inicio de sesión del tenant
http
PUT /v1/tenant/login-policyAnexa una versión (nada se edita en el sitio; véase .../history). Solo escribe una sesión ddu_ de un account_admin: una clave dd_ es 403 role_forbidden, porque una clave robada que apague el control lo convertiría en decorado.
mode es por defecto observe (el inicio de sesión nunca se deniega; cada intento que habría sido denegado se registra para que el admin vea quién quedaría fuera ANTES de armar enforce). Se rechaza, y no se almacena nada:
422 unprocessable: cuerpo mal formado, un CIDR que no se interpreta, un/0, más de 64 entradas, un rol desconocido, una ventana que no se interpreta o untimezoneausente, oenforcesinip_allowniwindows.409 client_address_not_measured: unip_allowenobserveoenforceen una celda cuya dirección de cliente no se mide.422 would_lock_out_caller: con unip_allow, la dirección propia de quien escribe no se puede atribuir; conenforce, la dirección, el rol y la hora de la petición de quien escribe deben cumplir la política que se está escribiendo.
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
mode | off · observe · enforce | no | off también es una versión (apagar es escribir, no borrar). observe nunca deniega. enforce deniega con 403 login_policy_denied. |
ip_allow | arreglo de string | no | Bloques CIDR o direcciones sueltas, IPv4 e IPv6; las familias nunca se cruzan. Vacío = sin restricción por dirección. Una entrada mal formada o un /0 es 422. |
windows | object | no | Horas de inicio de sesión por rol, con la forma de la ventana semanal de una campaña (un día sin entrada está cerrado, una ventana nunca cruza la medianoche). Un rol sin entrada no tiene restricción de horas. |
windows.member | object (CampaignWeeklyWindow) | no | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
windows.member.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.mon.start | string | sí | |
windows.member.mon.end | string | sí | |
windows.member.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.tue.start | string | sí | |
windows.member.tue.end | string | sí | |
windows.member.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.wed.start | string | sí | |
windows.member.wed.end | string | sí | |
windows.member.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.thu.start | string | sí | |
windows.member.thu.end | string | sí | |
windows.member.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.fri.start | string | sí | |
windows.member.fri.end | string | sí | |
windows.member.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.sat.start | string | sí | |
windows.member.sat.end | string | sí | |
windows.member.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.member.sun.start | string | sí | |
windows.member.sun.end | string | sí | |
windows.account_admin | object (CampaignWeeklyWindow) | no | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
windows.account_admin.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.mon.start | string | sí | |
windows.account_admin.mon.end | string | sí | |
windows.account_admin.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.tue.start | string | sí | |
windows.account_admin.tue.end | string | sí | |
windows.account_admin.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.wed.start | string | sí | |
windows.account_admin.wed.end | string | sí | |
windows.account_admin.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.thu.start | string | sí | |
windows.account_admin.thu.end | string | sí | |
windows.account_admin.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.fri.start | string | sí | |
windows.account_admin.fri.end | string | sí | |
windows.account_admin.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.sat.start | string | sí | |
windows.account_admin.sat.end | string | sí | |
windows.account_admin.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
windows.account_admin.sun.start | string | sí | |
windows.account_admin.sun.end | string | sí | |
timezone | string,null | no | Una zona IANA; obligatoria cuando windows no está vacío, rechazada en caso contrario. |
reason | string,null | no |
json
{
"mode": "observe",
"ip_allow": [
"203.0.113.0/24"
],
"windows": {
"member": {
"mon": {
"start": "08:00",
"end": "18:00"
},
"tue": {
"start": "08:00",
"end": "18:00"
}
}
},
"timezone": "America/Chicago",
"reason": "Office hours only"
}Respuestas
200
La versión que se almacenó.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
login_policy | object (TenantLoginPolicyVersion) | sí | Una versión publicada (de solo anexar) de la política de inicio de sesión del tenant. |
login_policy.version | integer | sí | |
login_policy.mode | off · observe · enforce | sí | |
login_policy.ip_allow | arreglo de string | sí | |
login_policy.windows | object | sí | |
login_policy.windows.member | object (CampaignWeeklyWindow) | no | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
login_policy.windows.member.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.member.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.member.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.member.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.member.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.member.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.member.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin | object (CampaignWeeklyWindow) | no | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
login_policy.windows.account_admin.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.windows.account_admin.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
login_policy.timezone | string,null | sí | |
login_policy.set_by | string | sí | |
login_policy.reason | string,null | sí | |
login_policy.inserted_at | string (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
role_forbidden: una clave dd_ o un inicio de sesión de dashboard sin el rol account_admin; forbidden: un token de widget o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "writing the login policy takes a dashboard login with the account_admin role"
}
}409
client_address_not_measured: esta celda no mide la dirección del cliente, así que un ip_allow compararía contra el proxy. No se almacena nada.
422
unprocessable (el mensaje nombra el motivo) o would_lock_out_caller (esta política dejaría fuera a quien escribe). No se almacena nada.
429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}Todas las versiones publicadas de la política de inicio de sesión del tenant
http
GET /v1/tenant/login-policy/historyLas más recientes primero. set_by es user:<id> para una versión que escribió el tenant y operator:<id> para una recuperación por el personal de la plataforma. Los mismos lectores que GET /v1/tenant/login-policy.
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
limit | query | integer | no | Tamaño de página (un valor por encima del techo se recorta, no se rechaza). |
cursor | query | integer | no | El next_cursor de la página anterior. |
Respuestas
200
Una página de versiones.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
versions | arreglo de TenantLoginPolicyVersion | sí | |
versions[].version | integer | sí | |
versions[].mode | off · observe · enforce | sí | |
versions[].ip_allow | arreglo de string | sí | |
versions[].windows | object | sí | |
versions[].windows.member | object (CampaignWeeklyWindow) | no | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
versions[].windows.member.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.member.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.member.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.member.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.member.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.member.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.member.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin | object (CampaignWeeklyWindow) | no | Ventanas por día de la semana con claves mon…sun; al menos un día. Un día que no es una clave no tiene ventana y está cerrado. |
versions[].windows.account_admin.mon | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin.tue | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin.wed | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin.thu | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin.fri | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin.sat | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].windows.account_admin.sun | object (CampaignDayWindow) | no | La ventana de un día de la semana, dentro del día: start estrictamente antes de end. Se acepta HH:MM y se almacena como HH:MM:SS. Sin desfase: la ventana se lee en la zona en la que la campaña lee la hora. |
versions[].timezone | string,null | sí | |
versions[].set_by | string | sí | |
versions[].reason | string,null | sí | |
versions[].inserted_at | string (date-time) | sí | |
next_cursor | integer,null | sí | Pásalo como cursor para la página siguiente; null en la última. |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403
role_forbidden: un inicio de sesión de dashboard sin el rol account_admin; forbidden: un token de widget o un tenant suspendido.
429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}503
No se pudo leer el historial.
Listar tenants (siempre un solo elemento)
http
GET /v1/tenantsParidad con la documentación: las claves de API están acotadas al tenant, así que la lista contiene exactamente el tenant autenticado.
Respuestas
200
Lista de un elemento (tenant propio).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenants | arreglo de Tenant | sí | |
tenants[].id | string (uuid) | no | |
tenants[].name | string | no | |
tenants[].account_id | string (uuid) | no | La EMPRESA a la que pertenece este tenant (sede) (DA-2). |
tenants[].billing_account_id | string (uuid) | no | La cuenta de facturación bajo la que se factura la sede (DA-3). Compartida por todas las sedes de una empresa consolidated; una por sede cuando es per_site. |
tenants[].status | active · suspended | no | |
tenants[].synthetic | boolean | no | true para un tenant de demostración. No origina tráfico: la marcación manual, el envío de SMS, la pierna externa de una transferencia y el sandbox responden 403 synthetic_tenant. Siempre presente, nunca nulo, para que una consola pueda avisar ANTES de que la acción falle (core#2512). |
tenants[].retention_months | integer | no | |
tenants[].seats | integer | no | |
tenants[].tier | starter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shield | no | |
tenants[].voice_ai_enabled | boolean | no | |
tenants[].ai_monthly_budget_usd | number | null | no | Techo de integridad de costes (lo fija el admin; null = sin tope). |
tenants[].ai_max_call_seconds | integer | null | no | Techo estricto de duración por llamada de IA (segundos) fijado por el admin. null significa que se aplica el valor por defecto de la PLATAFORMA, nunca "sin techo" (core#922, ADR #115). |
tenants[].ai_max_call_seconds_effective | integer | no | El techo que realmente se entrega a FreeSWITCH para las piernas de IA de este tenant: ai_max_call_seconds cuando está definido; si no, el valor por defecto de plataforma de la celda (AI_MAX_CALL_SECONDS_DEFAULT, valor de reserva compilado 900). Siempre presente, nunca nulo. Solo lectura (derivado). |
tenants[].sms_monthly_budget_usd | number | null | no | Tope de gasto en SMS acumulado en el mes hasta la fecha. |
tenants[].human_initiated_enabled | boolean | no | Postura de consentimiento D17 de iniciativa humana (la fija el admin; por defecto false). Cuando es true, una marcación de voz EN VIVO de vista previa/manual/CTI sin voz artificial y sin voz de IA no requiere consentimiento expreso previo (sin ATDS). Todas las demás compuertas (DNC, horas de silencio, Reg F, matriz estatal, voz artificial) siguen aplicándose sin cambios. |
tenants[].max_cps | integer | null | no | Techo de CPS por tenant (lo fija el admin; null = sin techo). ADR #12. |
tenants[].max_concurrent_channels | integer | null | no | Techo de llamadas concurrentes por tenant, una unidad por llamada con o sin agente (lo fija el admin; null = sin techo). ADR #12, ADR #185. |
tenants[].abandon_seller_name | string | null | no | Nombre del vendedor para el mensaje de identificación de llamada abandonada de la FTC TSR (lo fija el admin; null = no configurado, el sobremarcado predictivo permanece apagado). ADR #14. |
tenants[].abandon_seller_phone | string | null | no | Número de rellamada E.164 para el mensaje de identificación de llamada abandonada (lo fija el admin; null = no configurado). ADR #14. |
tenants[].tax_country | string | null | no | País ISO-3166-1 alpha-2 de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenants[].tax_region | string | null | no | Código de estado/provincia de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenants[].tax_postal_code | string | null | no | Código postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenants[].kyc_verified_at | string (date-time) | null | no | Cuándo verificó la plataforma a este cliente (lo fija el admin; null = aún no). Una cuenta de 1 asiento que no está verificada no puede crear ni iniciar una campaña que se ejecute sin agente (403 verification_required, ADR #279). |
tenants[].created_at | string (date-time) | no | |
tenants[].updated_at | string (date-time) | no |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}Obtener el tenant propio por id
http
GET /v1/tenants/{id}Id del tenant propio → el tenant. CUALQUIER otro id → 404 (nunca un 403; sin fuga de existencia).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string (uuid) | sí | UUID del recurso. Los ids mal formados o de otro tenant se leen como 404. |
Respuestas
200
El tenant.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenant | object (Tenant) | sí | |
tenant.id | string (uuid) | no | |
tenant.name | string | no | |
tenant.account_id | string (uuid) | no | La EMPRESA a la que pertenece este tenant (sede) (DA-2). |
tenant.billing_account_id | string (uuid) | no | La cuenta de facturación bajo la que se factura la sede (DA-3). Compartida por todas las sedes de una empresa consolidated; una por sede cuando es per_site. |
tenant.status | active · suspended | no | |
tenant.synthetic | boolean | no | true para un tenant de demostración. No origina tráfico: la marcación manual, el envío de SMS, la pierna externa de una transferencia y el sandbox responden 403 synthetic_tenant. Siempre presente, nunca nulo, para que una consola pueda avisar ANTES de que la acción falle (core#2512). |
tenant.retention_months | integer | no | |
tenant.seats | integer | no | |
tenant.tier | starter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shield | no | |
tenant.voice_ai_enabled | boolean | no | |
tenant.ai_monthly_budget_usd | number | null | no | Techo de integridad de costes (lo fija el admin; null = sin tope). |
tenant.ai_max_call_seconds | integer | null | no | Techo estricto de duración por llamada de IA (segundos) fijado por el admin. null significa que se aplica el valor por defecto de la PLATAFORMA, nunca "sin techo" (core#922, ADR #115). |
tenant.ai_max_call_seconds_effective | integer | no | El techo que realmente se entrega a FreeSWITCH para las piernas de IA de este tenant: ai_max_call_seconds cuando está definido; si no, el valor por defecto de plataforma de la celda (AI_MAX_CALL_SECONDS_DEFAULT, valor de reserva compilado 900). Siempre presente, nunca nulo. Solo lectura (derivado). |
tenant.sms_monthly_budget_usd | number | null | no | Tope de gasto en SMS acumulado en el mes hasta la fecha. |
tenant.human_initiated_enabled | boolean | no | Postura de consentimiento D17 de iniciativa humana (la fija el admin; por defecto false). Cuando es true, una marcación de voz EN VIVO de vista previa/manual/CTI sin voz artificial y sin voz de IA no requiere consentimiento expreso previo (sin ATDS). Todas las demás compuertas (DNC, horas de silencio, Reg F, matriz estatal, voz artificial) siguen aplicándose sin cambios. |
tenant.max_cps | integer | null | no | Techo de CPS por tenant (lo fija el admin; null = sin techo). ADR #12. |
tenant.max_concurrent_channels | integer | null | no | Techo de llamadas concurrentes por tenant, una unidad por llamada con o sin agente (lo fija el admin; null = sin techo). ADR #12, ADR #185. |
tenant.abandon_seller_name | string | null | no | Nombre del vendedor para el mensaje de identificación de llamada abandonada de la FTC TSR (lo fija el admin; null = no configurado, el sobremarcado predictivo permanece apagado). ADR #14. |
tenant.abandon_seller_phone | string | null | no | Número de rellamada E.164 para el mensaje de identificación de llamada abandonada (lo fija el admin; null = no configurado). ADR #14. |
tenant.tax_country | string | null | no | País ISO-3166-1 alpha-2 de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_region | string | null | no | Código de estado/provincia de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_postal_code | string | null | no | Código postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.kyc_verified_at | string (date-time) | null | no | Cuándo verificó la plataforma a este cliente (lo fija el admin; null = aún no). Una cuenta de 1 asiento que no está verificada no puede crear ni iniciar una campaña que se ejecute sin agente (403 verification_required, ADR #279). |
tenant.created_at | string (date-time) | no | |
tenant.updated_at | string (date-time) | no |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}404 — NotFound
Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).
json
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}