Skip to content

Errores y cumplimiento ​

English version: Errors & compliance.

El sobre de error ​

Todo error, en todo endpoint, tiene la misma forma:

json
{
  "error": {
    "code": "conflict",
    "message": "campaign cannot transition from completed to running"
  }
}

code es estable y legible por máquina — ramifica por él. message es para personas y puede reformularse en cualquier momento — nunca lo analices.

HTTPerror.codeSignificado
400bad_requestJSON mal formado, parámetros de consulta incorrectos, tipos de campo erróneos.
400invalidUn campo viola una regla de dominio (error de validación).
401unauthorizedClave API ausente, desconocida o revocada — consulta Autenticación.
403forbiddenCuenta suspendida.
404not_foundId desconocido, mal formado o de otra cuenta — nunca se filtra la existencia.
409conflictViolación de unicidad o de la máquina de estados (nombre duplicado, transición de ciclo de vida inválida, puesto ocupado).
415unsupported_media_typePetición con cuerpo sin un tipo de contenido JSON.
422unprocessableLa forma es correcta, la semántica no (una regla de dominio rota).
422compliance_blockedEl motor de cumplimiento rechazó la operación — ver más abajo.
422sms_optout_notice_missing / sms_content_prohibitedEl cuerpo del SMS se rechazó antes de que corriera ninguna compuerta — ver Revisiones del cuerpo del SMS.
429throttled / sms_spend_cap_exceededFrenos de ritmo y de gasto — ver Límites de uso y topes.
500internalFalla nuestra. Reintenta con backoff y cita el X-Request-ID a soporte.
502provider_error / dial_error / sbc_errorUn tercero (proveedor de SMS, switch, borde SIP) rechazó la operación.

Los códigos propios de un endpoint (p. ej. sms_provider_unconfigured) se documentan en cada respuesta de la referencia de endpoints.

422 compliance_blocked — la plataforma que se niega a infringir la ley ​

Dialer Digital tiene exactamente un camino hacia el teléfono de un consumidor: un motor con compuertas que comprueba consentimiento, estado de no llamar (DNC), horas de silencio, reglas de frecuencia de contacto (Reg F 7-en-7 y post-contacto), límites estatales y las políticas de tu propia cuenta — antes de contactar a ningún proveedor. Tus llamadas a la API pasan por las mismas compuertas que nuestras campañas y nuestro dashboard. No hay parámetro para saltárselas, y no se puede añadir uno por error: el motor falla cerrado.

Números reasignados: se rechaza, no se consulta

Comprobar si un número fue reasignado requiere un proveedor de la base de datos de números reasignados (Reassigned Numbers Database), y hoy la plataforma no consulta ninguno. El motor no toma la consulta ausente por un visto bueno: mientras no haya proveedor configurado, rechaza la marcación o el envío, y lo reporta como tenant_policy. No reporta number_reassigned para este rechazo, porque no se preguntó a ningún registro.

Cuando una compuerta rechaza, recibes:

json
{
  "error": {
    "code": "compliance_blocked",
    "message": "the compliance engine refused this send (dnc_listed)",
    "gate": "dnc_listed",
    "decision_id": "2c1d0e9f-8a7b-6c5d-4e3f-2a1b0c9d8e7f"
  }
}
  • gate — qué regla rechazó: tcpa_no_consent, quiet_hours, regf_7in7, regf_post_contact, state_limit, dnc_listed, consent_revoked, cease_and_desist, number_reassigned o tenant_policy. number_reassigned solo se reporta cuando de verdad se consultó un registro de números reasignados — consulta la advertencia de arriba.
  • decision_id — una cita dentro de la cadena de auditoría de solo anexado. La decisión que bloqueó la operación es evidencia duradera: si un consumidor o un regulador pregunta alguna vez «¿intentaron contactar a esta persona?», la respuesta es un registro verificable, a tu favor.

Un bloqueo es evidencia, no un error

No pasó nada: cero llamadas al proveedor, cero cargos. No reintentes sin cambios: la entrada que se rechazó se rechazará de nuevo, y machacar una compuerta solo añade ruido a tu propio rastro de auditoría. Corrige el estado de fondo (obtén el consentimiento, espera a que pasen las horas de silencio, respeta la revocación) o pasa a la siguiente cuenta. Esto es lo que estás pagando: cada bloqueo es una infracción que nunca llegó a un consumidor, empaquetada para tu defensa en GET /v1/reports/violations-prevented.

Dónde aparecen los bloqueos ​

SuperficieCómo se reporta un bloqueo
SMS individual (POST /v1/sms/messages)422 compliance_blocked con gate + decision_id.
Marcación manual/preview (POST /v1/campaigns/{id}/dial)200 con {"status": "blocked", "reason": "<gate>"} — un resultado válido que el agente debe ver, no un error del servidor.
Marcación automática de campañaLos bloqueos nunca llegan a quien llama a la API; se registran y se agregan en GET /v1/stats/blocked.
Transferencia ciega a un número externo (POST /v1/calls/{call_id}/transfer)422 compliance_blocked; gate dice qué comprobación rechazó. gate: "mid_call": la revisión sobre el consumidor rechazó (consentimiento revocado, cease-and-desist, horas de silencio) — lleva reason_code y rule_id, y queda armado el corte automático de la llamada (auto_hangup_at, cuando está presente, dice cuándo). gate: "pre_dial": se rechazó el nuevo tramo hacia el destino — lleva reason_code, rule_id y, cuando se escribió la fila de evidencia, decision_id; no se marcó nada y el consumidor sigue en la llamada.

Revisiones del cuerpo del SMS ​

