Skip to content

Límites de uso y topes ​

English version: Rate limits & caps.

Dialer Digital frena en más de un sitio, y el code del sobre de error estándar siempre dice con qué freno chocaste. Esta página recorre los que es más probable que tengas que tener en cuenta al diseñar; no es un censo cerrado de todos los 429 que puede devolver la API. Un 429 lleva la cabecera Retry-After solo donde la plataforma sabe cuándo vuelve la capacidad — la cuota por clave de abajo y los frenos de ventana fija. Todos los demás 429 la omiten a propósito, y tu cliente es dueño de su propio backoff.

La regla que sobrevive a los códigos nuevos

Reintenta cualquier 429 con backoff y jitter — salvo los topes de gasto, que reintentar no puede levantar. Ramifica por esa distinción y no por una lista de códigos: un cliente que enumera los códigos de hoy deja de hacer bien el backoff en cuanto sale uno nuevo.

429 rate_limited — la cuota de peticiones por clave ​

Publicado en core v0.86.0 (publicado como v0.86.0-prod-0.1)

La cuota por clave y las cabeceras Retry-After / X-RateLimit-* (core#1471, ADR #193) están en el contrato publicado, así que aparecen en la Referencia de la API.

Cada clave API de tenant (dd_…) tiene su propio presupuesto de peticiones en /v1: dos claves de la misma cuenta nunca comparten uno, de modo que una integración descontrolada en una clave no puede dejar sin recursos a la hermana. Es una red de seguridad contra bucles desbocados, no un límite de plan — el valor por defecto es generoso (3000 peticiones por minuto por clave, 50 por segundo sostenidos con una ráfaga de un minuto), y solo se cuenta después de que tu clave se autentique, así que una clave incorrecta no gasta nada.

Cada respuesta a una petición dd_ contada dice en qué punto estás (el hangup exento, y una petición admitida porque el limitador no pudo decidir, no llevan cabeceras X-RateLimit-*):

CabeceraSignificado
X-RateLimit-LimitPeticiones por minuto para esta clave.
X-RateLimit-RemainingPeticiones que quedan ahora mismo (0 en un 429).
X-RateLimit-ResetSegundos enteros hasta que el presupuesto vuelva a estar lleno.
Retry-AfterSolo en el 429: segundos enteros (al menos 1) hasta que quepa una petición.
http
HTTP/1.1 429 Too Many Requests
retry-after: 1
x-ratelimit-limit: 3000
x-ratelimit-remaining: 0
x-ratelimit-reset: 60
json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}
  • La petición rechazada no se procesó, y un rechazo no gasta presupuesto: esperar Retry-After segundos siempre basta. Reintenta la misma petición tras esa espera.
  • No se cuentan: POST /v1/calls/{call_id}/hangup (rechazar un corte dejaría viva una llamada), las sesiones de usuario de dashboard (ddu_) y de widget (ddw_) (tienen sus propios frenos, abajo), los webhooks entrantes y las rutas de salud.
  • Por nodo. La cuenta vive en la memoria del nodo que te atendió, así que detrás de varios nodos el techo efectivo puede ser mayor que la cifra; no la uses como medidor exacto.
  • El límite es ajustable por cuenta — pídelo a tu gerente de cuenta. Un valor que no sea un número positivo vuelve al valor por defecto; no existe ningún ajuste que signifique «ilimitado».
  • Si el propio limitador no puede decidir, la petición se admite en vez de rechazarse: la cuota acota la carga, no es un control de seguridad.

429 throttled — frenos de ritmo ​

Las operaciones que llegan a la tubería de telefonía o de SMS se frenan cuando llegan más rápido de lo que la tubería las acepta:

json
{
  "error": {
    "code": "throttled",
    "message": "dial throttled (rate)"
  }
}
  • Dónde: POST /v1/campaigns/{id}/dial (marcaciones manuales/preview) y POST /v1/sms/messages.
  • Reintentable: sí. Haz backoff exponencial con jitter (p. ej. 1 s → 2 s → 4 s …, con tope) y reintenta la misma petición. No hay Retry-After: la tubería de telefonía no sabe cuándo tendrá sitio, y un número inventado sería peor que ninguno.
  • En SMS, los reintentos deben reutilizar el mismo client_ref — es la clave de idempotencia, y un client_ref repetido se acusa sin contactar dos veces al proveedor. Nunca generes un client_ref nuevo para un reintento.

429 rate_limited — frenos de ritmo de peticiones en acciones de agente ​

json
{
  "error": {
    "code": "rate_limited",
    "message": "call control rate limit exceeded; retry later"
  }
}

