Errors & rate limits
HTTP status codes, retries, and idempotency.
The API uses conventional HTTP status codes and returns a JSON error body:
{
"error": "contact_opt_in_required",
"message": "An active WhatsApp opt-in record is required before sending to this contact."
}Status codes
| Code | Meaning |
|---|---|
200 | Success. |
400 | Invalid request (missing/!malformed fields). |
401 | Missing or invalid API key. |
403 | Key lacks the required scope. |
404 | Resource not found. |
409 | Conflict (e.g. duplicate idempotency key). |
422 | Valid shape but not allowed (e.g. outside the messaging window). |
429 | Rate limited — back off and retry. |
5xx | Transient server error — safe to retry. |
Retries & idempotency
Retry 429 and 5xx responses with exponential backoff. To make retries safe,
send an Idempotency-Key header on writes — Wabery returns the original result
for a repeated key instead of sending twice:
await wabery.messages.send({
channelId: "channel_...",
conversationId: "conversation_...",
text: "Hi",
idempotencyKey: "7c3f-order-2291",
});curl https://api.wabery.com/v1/messages \
-H "Authorization: Bearer $WABERY_API_KEY" \
-H "Idempotency-Key: 7c3f-order-2291" \
-H "Content-Type: application/json" \
-d '{ "channel_id": "channel_...", "conversation_id": "conversation_...", "text": "Hi" }'Rate limits
Limits scale with your plan. When you exceed them you get 429 with a
Retry-After header (seconds) — respect it before retrying.
Webhook deliveries are retried automatically with backoff on non-2xx responses, so a brief outage on your endpoint won't drop events.