Skip to content

Identidad ​

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

English version

Traducción en curso

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

Tenant autenticado, ajustes de la cuenta.

Endpoints ​

MétodoRutaResumen
GET/v1/meTenant autenticado, usuario y ASIENTO derivado
GET/v1/me/scorecardEl cuadro de mando del turno PROPIO de quien llama (asiento derivado del token)
GET/v1/screen-monitor/noticeEl aviso vigente de monitoreo de pantalla
GET/v1/screen-monitor/acksAcuses de recibo del tenant (asiento de supervisor/admin)
POST/v1/screen-monitor/acksAcusar recibo del aviso vigente de monitoreo de pantalla desde el asiento PROPIO
GET/v1/agent-monitoring-noticeEl aviso vigente de grabación y supervisión que acepta un agente
POST/v1/agent-monitoring-acknowledgementsAceptar el aviso vigente de grabación y supervisión desde el asiento PROPIO
PUT/v1/screen-monitor/armedArmar o desarmar la captura de pantalla que mantiene una pestaña del navegador del asiento PROPIO
GET/v1/screen-monitor/sessionsListar el libro de sesiones de monitoreo de pantalla (asiento de supervisor o admin)
POST/v1/screen-monitor/sessionsSolicitar ver la pantalla de un agente (asiento de supervisor o admin)
POST/v1/screen-monitor/sessions/{id}/signalEnviar un mensaje de señalización WebRTC a la otra parte de una sesión en vivo
GET/v1/screen-monitor/sessions/{id}/iceCredencial TURN efímera para UNA sesión de monitoreo (cualquiera de las dos partes)
POST/v1/screen-monitor/sessions/{id}/endCerrar una sesión de monitoreo (cualquiera de las dos partes)
PATCH/v1/tenantAjustes de la cuenta de autoservicio
GET/v1/tenant/calling-windowEl horario de llamadas propio del tenant
PUT/v1/tenant/calling-windowEstablecer el horario de llamadas del tenant
DELETE/v1/tenant/calling-windowBorrar el horario de llamadas del tenant
GET/v1/tenant/login-policyLa política de inicio de sesión del tenant
PUT/v1/tenant/login-policyPublicar una nueva versión de la política de inicio de sesión del tenant
GET/v1/tenant/login-policy/historyTodas las versiones publicadas de la política de inicio de sesión del tenant
GET/v1/tenantsListar tenants (siempre un solo elemento)
GET/v1/tenants/{id}Obtener el tenant propio por id

Tenant autenticado, usuario y ASIENTO derivado ​

http
GET /v1/me

Devuelve 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 — seat es la fila de agente propia de quien llama, en la misma proyección que devuelve GET /v1/agents/{id}.
  • no_identity — una clave de máquina dd_: no hay ninguna persona detrás, así que no existe un asiento "propio". seat es null; NO se consulta el cuerpo.
  • seat_not_linked — una persona ddu_ sin asiento activo (p. ej. un supervisor sin posición de marcación). seat es null.
  • seat_gone — el bearer apunta a una fila de agente eliminada. Solo es alcanzable por el carril ddw_, 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.