Los verbos de control de llamada y las disposiciones llevan su propio freno de ritmo de peticiones. A diferencia de throttled, que trata del apetito de la tubería de telefonía, este acota la rapidez con que un mismo principal puede actuar:

  • El presupuesto es por el principal durable más específico — agente, si no usuario, si no tenant — nunca por tenant cuando existe uno más estrecho, de modo que una página desbocada no pueda frenar a sus agentes hermanos. Durable es la palabra que sostiene todo: el presupuesto sobrevive a la reautenticación, así que volver a iniciar sesión emite un token nuevo pero no compra un presupuesto fresco.
  • Los cuatro interruptores de control de llamada comparten un presupuesto. hangup está exento y nunca responde 429 — rechazar un corte dejaría viva una llamada que su operador pidió terminar.
  • Las disposiciones tienen su propio presupuesto, más estrecho. Lo que acota es el crecimiento de filas durables: cada disposición aceptada inserta una fila de corrección en el historial de intentos de solo anexado.
  • El rechazo ocurre antes de la consulta, así que la respuesta es idéntica exista o no el call_id o el id del intento — un cliente frenado no puede usarla para sondear qué existe.

Reintentar en la siguiente ventana es la respuesta correcta, y el 429 dice cuándo se abre: Retry-After es el tiempo que queda de la ventana actual. Los mismos frenos de ventana fija protegen el inicio de sesión (incluido el paso del segundo factor), la emisión de tokens de widget, las credenciales SIP del widget, las invitaciones de usuarios, la confirmación de contraseña y la autenticación de administradores de la plataforma, y todos responden con la misma cabecera.

Techos de concurrencia — también 429, también reintentables ​

Tres 429 más no son frenos de ritmo en absoluto: dicen «este recurso está ocupado ahora mismo», y cada uno te dice exactamente qué tiene que pasar para que un reintento tenga éxito.

codeDóndeReintenta cuando
export_in_progressExportación de grabaciones/CDRTermina la exportación en curso de tu tenant. Una repetición idéntica toma entonces el camino de reutilización y responde desde el archivo que produjo.
supervision_capacityInicio de la supervisión de una llamadaTermina una llamada supervisada. Es la cuota de supervisión del tenant — su propio techo, nunca el de marcación.
too_many_connectionsPOST /v1/ws/ticket y el handshake de /v1/wsSe libera una conexión del piso en vivo. Acotado por token de widget y por tenant — abajo, cuál está lleno y qué lo libera.

Las cubre la regla de arriba del todo: haz backoff y reintenta. No las trates como fatales, y no reconectes en un bucle cerrado contra too_many_connections.

429 too_many_connections — los topes de sockets del piso en vivo ​

El piso en vivo (/v1/ws) tiene dos topes, comprobados en este orden:

  1. Por token de widget — solo en el carril de agente (un token ddw_): dos sockets vivos por defecto, uno para el agente más uno de solape mientras se recarga una página.
  2. Por tenant — derivado de tus agentes activos, así que crece con los puestos que operas; un tenant pequeño se sitúa en un piso fijo.

Ambos topes se comprueban dos veces, y las dos respuestas no llevan el mismo detalle:

DóndeCuerpo¿Lo puede leer un navegador?
POST /v1/ws/ticketerror.scope nombra el tope lleno: widget_token o tenant. No se emite ticket.Sí — es una respuesta HTTP normal.
El handshake GET /v1/wsEl mismo code, sin scope.No. El WebSocket de un navegador ve un handshake rechazado como un cierre 1006 pelado, indistinguible de una caída de red.
json
{
  "error": {
    "code": "too_many_connections",
    "message": "live-floor connection limit reached for this widget token",
    "scope": "widget_token"
  }
}

La respuesta del ticket es un aviso temprano, no una reserva: un ticket emitido cuando había sitio igualmente se rechaza si el tope se llena antes de canjearlo — en el handshake (el navegador ve 1006) o, en una carrera estrecha, con un socket que se abre y se cierra al instante con 1013 (Try Again Later). Ramifica por el scope del ticket; tras un 1006 o un 1013, emite un ticket nuevo con backoff, y su respuesta te dirá si la causa es un tope.

Qué hacer con cada scope:

  • widget_token — este agente ya tiene abiertos sus sockets, casi siempre en otra pestaña o ventana del widget. Cierra esa otra sesión; no reintentes en bucle, porque nada de tu lado libera un hueco hasta que se cierre uno. Díselo al agente, en vez de mostrar un genérico «reconectando».
  • tenant — el piso en vivo de tu cuenta está lleno. Se libera a medida que se cierran otros sockets, así que haz backoff con jitter como con cualquier otro 429; si sigue ocurriendo, díselo al administrador de tu cuenta — el tope sigue a tus agentes activos.

En ambos casos emite un ticket nuevo por intento: un ticket vale para un solo handshake.

429 sms_spend_cap_exceeded — el presupuesto mensual de SMS ​

json
{
  "error": {
    "code": "sms_spend_cap_exceeded",
    "message": "month-to-date SMS spend is at the tenant cap"
  }
}

