Appearance
SSO
Generado desde
DialerDigital/corecommit0fc18008b3c8— especificaciónopenapi/dialer-v1.yamlv0.134.0. No se edita a mano; consulta Cómo se construye esta referencia. Descargar la especificación OpenAPI.
Traducción en curso
Esta referencia se genera desde la especificación OpenAPI. Las cadenas que todavía no tienen traducción aparecen en inglés.
Inicio de sesión único SAML 2.0 por tenant: la configuración del proveedor de identidad del tenant (cargada por un account_admin, auditada de solo anexado) y los metadatos del proveedor de servicio que necesita el administrador de su IdP. Solo iniciado por el SP; la persona ya debe existir en el tenant (sin aprovisionamiento just-in-time).
Endpoints
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/auth/sso/{tenant_id}/metadata | Metadatos del proveedor de servicio SAML de un tenant (para el administrador de su IdP) |
GET | /v1/auth/sso/{tenant_id}/slo | SAML Single Logout, desde el IdP (binding HTTP-Redirect) |
POST | /v1/auth/sso/{tenant_id}/slo | SAML Single Logout, desde el IdP (binding HTTP-POST) |
POST | /v1/auth/sso/start | Iniciar un inicio de sesión SAML SSO (iniciado por el SP) |
POST | /v1/auth/sso/{tenant_id}/acs | SAML Assertion Consumer Service (binding HTTP-POST) |
POST | /v1/auth/sso/exchange | Canjear un código de inicio de sesión SSO por una sesión |
GET | /v1/sso/idp-config | La configuración del proveedor de identidad SAML del tenant |
PUT | /v1/sso/idp-config | Cargar o reemplazar la configuración del proveedor de identidad SAML del tenant |
PATCH | /v1/sso/idp-config | Habilitar o deshabilitar la configuración SAML del tenant |
DELETE | /v1/sso/idp-config | Eliminar la configuración SAML del tenant |
GET | /v1/sso/idp-config/events | Registro de auditoría de la configuración SAML del tenant |
Metadatos del proveedor de servicio SAML de un tenant (para el administrador de su IdP)
http
GET /v1/auth/sso/{tenant_id}/metadataLos metadatos SP de SAML 2.0 que el administrador del IdP del tenant pega en el proveedor de identidad: el entityID de este tenant, su Assertion Consumer Service (HTTP-POST), su SingleLogoutService (HTTP-POST y HTTP-Redirect) y el formato de NameID (emailAddress). PÚBLICO (security: []) y construido solo a partir del tenant_id de la ruta — no lee nada por tenant, así que los metadatos de un tenant que no existe son byte por byte los de uno que sí (sin oráculo de existencia de tenant). Cada tenant es un SP DISTINTO: una aserción emitida para el SP de un tenant nunca valida para el de otro. 404 mientras la celda no tenga URL base pública del SP configurada (SSO_SP_BASE_URL), y para un tenant_id que no es un UUID.
Autenticación: ninguna — se autentica por la firma de la petición (ver abajo).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
tenant_id | path | string (uuid) | sí |
Respuestas
200
El documento de metadatos del SP.
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"
}
}SAML Single Logout, desde el IdP (binding HTTP-Redirect)
http
GET /v1/auth/sso/{tenant_id}/sloSingle Logout (core#1581, ADR #210). Adonde el IdP del tenant envía una LogoutRequest a través del navegador — HTTP-Redirect (GET, SAMLRequest en la query, la firma en Signature/SigAlg, verificada sobre la query CRUDA) o HTTP-POST (campo de formulario SAMLRequest, firma envuelta). Cuando la petición está firmada por el IdP de ESTE tenant, se revocan todas las sesiones ddu_ vivas de la persona que nombra su NameID.
Validado tan estrictamente como el ACS: firma contra los certificados fijados solo de este tenant, Issuer, Destination (este endpoint), IssueInstant dentro de unos pocos minutos, NotOnOrAfter, y un ID de LogoutRequest nunca visto antes (repetición). Fail-closed: CUALQUIER duda — tenant desconocido, SSO desactivado, firma incorrecta, emisor ajeno, caducado, repetido — da UN mismo 400 idéntico y no se revoca nada; el motivo solo está en el log del servidor. Un NameID que no nombra a nadie da el mismo 303 que uno que sí (sin enumeración de usuarios). Nunca responde 500 ante un documento hostil.
El éxito es 303: al slo_url del IdP con una LogoutResponse (sin firmar; el RelayState repetido cuando es imprimible y de como máximo 80 bytes) o, cuando el IdP no publicó ninguno, al dashboard. Una SAMLResponse (el IdP respondiendo a un logout que inició core) no cambia nada y da 303 al dashboard. 404 mientras la celda no tenga URL base pública del SP.
El INICIO DE SESIÓN INICIADO POR EL IdP (una Response no solicitada en el ACS) NO está soportado deliberadamente: reabriría el login-CSRF y la repetición de aserciones, que el intercambio en dos pasos cierra.
Autenticación: ninguna — se autentica por la firma de la petición (ver abajo).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
tenant_id | path | string (uuid) | sí | |
SAMLRequest | query | string | no | |
RelayState | query | string | no | |
SigAlg | query | string | no | |
Signature | query | string | no |
Respuestas
303
Hecho (o nada que hacer); Location es el slo_url del IdP con la LogoutResponse, o el dashboard.
400
La petición de logout fue rechazada (una sola respuesta para cualquier motivo).
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 — LoginRateLimited
El freno de inicio de sesión por CUENTA rechazó este intento (ventana fija; se puede reintentar más tarde). code rate_limited. Se aplica por email (/v1/auth/login) o por {tenant_id, email} (/v1/auth/agent-login), de modo que limita muchas contraseñas contra UNA sola cuenta. El spraying de credenciales sobre muchas cuentas actualmente se CUENTA Y SE ALERTA pero no se rechaza, porque la única clave por origen disponible para core queda colapsada por el proxy inverso del dashboard; un intento de spraying por tanto sigue devolviendo el 401 ordinario. Retry-After es el tiempo que queda de la ventana fija.
/v1/auth/login tiene un segundo motivo, ACUMULATIVO, para el mismo 429 (core#2335, NIST SP 800-63B-4 §3.2.2): cuando una persona acumula login_progressive_threshold_failures (20 por defecto) contraseñas incorrectas desde su último inicio de sesión exitoso, dentro de 24 h, el inicio de sesión se retiene incluso con la contraseña CORRECTA durante 15 min tras el último fallo, y se duplica por cada conjunto adicional de fallos del umbral (con tope de 6 h); Retry-After es el tiempo que queda. Una pausa, nunca un bloqueo permanente. Los intentos retenidos no se registran, así que no la alargan.
json
{
"error": {
"code": "rate_limited",
"message": "too many login attempts; retry later"
}
}SAML Single Logout, desde el IdP (binding HTTP-POST)
http
POST /v1/auth/sso/{tenant_id}/sloSingle Logout (core#1581, ADR #210). Adonde el IdP del tenant envía una LogoutRequest a través del navegador — HTTP-Redirect (GET, SAMLRequest en la query, la firma en Signature/SigAlg, verificada sobre la query CRUDA) o HTTP-POST (campo de formulario SAMLRequest, firma envuelta). Cuando la petición está firmada por el IdP de ESTE tenant, se revocan todas las sesiones ddu_ vivas de la persona que nombra su NameID.
Validado tan estrictamente como el ACS: firma contra los certificados fijados solo de este tenant, Issuer, Destination (este endpoint), IssueInstant dentro de unos pocos minutos, NotOnOrAfter, y un ID de LogoutRequest nunca visto antes (repetición). Fail-closed: CUALQUIER duda — tenant desconocido, SSO desactivado, firma incorrecta, emisor ajeno, caducado, repetido — da UN mismo 400 idéntico y no se revoca nada; el motivo solo está en el log del servidor. Un NameID que no nombra a nadie da el mismo 303 que uno que sí (sin enumeración de usuarios). Nunca responde 500 ante un documento hostil.
El éxito es 303: al slo_url del IdP con una LogoutResponse (sin firmar; el RelayState repetido cuando es imprimible y de como máximo 80 bytes) o, cuando el IdP no publicó ninguno, al dashboard. Una SAMLResponse (el IdP respondiendo a un logout que inició core) no cambia nada y da 303 al dashboard. 404 mientras la celda no tenga URL base pública del SP.
El INICIO DE SESIÓN INICIADO POR EL IdP (una Response no solicitada en el ACS) NO está soportado deliberadamente: reabriría el login-CSRF y la repetición de aserciones, que el intercambio en dos pasos cierra.
Autenticación: ninguna — se autentica por la firma de la petición (ver abajo).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
tenant_id | path | string (uuid) | sí |
Respuestas
303
Hecho (o nada que hacer); Location es el slo_url del IdP con la LogoutResponse, o el dashboard.
400
La petición de logout fue rechazada (una sola respuesta para cualquier motivo).
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 — LoginRateLimited
El freno de inicio de sesión por CUENTA rechazó este intento (ventana fija; se puede reintentar más tarde). code rate_limited. Se aplica por email (/v1/auth/login) o por {tenant_id, email} (/v1/auth/agent-login), de modo que limita muchas contraseñas contra UNA sola cuenta. El spraying de credenciales sobre muchas cuentas actualmente se CUENTA Y SE ALERTA pero no se rechaza, porque la única clave por origen disponible para core queda colapsada por el proxy inverso del dashboard; un intento de spraying por tanto sigue devolviendo el 401 ordinario. Retry-After es el tiempo que queda de la ventana fija.
/v1/auth/login tiene un segundo motivo, ACUMULATIVO, para el mismo 429 (core#2335, NIST SP 800-63B-4 §3.2.2): cuando una persona acumula login_progressive_threshold_failures (20 por defecto) contraseñas incorrectas desde su último inicio de sesión exitoso, dentro de 24 h, el inicio de sesión se retiene incluso con la contraseña CORRECTA durante 15 min tras el último fallo, y se duplica por cada conjunto adicional de fallos del umbral (con tope de 6 h); Retry-After es el tiempo que queda. Una pausa, nunca un bloqueo permanente. Los intentos retenidos no se registran, así que no la alargan.
json
{
"error": {
"code": "rate_limited",
"message": "too many login attempts; retry later"
}
}Iniciar un inicio de sesión SAML SSO (iniciado por el SP)
http
POST /v1/auth/sso/startDevuelve la URL del proveedor de identidad del tenant con una AuthnRequest nueva (binding HTTP-Redirect, sin firmar), más un browser_secret. NO escribe NADA: el estado del inicio de sesión viaja en el RelayState, un token compacto firmado con HMAC (id de petición, caducidad, versión de la configuración del IdP; el tenant se autentica dentro del MAC y el secreto del navegador se deriva, nunca se transporta) que el ACS verifica. El dashboard envía el navegador a redirect_url y guarda browser_secret en el MISMO navegador (sessionStorage): es lo único que puede canjear el inicio de sesión después (POST /v1/auth/sso/exchange), de modo que un inicio de sesión que inicia un tercero no puede terminarse en el navegador de otra persona. El inicio de sesión vive 5 minutos y es de un solo uso (se gasta cuando una Response con firma válida para él llega al ACS). PÚBLICO; 404 es UNA sola respuesta para un tenant que no existe, un tenant cuyo IdP no está HABILITADO y una celda sin SSO_SP_BASE_URL. El freno por origen es SOLO DE OBSERVACIÓN por defecto (anónimo y sin estado, y detrás del proxy del dashboard el origen se colapsa, así que un tope aplicado denegaría a todos los tenants a la vez); una celda que confía en su eje de origen lo promueve con BRAKE_ENFORCE_BUCKETS=sso_start.
Autenticación: ninguna — se autentica por la firma de la petición (ver abajo).
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenant_id | string (uuid) | sí |
Respuestas
200
La URL del IdP y el secreto de este navegador (Cache-Control: no-store).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
redirect_url | string (uri) | sí | |
browser_secret | string | sí | Se muestra exactamente una vez; core no almacena nada al iniciar y lo vuelve a derivar en el ACS para conservar solo su hash. |
expires_at | string (date-time) | sí |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}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
Solo en una celda que PROMOVIÓ el freno de inicio (BRAKE_ENFORCE_BUCKETS lista sso_start): el freno por ORIGEN rechazó esta llamada (rate_limited; 300 por ventana fija por defecto, SSO_START_MAX_PER_WINDOW). Nunca en el modo por defecto de solo observación. Se aplica por origen de quien llama, nunca por tenant_id. Retry-After es el tiempo que queda de la ventana.
SAML Assertion Consumer Service (binding HTTP-POST)
http
POST /v1/auth/sso/{tenant_id}/acsAdonde el navegador del IdP hace POST de la SAMLResponse (codificada como formulario, con el RelayState devuelto por POST /v1/auth/sso/start). Nunca responde con una sesión: SIEMPRE es un 303 de vuelta al callback del dashboard (/sso/callback), con #code=<one-shot code> en el FRAGMENTO de la URL en caso de éxito (de un solo uso, válido 2 minutos, canjeado en POST /v1/auth/sso/exchange) o #error=sso_failed ante CUALQUIER rechazo — el motivo preciso solo está en el log del servidor, nunca en la respuesta.
La respuesta se valida fail-closed: la firma (aserción y/o respuesta, según la configuración del tenant) contra los certificados fijados solo de ESTE tenant (el KeyInfo del documento se ignora), el elemento firmado es aquel cuyas afirmaciones se leen (sin wrapping de firma XML), una única aserción, Issuer, Audience (el SP de este tenant), Recipient/Destination (este ACS), InResponseTo (la petición que core emitió, verificada desde el RelayState firmado y gastada solo cuando la firma del IdP valida: una Response falsificada no quema el inicio de sesión), NotBefore/NotOnOrAfter con 60 s de desfase, y un ID de aserción nunca visto antes (repetición). La persona debe existir YA (coincidencia por el email verificado; no hay aprovisionamiento just-in-time) y estar activa.
Autenticación: ninguna — se autentica por la firma de la petición (ver abajo).
Parámetros
| Nombre | En | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
tenant_id | path | string (uuid) | sí |
Respuestas
303
Redirección al callback del dashboard; Location lleva #code=... o #error=sso_failed en su fragmento.
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"
}
}Canjear un código de inicio de sesión SSO por una sesión
http
POST /v1/auth/sso/exchangeCanjea el code que el ACS entregó al dashboard junto con el browser_secret que POST /v1/auth/sso/start devolvió a este mismo navegador, y emite la sesión ddu_ habitual (el mismo cuerpo que POST /v1/auth/login). El código es de un solo uso y muere en su PRIMERA presentación, coincida o no el secreto; desconocido, caducado, gastado y de otro navegador dan UN mismo 401 idéntico. Un inicio de sesión SSO no pide el segundo factor propio de la aplicación (el IdP es la autoridad), y funciona haya cerrado o no el tenant la vía de contraseña (sso_required_enforced). Aquí se consulta la política de inicio de sesión del tenant (la palabra del IdP no es la lista de permitidos del tenant): un tenant que publicó una política de inicio de sesión (direcciones y horarios permitidos por rol, ADR 227) en modo enforce rechaza un inicio de sesión desde una dirección fuera de ella o a una hora fuera de ella con UN solo 403 login_policy_denied, el mismo para la dirección y la hora: cuál de las dos falló lo dice únicamente el registro de inicios de sesión del tenant (GET /v1/auth/sign-in-events). Se consulta DESPUÉS de que la credencial resultó correcta y antes de emitir nada. Con la política desactivada o ausente, el inicio de sesión es el de antes.
Autenticación: ninguna — se autentica por la firma de la petición (ver abajo).
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
code | string | sí | |
browser_secret | string | sí |
Respuestas
200
La sesión (token es la única vez que el texto plano es visible).
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
token | string | sí | |
session | object (UserSession) | sí | Una sesión de inicio de sesión ddu_ — el hash del token nunca se expone. |
session.id | string (uuid) | no | |
session.user_id | string (uuid) | no | |
session.expires_at | string (date-time) | no | Fijada al iniciar sesión (sin renovación deslizante); tope del motor de 24h. |
session.revoked_at | string (date-time) | null | no | |
session.created_at | string (date-time) | no | |
user | object (User) | sí | Un usuario del dashboard; el hash de la contraseña nunca se expone. |
user.id | string (uuid) | no | |
user.email | string (email) | no | Identificador de inicio de sesión global, almacenado en minúsculas. |
user.name | string | no | |
user.status | active · disabled | no | |
user.role | member · account_admin | no | Privilegio dentro de la cuenta. Solo un account_admin puede crear o modificar las filas de toda la empresa que heredan todas las sedes hermanas (scope: "empresa" en carriers y proveedores de SMS); un member recibe 403. Solo lectura aquí: la promoción es un gesto del operador, nunca una petición. |
user.password_updated_at | string (date-time) | no | |
user.last_login_at | string (date-time) | null | no | |
user.mfa_enabled | boolean | no | Si la persona tiene un segundo factor ACTIVO (una inscripción pendiente de confirmación es false). Se emite SOLO cuando quien llama es un inicio de sesión de dashboard account_admin, el que puede actuar sobre él con POST /v1/users/{id}/mfa/remove; para un member, una clave dd_, o cuando no se puede determinar el rol de quien llama, el campo está AUSENTE (no es false). Dondequiera que se renderice un User rige la misma regla: GET /v1/users, GET /v1/me, el usuario devuelto por DELETE /v1/users/{id} y POST /v1/users/{id}/enable, y el usuario en las respuestas de sesión (POST /v1/auth/login, POST /v1/auth/sso/exchange y las dos rutas de finalización de MFA); en ellas, para el account_admin que acaba de iniciar sesión. |
user.created_at | string (date-time) | no | |
user.updated_at | string (date-time) | no | |
tenant | object (Tenant) | sí | |
tenant.id | string (uuid) | no | |
tenant.name | string | no | |
tenant.account_id | string (uuid) | no | La EMPRESA a la que pertenece este tenant (sede) (DA-2). |
tenant.billing_account_id | string (uuid) | no | La cuenta de facturación bajo la que se factura la sede (DA-3). Compartida por todas las sedes de una empresa consolidated; una por sede cuando es per_site. |
tenant.status | active · suspended | no | |
tenant.synthetic | boolean | no | true para un tenant de demostración. No origina tráfico: la marcación manual, el envío de SMS, la pierna externa de una transferencia y el sandbox responden 403 synthetic_tenant. Siempre presente, nunca nulo, para que una consola pueda avisar ANTES de que la acción falle (core#2512). |
tenant.retention_months | integer | no | |
tenant.seats | integer | no | |
tenant.tier | starter · growth · scale · enterprise · dialer_core · compliance_pro · audit_shield | no | |
tenant.voice_ai_enabled | boolean | no | |
tenant.ai_monthly_budget_usd | number | null | no | Techo de integridad de costes (lo fija el admin; null = sin tope). |
tenant.ai_max_call_seconds | integer | null | no | Techo estricto de duración por llamada de IA (segundos) fijado por el admin. null significa que se aplica el valor por defecto de la PLATAFORMA, nunca "sin techo" (core#922, ADR #115). |
tenant.ai_max_call_seconds_effective | integer | no | El techo que realmente se entrega a FreeSWITCH para las piernas de IA de este tenant: ai_max_call_seconds cuando está definido; si no, el valor por defecto de plataforma de la celda (AI_MAX_CALL_SECONDS_DEFAULT, valor de reserva compilado 900). Siempre presente, nunca nulo. Solo lectura (derivado). |
tenant.sms_monthly_budget_usd | number | null | no | Tope de gasto en SMS acumulado en el mes hasta la fecha. |
tenant.human_initiated_enabled | boolean | no | Postura de consentimiento D17 de iniciativa humana (la fija el admin; por defecto false). Cuando es true, una marcación de voz EN VIVO de vista previa/manual/CTI sin voz artificial y sin voz de IA no requiere consentimiento expreso previo (sin ATDS). Todas las demás compuertas (DNC, horas de silencio, Reg F, matriz estatal, voz artificial) siguen aplicándose sin cambios. |
tenant.max_cps | integer | null | no | Techo de CPS por tenant (lo fija el admin; null = sin techo). ADR #12. |
tenant.max_concurrent_channels | integer | null | no | Techo de llamadas concurrentes por tenant, una unidad por llamada con o sin agente (lo fija el admin; null = sin techo). ADR #12, ADR #185. |
tenant.abandon_seller_name | string | null | no | Nombre del vendedor para el mensaje de identificación de llamada abandonada de la FTC TSR (lo fija el admin; null = no configurado, el sobremarcado predictivo permanece apagado). ADR #14. |
tenant.abandon_seller_phone | string | null | no | Número de rellamada E.164 para el mensaje de identificación de llamada abandonada (lo fija el admin; null = no configurado). ADR #14. |
tenant.tax_country | string | null | no | País ISO-3166-1 alpha-2 de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_region | string | null | no | Código de estado/provincia de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.tax_postal_code | string | null | no | Código postal de la jurisdicción de impuesto sobre ventas del cliente (lo fija el admin; null = no declarado). ADR #77. |
tenant.kyc_verified_at | string (date-time) | null | no | Cuándo verificó la plataforma a este cliente (lo fija el admin; null = aún no). Una cuenta de 1 asiento que no está verificada no puede crear ni iniciar una campaña que se ejecute sin agente (403 verification_required, ADR #279). |
tenant.created_at | string (date-time) | no | |
tenant.updated_at | string (date-time) | no |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401
Código desconocido, caducado, ya usado o de otro navegador.
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 — LoginRateLimited
El freno de inicio de sesión por CUENTA rechazó este intento (ventana fija; se puede reintentar más tarde). code rate_limited. Se aplica por email (/v1/auth/login) o por {tenant_id, email} (/v1/auth/agent-login), de modo que limita muchas contraseñas contra UNA sola cuenta. El spraying de credenciales sobre muchas cuentas actualmente se CUENTA Y SE ALERTA pero no se rechaza, porque la única clave por origen disponible para core queda colapsada por el proxy inverso del dashboard; un intento de spraying por tanto sigue devolviendo el 401 ordinario. Retry-After es el tiempo que queda de la ventana fija.
/v1/auth/login tiene un segundo motivo, ACUMULATIVO, para el mismo 429 (core#2335, NIST SP 800-63B-4 §3.2.2): cuando una persona acumula login_progressive_threshold_failures (20 por defecto) contraseñas incorrectas desde su último inicio de sesión exitoso, dentro de 24 h, el inicio de sesión se retiene incluso con la contraseña CORRECTA durante 15 min tras el último fallo, y se duplica por cada conjunto adicional de fallos del umbral (con tope de 6 h); Retry-After es el tiempo que queda. Una pausa, nunca un bloqueo permanente. Los intentos retenidos no se registran, así que no la alargan.
json
{
"error": {
"code": "rate_limited",
"message": "too many login attempts; retry later"
}
}La configuración del proveedor de identidad SAML del tenant
http
GET /v1/sso/idp-configLa configuración del IdP del tenant (sso_idp_config, null cuando no hay ninguna cargada) y las URL del proveedor de servicio para dar al IdP (service_provider, null mientras la celda no tenga URL base del SP). Los certificados se muestran como huella SHA-256 + ventana de validez, nunca como PEM.
Solo inicio de sesión de account_admin en el dashboard (véase 403).
Respuestas
200
La configuración y las URL del SP.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
sso_idp_config | SsoIdpConfig | null | sí | |
service_provider | SsoServiceProvider | null | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403
role_forbidden: cualquier cosa que no sea un inicio de sesión de dashboard (ddu_) cuya persona tenga el rol account_admin — incluidos una clave de API dd_ y un inicio de sesión member; forbidden: un token de widget (ddw_) o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "SSO configuration takes a dashboard login with the account_admin role"
}
}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"
}
}Cargar o reemplazar la configuración del proveedor de identidad SAML del tenant
http
PUT /v1/sso/idp-configUpsert de cuerpo completo: el entityID del IdP, su URL SSO HTTP-Redirect (solo https) y uno o dos certificados de firma X.509 (dos para rotar sin cortar el acceso). Cada certificado debe ser exactamente un bloque PEM, RSA de al menos 2048 bits, y válido ahora. Un certificado es de confianza por su huella SHA-256 (la cadena no se valida). Al menos uno de require_signed_assertion / require_signed_response debe ser true: no existe ninguna configuración que acepte una aserción sin firmar.
La configuración se almacena DESHABILITADA salvo que se envíe enabled: true; cargarla no abre la puerta. Todo cambio efectivo escribe un hecho de auditoría, en la misma transacción, con la persona que lo hizo. Un reemplazo idéntico a lo almacenado no escribe nada.
Un valor que falla la validación (una URL que no es https, un certificado débil, caducado o que no es exactamente un bloque PEM, un certificado repetido) da 400 con el nombre del campo.
Solo inicio de sesión de account_admin en el dashboard (véase 403).
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
idp_entity_id | string | sí | |
sso_url | string | sí | |
slo_url | string,null | no | El SingleLogoutService del IdP (https); ausente o null significa que el IdP no publica ninguno. |
certificates | arreglo de string | sí | |
require_signed_assertion | boolean | no | |
require_signed_response | boolean | no | |
email_attribute | string,null | no | Atributo SAML que lleva el email; ausente o null significa el NameID. |
enabled | boolean | no |
Respuestas
200
Almacenada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
sso_idp_config | SsoIdpConfig | null | sí | |
service_provider | SsoServiceProvider | null | sí |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403
role_forbidden: cualquier cosa que no sea un inicio de sesión de dashboard (ddu_) cuya persona tenga el rol account_admin — incluidos una clave de API dd_ y un inicio de sesión member; forbidden: un token de widget (ddw_) o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "SSO configuration takes a dashboard login with the account_admin role"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}Habilitar o deshabilitar la configuración SAML del tenant
http
PATCH /v1/sso/idp-configSolo se acepta enabled. Habilitar vuelve a comprobar que cada certificado almacenado sigue siendo válido ahora (422 en caso contrario). 404 cuando el tenant no tiene configuración. Establecer el valor que ya tiene no escribe nada.
Solo inicio de sesión de account_admin en el dashboard (véase 403).
Cuerpo de la petición (JSON, obligatorio)
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
enabled | boolean | sí |
Respuestas
200
La configuración con su nuevo estado.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
sso_idp_config | SsoIdpConfig | null | sí | |
service_provider | SsoServiceProvider | null | sí |
400 — BadRequest
Petición mal formada (errores de forma/tipo, filtros no válidos, marcas de tiempo erróneas).
json
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403
role_forbidden: cualquier cosa que no sea un inicio de sesión de dashboard (ddu_) cuya persona tenga el rol account_admin — incluidos una clave de API dd_ y un inicio de sesión member; forbidden: un token de widget (ddw_) o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "SSO configuration takes a dashboard login with the account_admin role"
}
}404 — NotFound
Id desconocido, mal formado (no UUID) O id de otro tenant: RLS devuelve cero filas, así que los tres son indistinguibles por diseño (sin fuga de existencia, nunca un 403 para ids ajenos).
json
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}422 — Unprocessable
La forma es correcta, la semántica no (regla de dominio incumplida).
json
{
"error": {
"code": "unprocessable",
"message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
}
}429 — ApiKeyRateLimited
Esta clave de API ha superado su cuota de peticiones, o las claves del tenant en conjunto han superado el techo del tenant (core#2503) (code rate_limited). La petición no se procesó. Espera Retry-After segundos y reintenta; un rechazo no cuenta contra el presupuesto, así que esa espera siempre basta. Acotado por clave de API y, por encima de las claves, por tenant (api_tenant_requests_per_minute): una clave hermana no se ve afectada hasta que las claves del tenant en conjunto alcanzan ese techo. La cuota se aplica a toda operación alcanzable con una clave dd_, así que las operaciones que nombran un 429 más específico (control de llamadas, disposiciones, SMS, …) PUEDEN responder TAMBIÉN este, con sus cabeceras X-RateLimit-*.
json
{
"error": {
"code": "rate_limited",
"message": "API key request quota exceeded; retry after the interval in Retry-After"
}
}Eliminar la configuración SAML del tenant
http
DELETE /v1/sso/idp-configElimina la configuración; su historial de auditoría permanece. La respuesta es la configuración que se eliminó. 404 cuando no hay ninguna.
Solo inicio de sesión de account_admin en el dashboard (véase 403).
Respuestas
200
Eliminada.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
sso_idp_config | object (SsoIdpConfig) | sí | |
sso_idp_config.id | string (uuid) | sí | |
sso_idp_config.idp_entity_id | string | sí | |
sso_idp_config.sso_url | string | sí | |
sso_idp_config.slo_url | string,null | sí | |
sso_idp_config.certificates | arreglo de object | sí | |
sso_idp_config.certificates[].sha256 | string | sí | SHA-256 hexadecimal en minúsculas del DER del certificado; aquello a lo que se fija una firma. |
sso_idp_config.certificates[].not_before | string,null (date-time) | sí | |
sso_idp_config.certificates[].not_after | string,null (date-time) | sí | |
sso_idp_config.require_signed_assertion | boolean | sí | |
sso_idp_config.require_signed_response | boolean | sí | |
sso_idp_config.email_attribute | string,null | sí | |
sso_idp_config.enabled | boolean | sí | |
sso_idp_config.updated_by | string | sí | |
sso_idp_config.created_at | string (date-time) | sí | |
sso_idp_config.updated_at | string (date-time) | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403
role_forbidden: cualquier cosa que no sea un inicio de sesión de dashboard (ddu_) cuya persona tenga el rol account_admin — incluidos una clave de API dd_ y un inicio de sesión member; forbidden: un token de widget (ddw_) o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "SSO configuration takes a dashboard login with the account_admin role"
}
}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"
}
}Registro de auditoría de la configuración SAML del tenant
http
GET /v1/sso/idp-config/eventsLos hechos de solo anexado (created, updated, enabled, disabled, deleted), del más reciente al más antiguo, como máximo 100. Cada uno lleva quién lo hizo y una instantánea con huellas de certificados (nunca PEM).
Solo inicio de sesión de account_admin en el dashboard (véase 403).
Respuestas
200
Los hechos.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
events | arreglo de SsoIdpConfigEvent | sí | |
events[].id | string (uuid) | sí | |
events[].action | created · updated · enabled · disabled · deleted | sí | |
events[].actor | string | sí | |
events[].snapshot | object | sí | |
events[].occurred_at | string (date-time) | sí |
401 — Unauthorized
Clave de API ausente, desconocida o revocada (o, en /v1/admin/*, un token de administración incorrecto o sin definir; en webhooks, una firma incorrecta o ausente). Una sesión del dashboard (ddu_) que un inicio de sesión más reciente de la misma persona haya cerrado responde en cambio con el código session_superseded (indicador de tenant single_session_enforced), para que la pantalla pueda explicar por qué se cerró la sesión; un cierre de sesión, un token vencido o uno desconocido siguen siendo unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403
role_forbidden: cualquier cosa que no sea un inicio de sesión de dashboard (ddu_) cuya persona tenga el rol account_admin — incluidos una clave de API dd_ y un inicio de sesión member; forbidden: un token de widget (ddw_) o un tenant suspendido.
json
{
"error": {
"code": "role_forbidden",
"message": "SSO configuration takes a dashboard login with the account_admin role"
}
}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"
}
}