CampoTipoObligatorioDescripción
tenantobject (Tenant)sí
tenant.idstring (uuid)no
tenant.namestringno
tenant.account_idstring (uuid)noLa EMPRESA a la que pertenece este tenant (sede) (DA-2).
tenant.billing_account_idstring (uuid)noLa 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.statusactive · suspendedno
tenant.syntheticbooleannotrue 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_monthsintegerno
tenant.seatsintegerno
tenant.tierstarter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shieldno
tenant.voice_ai_enabledbooleanno
tenant.ai_monthly_budget_usdnumber | nullnoTecho de integridad de costes (lo fija el admin; null = sin tope).
tenant.ai_max_call_secondsinteger | nullnoTecho 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_effectiveintegernoEl 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_usdnumber | nullnoTope de gasto en SMS acumulado en el mes hasta la fecha.
tenant.human_initiated_enabledbooleannoPostura 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_cpsinteger | nullnoTecho de CPS por tenant (lo fija el admin; null = sin techo). ADR #12.
tenant.max_concurrent_channelsinteger | nullnoTecho 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_namestring | nullnoNombre 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_phonestring | nullnoNú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_countrystring | nullnoPaí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_regionstring | nullnoCó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_codestring | nullnoCódigo postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77.
tenant.kyc_verified_atstring (date-time) | nullnoCuá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_atstring (date-time)no
tenant.updated_atstring (date-time)no
userobject (User)noUn usuario del dashboard; el hash de la contraseña nunca se expone.
user.idstring (uuid)no
user.emailstring (email)noIdentificador de inicio de sesión global, almacenado en minúsculas.
user.namestringno
user.statusactive · disabledno
user.rolemember · account_adminnoPrivilegio 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_atstring (date-time)no
user.last_login_atstring (date-time) | nullno
user.mfa_enabledbooleannoSi 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_atstring (date-time)no
user.updated_atstring (date-time)no
supportobjectnoSolo para una sesión de soporte dds_ (ADR #225): la sesión es de solo lectura y termina en expires_at.
support.read_onlytruesí
support.session_idstring (uuid)sí
support.expires_atstring (date-time)sí
support.operatorstringsíEl operador de la plataforma que la abrió, operator:<id> (<name>).
seatAgent | nullsíEl asiento propio de quien llama, o null cuando seat_source no es token.
seat_sourcetoken · no_identity · seat_not_linked · seat_gonesíCómo se resolvió el asiento (o por qué no hay ninguno). Siempre presente.
capabilitiesobjectsíCapacidades de UI por tenant (core#1806), una lista cerrada. Una capacidad que no se puede resolver se lee como false.
capabilities.conferencebooleansí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_channelbooleannotrue 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_monitorobject (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.enabledbooleansí
screen_monitor.notice_ack_requiredbooleansí
screen_monitor.notice_versioninteger | nullsíLa versión vigente del aviso, o null cuando no hay ninguna publicada o la función está apagada.
screen_monitor.scopesobjectsí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_monitoringobject (ScreenMonitorScopeStatus)sí
screen_monitor.scopes.live_monitoring.ack_currentbooleansí
screen_monitor.scopes.live_monitoring.acked_atstring (date-time) | nullsíCuándo acusó recibo el asiento de la versión vigente para este alcance, o null.
screen_monitor.scopes.screen_recordingobject (ScreenMonitorScopeStatus)sí
screen_monitor.scopes.screen_recording.ack_currentbooleansí
screen_monitor.scopes.screen_recording.acked_atstring (date-time) | nullsí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/scorecard

El 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

NombreEnTipoObligatorioDescripción
fromquerystring (date-time)noInicio inclusivo (RFC 3339). Por defecto, el checked_in_at activo del asiento, o now - 12h cuando no hay ninguno; véase window.source.
toquerystring (date-time)noFin 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.

CampoTipoObligatorioDescripción
agentobjectsí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.idstring (uuid)sí
agent.namestringsí
agent.sip_extensionstring | nullsí
windowobjectsí
window.fromstring (date-time)sí
window.tostring (date-time)sí
window.sourcecheckin · explicit · fallback_12hsí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.
callsobjectsí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.totalintegersí
calls.in_flightintegersí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_dispositionobjectsíConteos de intentos indexados por la disposición de la ÚLTIMA corrección de cierre escrita para el intento.
promisesobjectsíPromesas que este asiento REGISTRÓ en la ventana (inserted_at, no promised_date: un turno contiene promesas fechadas con semanas de antelación).
promises.countintegersí
promises.by_statusobjectsí
promises.by_status.pendingintegersí
promises.by_status.keptintegersí
promises.by_status.brokenintegersí
promises.by_status.cancelledintegersí
promises.amount_centsobjectsí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.USDintegersí
promises.amount_cents.MXNintegersí

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/notice

La 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.

CampoTipoObligatorioDescripción
noticeobject (ScreenMonitorNotice)síUna versión del aviso de monitoreo de pantalla (GET /v1/screen-monitor/notice). Global, de solo anexar.
notice.versionintegersí
notice.langstringsíIdioma del texto, p. ej. es.
notice.titlestringsí
notice.bodystringsí
notice.text_sha256stringsíSHA-256 (hex) de body en UTF-8; un acuse de recibo lo copia.
notice.effective_atstring (date-time)sí
notice.published_atstring (date-time)sí

401 — Unauthorized ​

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

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

403 — Forbidden ​

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

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

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/acks

Los 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

NombreEnTipoObligatorioDescripción
limitqueryintegernoTamaño de página (un valor por encima del techo se recorta, no se rechaza).
cursorquerystringnoCursor keyset opaco: el next_cursor de la página anterior.
agent_idquerystring (uuid)noSolo los acuses de recibo de este asiento.

Respuestas

200 ​

Una página de acuses de recibo, los más recientes primero.

CampoTipoObligatorioDescripción
acksarreglo de ScreenMonitorAcksí
acks[].idstring (uuid)sí
acks[].agent_idstring (uuid)sí
acks[].notice_versionintegersí
acks[].scopelive_monitoring · screen_recordingsíEl alcance aceptado; una fila por (asiento, versión del aviso, alcance).
acks[].text_sha256stringsíEl hash del texto que vio el asiento (copiado de la versión del aviso).
acks[].channeldashboard_checkinsí
acks[].acked_atstring (date-time)sí
next_cursorstring | nullsíCursor de la página siguiente; null en la última.

400 — BadRequest ​

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

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

401 — Unauthorized ​

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

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

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/acks

Registra 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)

CampoTipoObligatorioDescripción
notice_versionintegersíLa versión que se acepta; debe ser la vigente.
scopelive_monitoring · screen_recordingsíLo que el asiento acepta con este acuse de recibo.
agent_idstring (uuid)noOpcional. 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).

CampoTipoObligatorioDescripción
ackobject (ScreenMonitorAck)síUn acuse de recibo de una versión del aviso por un asiento. De solo anexar.
ack.idstring (uuid)sí
ack.agent_idstring (uuid)sí
ack.notice_versionintegersí
ack.scopelive_monitoring · screen_recordingsíEl alcance aceptado; una fila por (asiento, versión del aviso, alcance).
ack.text_sha256stringsíEl hash del texto que vio el asiento (copiado de la versión del aviso).
ack.channeldashboard_checkinsí
ack.acked_atstring (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-notice

Los 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.

CampoTipoObligatorioDescripción
noticeobject (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.versionintegersí
notice.effective_atstring (date-time)sí
notice.textsarreglo de objectsí
notice.texts[].langstringsíIdioma del texto, p. ej. es.
notice.texts[].titlestringsí
notice.texts[].bodystringsí
notice.texts[].text_sha256stringsíSHA-256 (hex) de body en UTF-8; una aceptación lo copia.
notice.texts[].published_atstring (date-time)sí

401 — Unauthorized ​

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

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

403 — Forbidden ​

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

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

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-acknowledgements

Registra 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)

CampoTipoObligatorioDescripción
notice_versionintegersíLa versión que se acepta; debe ser la vigente.
langstringsíEl idioma del texto que se mostró al agente, p. ej. es o en.
agent_idstring (uuid)noOpcional. 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).

CampoTipoObligatorioDescripción
acknowledgementobject (AgentMonitoringAck)síUna aceptación de una versión del aviso por un agente. De solo anexar.
acknowledgement.idstring (uuid)sí
acknowledgement.agent_idstring (uuid)sí
acknowledgement.notice_versionintegersí
acknowledgement.langstringsíEl idioma del texto que vio el agente.
acknowledgement.text_sha256stringsíEl hash del texto que vio el agente (copiado de la versión del aviso).
acknowledgement.channeldashboard_checkinsíEl origen de la aceptación.
acknowledgement.acked_atstring (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/armed

El 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)

CampoTipoObligatorioDescripción
armedbooleansí
tab_idstringsíUn id opaco que el dashboard asigna a la pestaña que mantiene la captura.

Respuestas

200 ​

El estado del asiento tras la llamada.

CampoTipoObligatorioDescripción
armedbooleansí
tab_idstringnoPresente 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/sessions

Las 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

NombreEnTipoObligatorioDescripción
limitqueryintegernoTamaño de página (un valor por encima del techo se recorta, no se rechaza).
cursorquerystringnoCursor keyset opaco: el next_cursor de la página anterior.
agent_idquerystring (uuid)noSolo las sesiones sobre este asiento.
resultquerystarted · ended · failed · refusednoSolo las filas con este resultado.

Respuestas

200 ​

Una página de sesiones, las más recientes primero.

CampoTipoObligatorioDescripción
sessionsarreglo de ScreenMonitorSessionsí
sessions[].idstring (uuid)sí
sessions[].supervisor_user_idstring (uuid)síEl inicio de sesión que lo solicitó.
sessions[].supervisor_idstring (uuid) | nullsíEl asiento de quien solicita; null cuando el inicio de sesión no tiene asiento.
sessions[].agent_idstring (uuid) | nullsíEl asiento observado; null cuando no se resolvió para el tenant o el rol fue rechazado primero.
sessions[].notice_versioninteger | nullsíLa versión del aviso en vigor cuando se abrió la sesión, cuando el agente tenía una.
sessions[].resultstarted · ended · failed · refusedsí
sessions[].reasonflag_off · role · agent_not_found · no_ack · not_armed · agent_busy · agent_stopped · credential_lapsed · checkout · peer_failed | nullsí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_atstring (date-time)sí
sessions[].ended_atstring (date-time) | nullsí
next_cursorstring | nullsíCursor de la página siguiente; null en la última.

400 — BadRequest ​

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

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

401 — Unauthorized ​

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

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

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/sessions

Abre 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)

