Saltar al contenido
whatsapi.sh
Tema
Docs

Errores que te dicen qué hacer

Cada error es un envelope JSON con un código estable, un mensaje que dice cómo resolverlo, y Retry-After en todo lo que se soluciona esperando. Esta página es la tabla que tu lógica de reintentos necesita.

La forma

429 Too Many Requests
Retry-After: 42

{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Daily send limit reached (20/day on Free). It clears at midnight, or Starter sends 60/day",
    "messageKey": "serverErrors.dailyCapReached",
    "details": { "scope": "day", "limit": 20, "resetAt": "2026-08-25T03:00:00.000Z", "retryAfterSecs": 42 }
  }
}

messageKey es estable, para que lo traduzcas a tu manera. message es el texto en inglés, de respaldo. Los rechazos que piden un upgrade nombran el plan más barato que lo levanta, porque un error sin salida no es un mensaje de error.

Todos los códigos

400 VALIDATION_ERRORAlgún campo vino mal. `details` lista rutas y reglas, nunca tus valores.
401 UNAUTHORIZED / INVALID_TOKENLa clave falta, está mal o fue revocada. Manda `Authorization: Bearer wa_…`.
402 PAYMENT_REQUIREDUn límite que solo levanta una compra, como más números o envíos en lote. Esperar no lo resuelve.
403 FORBIDDENLa cuenta está suspendida.
404 NOT_FOUNDEse recurso no existe, o no es tuyo.
409 NUMBER_NOT_CONNECTEDEl número no tiene una sesión de WhatsApp que permita enviar. Abre Números, revisa WhatsApp en el teléfono y reconecta. Reintentar el envío ahora no sirve.
409 CONFLICTLa petición necesita una acción o ya está en ese estado. Un número restringido requiere `recoveryConfirmed: true`, solo después de comprobar que WhatsApp funciona en el teléfono. Una petición con el mismo `Idempotency-Key` puede seguir en curso: espera el `Retry-After` y repítela con la misma clave.
422 IDEMPOTENCY_KEY_REUSEDEste `Idempotency-Key` ya se usó en otra petición. Envía uno nuevo. Esperar no lo resuelve.
429 RATE_LIMIT_EXCEEDEDDemasiado rápido. `Retry-After` dice exactamente cuánto esperar.
429 QUOTA_EXCEEDEDLímite diario, cuota mensual, o el límite de conversaciones nuevas de un número. `details.scope` dice cuál: `day`, `month` o `newConversations`. `resetAt` dice cuándo se libera.
429 PAIRING_COOLDOWNDemasiadas solicitudes de código de vinculación para este número. `Retry-After` dice cuándo puedes pedir otro. No es un tiempo fijo de restricción.
502 GATEWAY_ERRORWhatsApp lo rechazó más arriba. Reintentar podría funcionar.
504 GATEWAY_TIMEOUTWhatsApp no confirmó a tiempo. Revisa el chat y, si aparece `details.messageId`, sigue ese id en el webhook `message.status` antes de volver a enviar. Un reintento con el mismo `Idempotency-Key` recibe la misma respuesta, sin un segundo envío.
503 SERVICE_UNAVAILABLEUna dependencia está caída. Reintenta en un momento.

Cuando el número se cae

El teléfono puede perder la conexión y una cuenta nueva puede no tener número todavía. Los nuevos envíos responden 409 NUMBER_NOT_CONNECTED sin cobrar nada. Los mensajes ya en cola que aún no salieron se pausan hasta que el número reconecte. No se marcan como fallidos solo porque se desconectó, y vuelven a salir con el intervalo configurado. message.failed indica un envío que no puede continuar o cuyo resultado no se conoce, no una cola pausada.

  • Ten un segundo canal. Lo que alguien necesita recibir, mándalo también por correo, o por correo cuando WhatsApp lo rechace.
  • Antes de ofrecer algo que solo funciona por WhatsApp, como un código de acceso, comprueba que haya un número conectado. Guarda la respuesta un minuto y descártala cuando un envío vuelva con NUMBER_NOT_CONNECTED.
  • Si no hay número conectado, oculta esa opción y lleva a la persona al correo. No muestres un botón que solo puede fallar.
  • Si tienes webhook, number.disconnected llega cuando el número sigue caído tras 15 minutos de reintentos, y number.connected cuando vuelve.
GET /v1/numbers
import { Whatsapi } from "@whatsapi.sh/sdk";

const wa = new Whatsapi({ apiKey: process.env.WHATSAPI_KEY! });

const numbers = await wa.listNumbers();
const whatsappReady = numbers.some((n) => n.status === "connected");

Reglas para reintentar

Respeta Retry-After en un 429. Nunca reintentes un 402 de forma automática, porque se resuelve comprando, no esperando. En un 504, no reenvíes a ciegas: el id del mensaje en details va a aparecer en tu webhook message.status si llegó, y reenviar sin revisar es como una persona recibe el mismo mensaje dos veces. Para reintentar un envío sin riesgo, mándalo con el header Idempotency-Key y repítelo con el mismo valor: recibes la primera respuesta en lugar de un segundo mensaje. Los límites de tasa son protección, no burocracia, y cómo evitar el baneo explica cómo quedarte por debajo desde el principio.