Tu cuenta tiene un tope de gasto de SMS en el mes en curso (sms_monthly_budget_usd en GET /v1/me). Cuando el gasto acumulado del mes lo alcanza, los envíos se detienen antes de contactar a ningún proveedor — no sale ningún mensaje, no se factura nada.

  • Reintentar no sirve hasta que se suba el tope o cambie el mes natural. Trátalo como un evento de presupuesto, no como un error transitorio: avisa a una persona.
  • Los topes se fijan con tu gerente de cuenta (viven en la superficie de administración, de modo que una clave API filtrada no pueda subir tu propio presupuesto).

Por qué existen los topes ​

Los autodialers y los emisores masivos de SMS son amplificadores de gasto — un mal bucle o una mala lista pueden quemar el presupuesto de un mes en una hora. Los topes son una función de integridad de costes: la plataforma impone el techo aguas arriba del dinero, con la misma lógica de fallar cerrado con que impone el cumplimiento. Los topes de tu cuenta hoy:

TopeDónde lo vesQué detiene
sms_monthly_budget_usdGET /v1/meEnvíos de SMS más allá del presupuesto mensual (429 sms_spend_cap_exceeded).
ai_monthly_budget_usdGET /v1/meMinutos de voz con IA más allá del presupuesto mensual.
ai_max_call_secondsGET /v1/meCualquier llamada de IA individual que se desboque.

El uso acumulado del mes se puede consultar en GET /v1/stats/sms-usage y GET /v1/stats/ai-usage — sondéalos si quieres tus propias alertas antes de que el freno actúe.

Alertas de gasto — quién las recibe y cuándo ​

Aparte de esos topes, la plataforma te avisa cuando tu gasto se acerca al límite de tu plan (el saldo o límite de crédito que reporta GET /v1/billing/account):

  • Cuándo. Una comprobación en segundo plano corre cada pocos minutos y registra cada umbral la primera vez que tu gasto lo cruza: por defecto al 80 %, 90 % y 100 % del límite. Los umbrales se fijan con tu gerente de cuenta; el 100 siempre se conserva, aunque se quite. Se rearman cada vez que la cuenta vuelve a recibir fondos — una recarga en un plan prepago, un pago que reduce lo adeudado en uno pospago.
  • Qué se registra. Cada cruce se escribe una sola vez, tal como era en ese momento (gastado, límite, modo de aplicación), y lo lista GET /v1/billing/alerts. Un aviso del 80 % sigue siendo un aviso del 80 % aunque después recargues.
  • Quién es notificado. Solo cuando el modo de aplicación de tu plan es graduated; bajo hard_block los cruces se registran pero no se envían. Donde la entrega de alertas por correo esté habilitada en tu despliegue, la alerta va por correo a la unión de la dirección de alertas configurada en tu plan (alert_email) y de todos los administradores de cuenta activos de tu cuenta, sin duplicados (las direcciones se comparan sin distinguir mayúsculas), hasta 20 destinatarios — primero la dirección configurada. La dirección configurada añade un destinatario; nunca reemplaza a tus administradores. Una cuenta sin administrador activo pero con un alert_email igualmente la recibe.
  • Qué no hace. Una alerta nunca bloquea nada: detenerse en el límite es trabajo del modo de aplicación, no de la alerta. La entrega por correo es de mejor esfuerzo (un intento, sin cola de reintentos), así que trata GET /v1/billing/alerts como el registro, y el correo como el aviso.

La dirección de alertas y los umbrales viven en la superficie de administración, igual que los topes: pide a tu gerente de cuenta que los fije o los cambie.

Límites a nivel de petición ​

LímiteValorComportamiento en el límite
Tamaño del cuerpo de la petición8 MiBSe rechaza. Divide las importaciones grandes en lotes.
Tamaño del cuerpo de un webhook1 MB413.
Endpoints de webhook saliente habilitados20 por tenant422 unprocessable en POST /v1/webhooks o al rehabilitar (PATCH enabled:true); los existentes se conservan. Mira Webhooks salientes.
Tamaño de página (parámetro limit)máx. 1000, por defecto 100Los valores por encima del máximo se rechazan como bad_request.

Ser un buen ciudadano ​

  • Pagina con cursores (limit + next_cursor) en vez de límites enormes.
  • Prefiere el piso en vivo por WebSocket al sondeo para el estado en tiempo real (presencia de agentes, ciclo de vida de llamadas, ritmo).
  • Importa leads por lotes (POST /v1/debts/import acepta arreglos y es idempotente) en vez de importar una deuda por petición.
  • Backoff con jitter en todo 429 salvo sms_spend_cap_exceeded, y en todo 5xx. Ramifica según que el código sea un tope de gasto, no según una lista que tengas que mantener sincronizada — consulta la regla al principio de esta página.

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