CampoTipoObligatorioDescripción
agent_idstring (uuid)síEl asiento a observar.

Respuestas

201 ​

La sesión se abrió; la fila está started.

CampoTipoObligatorioDescripción
sessionobject (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.idstring (uuid)sí
session.supervisor_user_idstring (uuid)síEl inicio de sesión que lo solicitó.
session.supervisor_idstring (uuid) | nullsíEl asiento de quien solicita; null cuando el inicio de sesión no tiene asiento.
session.agent_idstring (uuid) | nullsíEl asiento observado; null cuando no se resolvió para el tenant o el rol fue rechazado primero.
session.notice_versioninteger | nullsíLa versión del aviso en vigor cuando se abrió la sesión, cuando el agente tenía una.
session.resultstarted · ended · failed · refusedsí
session.reasonflag_off · role · agent_not_found · no_ack · not_armed · agent_busy · agent_stopped · credential_lapsed · checkout · peer_failed | nullsí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_atstring (date-time)sí
session.ended_atstring (date-time) | nullsí

400 — BadRequest ​

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

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

401 — Unauthorized ​

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

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

403 ​

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}/signal

Reenví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

NombreEnTipoObligatorioDescripción
idpathstring (uuid)sí

Cuerpo de la petición (JSON, obligatorio)