Antes de que el motor vea un SMS — antes también de tus frenos de plan, ritmo y gasto — la plataforma lee el cuerpo por sí misma. Dos reglas, tanto en el envío individual como en toda campaña de SMS:

error.codeEl cuerpo…Qué lo arregla
sms_optout_notice_missingno nombra ninguna palabra clave de baja que la plataforma respete en las respuestas entrantes (STOP, ALTO, UNSUBSCRIBE, CANCEL, QUIT, END). Reg F, 12 CFR 1006.6(e).Dile al consumidor cómo parar: «Reply STOP to opt out.»
sms_content_prohibitednombra contenido que los operadores de EE. UU. sancionan con independencia del consentimiento: SHAFT (sex, hate, alcohol, firearm, firearms, tobacco) o alivio de deuda (debt relief, consolidation, forgiveness, credit repair).Reformula el mensaje.
json
{
  "error": {
    "code": "sms_content_prohibited",
    "message": "body must not contain carrier-prohibited content (SHAFT: sex, hate, alcohol, firearms, tobacco; debt relief, consolidation, forgiveness, credit repair)"
  }
}
  • Palabras completas, en cualquier caja. «Essex» no coincide con sex; «Debt-Relief» y «debt relief» coinciden ambos con debt relief. «debt» y «credit» por sí solas son vocabulario ordinario de cobranza y no están en la lista.
  • Bloquea más de lo necesario, a propósito. Una palabra de la lista se rechaza en cualquier sentido — «we hate to bother you» se rechaza. Una infracción de contenido ante el operador se le cobra a la plataforma; reformular te cuesta una frase.
  • No pasó nada. Cero llamadas al proveedor, cero filas en el libro mayor, cero cargos. No hay decision_id ni fila de evidencia: el motor nunca evaluó el envío, así que estos rechazos no aparecen en GET /v1/stats/blocked ni en el reporte de infracciones evitadas.
  • Sin interruptor. Ningún parámetro de cuenta, campaña o petición se salta ninguna de las dos revisiones. Reintentar el mismo cuerpo se rechaza de nuevo.
  • Las campañas reciben la misma respuesta antes. POST y PATCH /v1/campaigns rechazan un sms_body que incumple cualquiera de las dos reglas con 400 invalid y el mismo mensaje, de modo que una campaña nunca arranca con un cuerpo que cada envío rechazaría. Una campaña guardada antes de que existiera una regla deja de enviar, y no acepta ningún cambio (ni siquiera una pausa) hasta que se corrija su sms_body.

Despliegue

sms_content_prohibited está activo desde core v0.76.0 (core#1351, core ADR #181). sms_optout_notice_missing es anterior: ya estaba en core v0.75.0.

Bloqueos tenant_policy y su rule_id ​

tenant_policy cubre más de una regla — el rechazo por números reasignados de arriba es una de ellas. El rule_id de la decisión (GET /v1/compliance/decisions/{id}, y las muestras de GET /v1/reports/violations-prevented) dice cuál. Tres de ellas:

rule_idPor qué se rechazó la llamadaQué lo arregla
tcpa.artificial_voice.optout.v1Una campaña de TTS, IVR o encuesta no tiene optout_digit.Define esa clave en la campaña — consulta campañas de voz artificial.
egress.destination_not_allowed.v1El número está fuera de la lista de destinos permitidos de uno de tus troncales BYOC.Añádelo a la lista, o no está pensado para llamarse — consulta troncales BYOC: lista de destinos permitidos.
egress.destination_allowlist_unavailable.v1No se pudo leer la lista de destinos permitidos, así que la llamada se rechazó en lugar de arriesgarla.Reintenta más tarde; si persiste, contacta a soporte.

Convertir los bloqueos en papeleo ​

  • GET /v1/stats/blocked — conteos por compuerta y una serie diaria, calculados desde la tabla de evidencia de solo anexado.
  • GET /v1/reports/violations-prevented — el reporte empaquetado (JSON o CSV) que puedes entregar a un cliente, un auditor o un tribunal.
  • GET /v1/debts/{id}/defense-packet — el paquete sellado de litigio por cuenta; cada decision_id que hayas recibido es citable contra él.

404 y multitenencia ​

Los ids de otra cuenta, los UUID mal formados y los ids genuinamente desconocidos son todos el mismo 404 not_found. Lo impone la seguridad a nivel de fila de la base de datos — la API no puede decirte si existe el recurso de otra persona, por diseño.

Invitar a un usuario: una sola respuesta para una dirección libre y una ocupada ​

POST /v1/users invita a una persona al dashboard (core v0.77.0, core ADR #182). Sigue la misma regla que el 404 de arriba: nunca te cuenta nada de otra cuenta.

HTTPerror.codeSignificado
202—Invitación registrada: {"status": "invited", "message": "invitation recorded"}. El mismo cuerpo, byte a byte, tanto si la dirección está libre como si ya es usuaria de otra cuenta.
400bad_requestEl cuerpo lleva un password: la contraseña la elige la persona invitada.
403role_forbiddenLa cuenta tiene un account_admin activo y quien llama es un inicio de sesión member o una clave dd_: invitar exige un inicio de sesión account_admin.
409conflictLa dirección ya es usuaria de tu cuenta, activa o deshabilitada.
429rate_limitedTu cuenta envió demasiadas invitaciones en poco tiempo. Reintenta más tarde.

Canjear el enlace (POST /v1/auth/password-reset/confirm) falla de una sola manera: un enlace desconocido, caducado, usado o reemplazado, o uno cuya dirección pasó a ser usuaria entretanto, es el mismo 401 unauthorized. Una contraseña demasiado corta es 422 y deja el enlace válido. El flujo completo, y cómo migrar una integración que todavía envía password, está en Usuarios del dashboard.

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