Errors
One envelope, stable codes, a request id on everything.
Every error, from a missing scope to a database that is down, has the same shape. Branch on the code; show the message to a person; keep the request_id for support.
The envelope
{
"error": {
"code": "outside_service_window",
"message": "More than 24 hours have passed since this customer last wrote. Send an approved template instead.",
"details": { "expires_at": null },
"request_id": "2c0d8f0e-6a1b-4c3d-9e8f-7a6b5c4d3e2f"
}
}
codeis stable and machine-readable. Codes are added over time; none is renamed or repurposed without a new API version.messageis a sentence for a person, and may change wording.detailsis optional and specific to the code: field errors forvalidation_failed, the template's status fortemplate_not_approved, the owning contact foridentity_already_claimed.request_idis the same value as theX-Request-IDresponse header, and the id every log line for that request carries.
Codes
| Code | HTTP status | Meaning |
|---|---|---|
unauthenticated | 401 | Authentication is required. |
forbidden | 403 | You do not have permission to perform this action. |
not_found | 404 | The requested resource was not found. |
validation_failed | 422 | The given data was invalid. |
rate_limited | 429 | Too many requests. |
idempotency_conflict | 409 | This idempotency key was used with a different request body. |
identity_already_claimed | 409 | That handle already belongs to another contact. Merge the contacts instead of reassigning it. |
connection_owned_elsewhere | 409 | That account is already connected to another workspace. Disconnect it there first. |
channel_unavailable | 422 | The channel connection is not available. |
provider_error | 502 | The upstream provider returned an error. |
template_not_approved | 422 | The message template is not approved for sending. |
outside_service_window | 422 | The customer service window has expired; send an approved template instead. |
account_suspended | 403 | This account is suspended. |
method_not_allowed | 405 | The HTTP method is not supported for this route. |
payload_too_large | 413 | The request payload is too large. |
internal_error | 500 | An unexpected error occurred. |
bad_request | 400 | The request could not be understood. |
service_unavailable | 503 | The service is temporarily unavailable. Try again shortly. |
google_email_not_invited | 403 | No one in this workspace has been invited with that Google account. Ask an administrator to invite you, then sign in with Google again. |
channel_setup_failed | 422 | The provider refused a step of the channel set-up. |
insufficient_balance | 422 | The wallet cannot cover this. |
invitation_not_found | 404 | This invitation does not exist. |
invitation_expired | 410 | This invitation has expired. |
invitation_revoked | 410 | This invitation was withdrawn. |
invitation_resend_throttled | 429 | This invitation was sent recently. Wait before sending it again. |
google_email_mismatch | 422 | The Google account does not match the invited address. |
invitation_not_accepted | 403 | This account has been invited but the invitation has not been accepted yet. Use the link in the invitation email to set up your sign-in. |
registration_closed | 403 | New sign-ups are closed. |
verification_failed | 422 | That code is wrong or has expired. Request a new one. |
workspace_unavailable | 423 | This workspace is not available right now. |
workspace_suspended | 423 | This workspace is suspended. You can view your data, but changes are paused until billing is settled. |
invalid_tenant_transition | 409 | The workspace cannot move to that state from where it is. |
invalid_transition | 409 | That change is not possible from its current state. |
mode_switch_in_progress | 409 | A platform mode switch is already running. |
ownership_required | 403 | Only the workspace owner can do this. |
feature_not_in_plan | 402 | This feature is not included in your plan. |
limit_reached | 402 | Your plan limit for this has been reached. |
plan_change_refused | 422 | That plan change is not possible. |
plan_in_use | 409 | This plan has subscribers. Archive it instead. |
nothing_owed | 422 | Nothing is owed on this subscription. |
refund_refused | 422 | That refund is not possible. |
method_disabled_by_platform | 422 | That sign-in method is turned off for the whole platform. |
whatsapp_sender_not_configured | 422 | The platform WhatsApp sender is not set up. |
coupon_invalid | 422 | That coupon code is not valid. |
coupon_expired | 422 | That coupon has expired. |
coupon_exhausted | 422 | That coupon has been used up. |
coupon_not_applicable | 422 | That coupon does not apply to this plan. |
coupon_already_used | 422 | This coupon has already been used on this workspace. |
Validation errors
422 validation_failed carries every field problem at once under details, keyed by field, so a form can show all of them:
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid.",
"details": {
"to": ["The to field is required."],
"message.template": ["The message.template field is required when type is template."]
},
"request_id": "…"
}
}
What to retry
| Status | Retry? |
|---|---|
429 |
Yes, after Retry-After seconds. |
500, 502, 503 |
Yes, with backoff and the same Idempotency-Key. |
401, 403 |
No - fix the credential or the scope. |
404, 409, 413, 422 |
No - the request itself is wrong. |
A 502 provider_error means the channel's provider refused or timed out. The message row, if one was created, is marked failed with the platform's code; the raw provider reason is in the dashboard for the business.
OAuth2 token errors
POST /oauth/token speaks the OAuth2 error shape instead, because that is the contract token libraries expect: { "error": "invalid_client", "error_description": "…" } with 400 or 401.
Base URL
https://communication-api.artofluminaire.com
Every response carries X-Request-ID; quote it when you write to support.