CampoTipoObligatorioDescripción
kindoffer · answer · candidatesí
payloadobjectsíEl objeto SDP o candidato ICE, pasado sin modificar.

Respuestas

202 ​

Entregado al tópico privado de la otra parte.

CampoTipoObligatorioDescripción
deliveredbooleansí

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}/ice

El 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

NombreEnTipoObligatorioDescripción
idpathstring (uuid)sí

Respuestas

200 ​

La credencial de relay para el lado de la sesión de quien llama.

CampoTipoObligatorioDescripción
ice_serverobjectsí
ice_server.urlsarreglo de stringsíURLs turn: / turns: del relay de la celda.
ice_server.usernamestringsí<unix expiry>:scr.<session_id>.<agent|viewer>
ice_server.credentialstringsíBase64 de HMAC-SHA1(secreto compartido, username).
expires_atstring (date-time)sí

401 — Unauthorized ​

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

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

403 — Forbidden ​

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

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

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}/end

Cierra 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

NombreEnTipoObligatorioDescripción
idpathstring (uuid)sí

Cuerpo de la petición (JSON)

CampoTipoObligatorioDescripción
reasonpeer_failedno

Respuestas

200 ​

La sesión tras la llamada.

CampoTipoObligatorioDescripción
sessionobject (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.idstring (uuid)sí
session.supervisor_user_idstring (uuid)síEl inicio de sesión que lo solicitó.
session.supervisor_idstring (uuid) | nullsíEl asiento de quien solicita; null cuando el inicio de sesión no tiene asiento.
session.agent_idstring (uuid) | nullsíEl asiento observado; null cuando no se resolvió para el tenant o el rol fue rechazado primero.
session.notice_versioninteger | nullsíLa versión del aviso en vigor cuando se abrió la sesión, cuando el agente tenía una.
session.resultstarted · ended · failed · refusedsí
session.reasonflag_off · role · agent_not_found · no_ack · not_armed · agent_busy · agent_stopped · credential_lapsed · checkout · peer_failed | nullsí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_atstring (date-time)sí
session.ended_atstring (date-time) | nullsí

400 — BadRequest ​

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

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

401 — Unauthorized ​

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

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

403 — Forbidden ​

El tenant está suspendido. Una sesión del dashboard (ddu_) de un tenant cuya política de inicio de sesión (ADR 227) está en modo enforce responde 403 con el código login_policy_denied ante una solicitud desde una dirección o en una hora que la política no permite — el mismo cuerpo en ambos casos; la sesión no se revoca y una solicitud posterior dentro de la política se procesa. Un token de widget (ddw_) de un puesto queda sujeto a la misma política en cada solicitud (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/tenant

Solo 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)

CampoTipoObligatorioDescripción
namestringno
json
{
  "name": "Acme Collections LLC"
}

Respuestas

200 ​

Tenant actualizado.

CampoTipoObligatorioDescripción
tenantobject (Tenant)sí
tenant.idstring (uuid)no
tenant.namestringno
tenant.account_idstring (uuid)noLa EMPRESA a la que pertenece este tenant (sede) (DA-2).
tenant.billing_account_idstring (uuid)noLa 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.statusactive · suspendedno
tenant.syntheticbooleannotrue 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_monthsintegerno
tenant.seatsintegerno
tenant.tierstarter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shieldno
tenant.voice_ai_enabledbooleanno
tenant.ai_monthly_budget_usdnumber | nullnoTecho de integridad de costes (lo fija el admin; null = sin tope).
tenant.ai_max_call_secondsinteger | nullnoTecho 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_effectiveintegernoEl 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_usdnumber | nullnoTope de gasto en SMS acumulado en el mes hasta la fecha.
tenant.human_initiated_enabledbooleannoPostura 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_cpsinteger | nullnoTecho de CPS por tenant (lo fija el admin; null = sin techo). ADR #12.
tenant.max_concurrent_channelsinteger | nullnoTecho 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_namestring | nullnoNombre 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_phonestring | nullnoNú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_countrystring | nullnoPaí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_regionstring | nullnoCó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_codestring | nullnoCódigo postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77.
tenant.kyc_verified_atstring (date-time) | nullnoCuá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_atstring (date-time)no
tenant.updated_atstring (date-time)no

400 — BadRequest ​

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

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

401 — Unauthorized ​

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

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

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-window

La 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.

CampoTipoObligatorioDescripción
calling_windowTenantCallingWindow | nullsí

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-window

Reemplaza 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)

