The API uses standard HTTP status codes. 2xx means success; anything else comes with a JSON body that explains what went wrong.
Error format#
{
"statusCode": 409,
"error": "Conflict",
"code": "NUMBER_NOT_CONNECTED",
"message": "This number isn't connected, so you can't send messages right now"
}codeis stable: it never changes once published. Branch oncodein your software, not on the HTTP status or the message.messageis written for people. It's safe to show it to your users or log it, but its wording may improve over time.
Validation errors#
When the request body or query is invalid, you get 400 with the code VALIDATION_FAILED and a fieldErrors object: each invalid field and what's wrong with it.
{
"statusCode": 400,
"error": "Bad Request",
"code": "VALIDATION_FAILED",
"message": "Validation failed",
"fieldErrors": {
"to": ["Enter the number in international format, e.g. +14155550123"],
"text": ["Message cannot be empty"]
}
}Out of credits#
When your balance is too low to send, you get 402 with the code CREDITS_REQUIRED:
{
"statusCode": 402,
"error": "Payment Required",
"code": "CREDITS_REQUIRED",
"message": "You're out of credits. Top up on the Billing page to continue."
}Error codes#
| Code | Status | Meaning | What to do |
|---|---|---|---|
VALIDATION_FAILED | 400 | A field is missing or invalid. | Fix the fields listed in fieldErrors. |
INVALID_PHONE | 400 | to or phone isn't a phone number in international format. | Send it as +14155550123. |
INVALID_FROM | 400 | from isn't a phone number in international format. | Send it as +12125550147. |
NUMBER_REQUIRED | 400 | No from or numberId, and your key can use more than one number. | Say which number sends: from or numberId. |
FILE_NOT_UPLOADED | 400 | The upload never finished, or it expired. | Upload the file again. |
FILE_TYPE_NOT_ALLOWED | 400 | A photo that isn't JPG or PNG. | Convert it, or send it as a document. |
API_KEY_MISSING | 401 | No Authorization header. | Send Authorization: Bearer <your key>. |
API_KEY_INVALID | 401 | The key is wrong or was revoked. | Create a new key in Settings → Developer. |
CREDITS_REQUIRED | 402 | Not enough credits to send or connect. | Top up on the Billing page. |
NUMBER_NOT_FOUND | 404 | The number isn't in your business, or your key can't use it. | Check the id, or the key's numbers. |
MESSAGE_NOT_FOUND | 404 | The message isn't in your business, or belongs to a number your key can't use. | Check the id. |
MESSAGE_HAS_NO_FILE | 404 | The message has no photo or document, or it isn't ready yet. | Wait for media.status to be READY. |
WEBHOOK_NOT_FOUND | 404 | The webhook isn't in your business, or your key can't see it. | Check the id. |
NUMBER_NOT_CONNECTED | 409 | The number isn't connected to WhatsApp right now. | Reconnect it (scan the QR again). |
NUMBER_ALREADY_CONNECTED | 409 | You asked for a new QR for a number that is already connected. | Nothing to do. |
NO_NUMBER | 409 | Your key can't use any number yet. | Connect a number. |
FILE_TOO_LARGE | 413 | The file is over the size limit. | Photos up to 16 MB, documents up to 100 MB. |
OUTSIDE_REPLY_WINDOW | 403 | The recipient hasn't messaged this number in the last 30 days. API numbers only reply. | Wait until they message you, then reply. |
NOT_ON_WHATSAPP | 422 | The recipient's phone number isn't on WhatsApp. | Don't retry; reach this person another way. |
RATE_LIMITED | 429 | Over the rate limit. | Wait, then retry. |
WHATSAPP_TIMEOUT | 503 | WhatsApp didn't answer in time. | Retry in a few seconds. |
INTERNAL_ERROR | 500 | Something went wrong on our side. | Retry later. |
Errors without a more specific code use a general one for their status: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, PAYLOAD_TOO_LARGE or SERVICE_UNAVAILABLE. New codes may be added; treat a code you don't know by its HTTP status.
const response = await fetch("https://api.waconnect.ai/v1/messages", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.WACONNECT_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ from: "+12125550147", to: "+14155550123", text: "Your table is ready" }),
})
if (!response.ok) {
const error = await response.json()
switch (error.code) {
case "NOT_ON_WHATSAPP":
await sendSms("+14155550123", "Your table is ready")
break
case "NUMBER_NOT_CONNECTED":
await alertTeam("Reconnect the front desk WhatsApp number")
break
default:
throw new Error(error.message)
}
}Status codes#
| Status | Meaning | Typical cause |
|---|---|---|
200 / 201 | OK | The request worked. |
202 | Accepted | A message or removal was queued and happens a moment later. |
400 | Bad request | A field is missing or invalid — see fieldErrors. |
401 | Unauthorized | Missing, invalid or revoked API key. |
402 | Payment required | Not enough credits. |
404 | Not found | The id doesn't exist in your business, or your key can't use it. |
409 | Conflict | The number isn't connected, or is already connected. |
413 | Payload too large | The file is over the size limit. |
422 | Unprocessable | The phone number isn't on WhatsApp. |
429 | Too many requests | Over the rate limit. |
503 | Service unavailable | WhatsApp didn't answer in time — retry in a few seconds. |
Retrying safely
429, 503 and network errors are safe to retry. When retrying POST /v1/messages, send an Idempotency-Key so a retry can never send the message twice.