Error codes

Standard HTTP status codes with a consistent error envelope.
View as Markdown

All error responses share the envelope { message, errors? }. The errors array is only present on 400 Bad Request validation failures.

HTTP status reference

StatusWhenExample message
400Body validation failedValidation failed (see errors[] for fields)
400File upload missing or wrong MIMENo file uploaded · Only JPEG, PNG, WebP, MP4, and PDF files are supported
401No / malformed Authorization headerMissing or invalid Authorization header
401Bearer token doesn’t match any active keyInvalid API key
401Key was revoked from the dashboardAPI key has been revoked
401Key has expiredAPI key has expired
402Prepaid balance below required costInsufficient credit. Required: RMx.xx, available: RMy.yy
402Postpaid spending cap exceededSpending cap exceeded. Accrued: RM…, this broadcast: RM…, cap: RM…
403Caller IP not in API key whitelistRequest IP is not in the API key whitelist
403Channel subscription inactiveClient does not have an active SMS channel subscription
403Same for WhatsAppClient does not have an active WhatsApp channel subscription
404SMS keyword not foundKeyword not found
404Template not found by nameTemplate not found
404WhatsApp template not foundWhatsApp template not found
404Client has no WABA + platform-default disabledNo active WhatsApp configuration found for this client
422WhatsApp template not yet approvedTemplate is not approved (current status: pending)
422META rejected the test sendTest send failed (<META code>): <reason>
429Per-channel per-client rate limit hitToo many requests, please try again later
500Unexpected server errorUnexpected error (no further detail leaked)
500SQS enqueue failed after DB writeFailed to enqueue broadcast — messages marked failed (credit released automatically)

Validation error shape

1{
2 "message": "Validation failed",
3 "errors": [
4 { "field": "campaignName", "message": "Campaign name is required" },
5 { "field": "recipients.0", "message": "Must be a valid phone number (e.g. +60123456789)" },
6 { "field": "scheduledAt", "message": "scheduledAt is required for schedule broadcast" }
7 ]
8}

The field is a dotted JSON path. Array indices appear as numeric segments (recipients.0).

Rate-limit headers

When you hit the per-client rate limit, the 429 response includes a Retry-After header (seconds until the limiter resets). Use it to back off rather than retrying immediately.

Credit safety on enqueue failure

If the API has already debited credit and then the SQS enqueue fails, Teekrr automatically releases the reserved credit and marks the messages as failed. You’ll see 500 — Failed to enqueue broadcast — messages marked failed in this case. No manual ledger correction is needed on your side.