CampoTipoObligatorioDescripción
timezonestringsíUn nombre de zona IANA. Una zona que no se resuelve es 422.
weeklyobject (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.monobject (CampaignDayWindow)noLa 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.startstringsí
weekly.mon.endstringsí
weekly.tueobject (CampaignDayWindow)noLa 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.startstringsí
weekly.tue.endstringsí
weekly.wedobject (CampaignDayWindow)noLa 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.startstringsí
weekly.wed.endstringsí
weekly.thuobject (CampaignDayWindow)noLa 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.startstringsí
weekly.thu.endstringsí
weekly.friobject (CampaignDayWindow)noLa 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.startstringsí
weekly.fri.endstringsí
weekly.satobject (CampaignDayWindow)noLa 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.startstringsí
weekly.sat.endstringsí
weekly.sunobject (CampaignDayWindow)noLa 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.startstringsí
weekly.sun.endstringsí
json
{
  "timezone": "America/Chicago",
  "weekly": {
    "mon": {
      "start": "09:00",
      "end": "20:00"
    },
    "tue": {
      "start": "09:00",
      "end": "20:00"
    }
  }
}

Respuestas

200 ​

Almacenada (normalizada).

CampoTipoObligatorioDescripción
calling_windowobject (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.timezonestringsíUn nombre de zona IANA. Una zona que no se resuelve es 422.
calling_window.weeklyobject (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.monobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.mon.endstringsí
calling_window.weekly.tueobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.tue.endstringsí
calling_window.weekly.wedobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.wed.endstringsí
calling_window.weekly.thuobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.thu.endstringsí
calling_window.weekly.friobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.fri.endstringsí
calling_window.weekly.satobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.sat.endstringsí
calling_window.weekly.sunobject (CampaignDayWindow)noLa 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.startstringsí
calling_window.weekly.sun.endstringsí

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-window

Vuelve 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.

CampoTipoObligatorioDescripción
calling_windownullsí

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-policy

La 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.

CampoTipoObligatorioDescripción
login_policyTenantLoginPolicyVersion | nullsí
client_address_measuredbooleansí

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-policy

Anexa 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 un timezone ausente, o enforce sin ip_allow ni windows.
  • 409 client_address_not_measured: un ip_allow en observe o enforce en una celda cuya dirección de cliente no se mide.
  • 422 would_lock_out_caller: con un ip_allow, la dirección propia de quien escribe no se puede atribuir; con enforce, 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)

CampoTipoObligatorioDescripción
modeoff · observe · enforcenooff también es una versión (apagar es escribir, no borrar). observe nunca deniega. enforce deniega con 403 login_policy_denied.
ip_allowarreglo de stringnoBloques 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.
windowsobjectnoHoras 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.memberobject (CampaignWeeklyWindow)noVentanas 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.monobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.mon.endstringsí
windows.member.tueobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.tue.endstringsí
windows.member.wedobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.wed.endstringsí
windows.member.thuobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.thu.endstringsí
windows.member.friobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.fri.endstringsí
windows.member.satobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.sat.endstringsí
windows.member.sunobject (CampaignDayWindow)noLa 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.startstringsí
windows.member.sun.endstringsí
windows.account_adminobject (CampaignWeeklyWindow)noVentanas 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.monobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.mon.endstringsí
windows.account_admin.tueobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.tue.endstringsí
windows.account_admin.wedobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.wed.endstringsí
windows.account_admin.thuobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.thu.endstringsí
windows.account_admin.friobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.fri.endstringsí
windows.account_admin.satobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.sat.endstringsí
windows.account_admin.sunobject (CampaignDayWindow)noLa 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.startstringsí
windows.account_admin.sun.endstringsí
timezonestring,nullnoUna zona IANA; obligatoria cuando windows no está vacío, rechazada en caso contrario.
reasonstring,nullno
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ó.

CampoTipoObligatorioDescripción
login_policyobject (TenantLoginPolicyVersion)síUna versión publicada (de solo anexar) de la política de inicio de sesión del tenant.
login_policy.versionintegersí
login_policy.modeoff · observe · enforcesí
login_policy.ip_allowarreglo de stringsí
login_policy.windowsobjectsí
login_policy.windows.memberobject (CampaignWeeklyWindow)noVentanas 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.monobject (CampaignDayWindow)noLa 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.tueobject (CampaignDayWindow)noLa 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.wedobject (CampaignDayWindow)noLa 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.thuobject (CampaignDayWindow)noLa 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.friobject (CampaignDayWindow)noLa 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.satobject (CampaignDayWindow)noLa 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.sunobject (CampaignDayWindow)noLa 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_adminobject (CampaignWeeklyWindow)noVentanas 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.monobject (CampaignDayWindow)noLa 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.tueobject (CampaignDayWindow)noLa 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.wedobject (CampaignDayWindow)noLa 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.thuobject (CampaignDayWindow)noLa 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.friobject (CampaignDayWindow)noLa 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.satobject (CampaignDayWindow)noLa 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.sunobject (CampaignDayWindow)noLa 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.timezonestring,nullsí
login_policy.set_bystringsí
login_policy.reasonstring,nullsí
login_policy.inserted_atstring (date-time)sí

401 — Unauthorized ​

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

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

403 ​

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/history

Las 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

NombreEnTipoObligatorioDescripción
limitqueryintegernoTamaño de página (un valor por encima del techo se recorta, no se rechaza).
cursorqueryintegernoEl next_cursor de la página anterior.

Respuestas

200 ​

Una página de versiones.

CampoTipoObligatorioDescripción
versionsarreglo de TenantLoginPolicyVersionsí
versions[].versionintegersí
versions[].modeoff · observe · enforcesí
versions[].ip_allowarreglo de stringsí
versions[].windowsobjectsí
versions[].windows.memberobject (CampaignWeeklyWindow)noVentanas 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.monobject (CampaignDayWindow)noLa 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.tueobject (CampaignDayWindow)noLa 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.wedobject (CampaignDayWindow)noLa 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.thuobject (CampaignDayWindow)noLa 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.friobject (CampaignDayWindow)noLa 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.satobject (CampaignDayWindow)noLa 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.sunobject (CampaignDayWindow)noLa 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_adminobject (CampaignWeeklyWindow)noVentanas 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.monobject (CampaignDayWindow)noLa 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.tueobject (CampaignDayWindow)noLa 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.wedobject (CampaignDayWindow)noLa 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.thuobject (CampaignDayWindow)noLa 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.friobject (CampaignDayWindow)noLa 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.satobject (CampaignDayWindow)noLa 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.sunobject (CampaignDayWindow)noLa 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[].timezonestring,nullsí
versions[].set_bystringsí
versions[].reasonstring,nullsí
versions[].inserted_atstring (date-time)sí
next_cursorinteger,nullsí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/tenants

Paridad 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).

CampoTipoObligatorioDescripción
tenantsarreglo de Tenantsí
tenants[].idstring (uuid)no
tenants[].namestringno
tenants[].account_idstring (uuid)noLa EMPRESA a la que pertenece este tenant (sede) (DA-2).
tenants[].billing_account_idstring (uuid)noLa 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[].statusactive · suspendedno
tenants[].syntheticbooleannotrue 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_monthsintegerno
tenants[].seatsintegerno
tenants[].tierstarter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shieldno
tenants[].voice_ai_enabledbooleanno
tenants[].ai_monthly_budget_usdnumber | nullnoTecho de integridad de costes (lo fija el admin; null = sin tope).
tenants[].ai_max_call_secondsinteger | nullnoTecho 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_effectiveintegernoEl 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_usdnumber | nullnoTope de gasto en SMS acumulado en el mes hasta la fecha.
tenants[].human_initiated_enabledbooleannoPostura 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_cpsinteger | nullnoTecho de CPS por tenant (lo fija el admin; null = sin techo). ADR #12.
tenants[].max_concurrent_channelsinteger | nullnoTecho 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_namestring | nullnoNombre 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_phonestring | nullnoNú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_countrystring | nullnoPaí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_regionstring | nullnoCó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_codestring | nullnoCódigo postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77.
tenants[].kyc_verified_atstring (date-time) | nullnoCuá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_atstring (date-time)no
tenants[].updated_atstring (date-time)no

401 — Unauthorized ​

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

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

429 — ApiKeyRateLimited ​

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

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

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

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

Respuestas

200 ​

El tenant.

CampoTipoObligatorioDescripción
tenantobject (Tenant)sí
tenant.idstring (uuid)no
tenant.namestringno
tenant.account_idstring (uuid)noLa EMPRESA a la que pertenece este tenant (sede) (DA-2).
tenant.billing_account_idstring (uuid)noLa 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.statusactive · suspendedno
tenant.syntheticbooleannotrue 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_monthsintegerno
tenant.seatsintegerno
tenant.tierstarter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shieldno
tenant.voice_ai_enabledbooleanno
tenant.ai_monthly_budget_usdnumber | nullnoTecho de integridad de costes (lo fija el admin; null = sin tope).
tenant.ai_max_call_secondsinteger | nullnoTecho 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_effectiveintegernoEl 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_usdnumber | nullnoTope de gasto en SMS acumulado en el mes hasta la fecha.
tenant.human_initiated_enabledbooleannoPostura 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_cpsinteger | nullnoTecho de CPS por tenant (lo fija el admin; null = sin techo). ADR #12.
tenant.max_concurrent_channelsinteger | nullnoTecho 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_namestring | nullnoNombre 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_phonestring | nullnoNú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_countrystring | nullnoPaí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_regionstring | nullnoCó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_codestring | nullnoCódigo postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77.
tenant.kyc_verified_atstring (date-time) | nullnoCuá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_atstring (date-time)no
tenant.updated_atstring (date-time)no

401 — Unauthorized ​

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

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

404 — NotFound ​

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

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

429 — ApiKeyRateLimited ​

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

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

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