Errors
Errors use standard HTTP status codes and always return the same body shape:
{ "error": { "type": "invalid_request_error", "code": "missing_template_parameter", "message": "Template \"order_shipped\" is missing the parameter(s): tracking. It expects: name, tracking.", "param": "parameters", "doc_url": "https://developers.k-message.kerneltics.com/errors#missing-template-parameter" }}Branch on code. It is part of the contract and will never be renamed;
codes are only ever added. message is written for a human reading a log and
may be reworded. param names the offending field when there is one.
type | Meaning | Retry? |
|---|---|---|
authentication_error | The key is missing, malformed, unknown, expired or revoked | No, fix the key |
permission_error | The key is valid but lacks the scope, or the subscription lapsed | No |
invalid_request_error | Something about the request is wrong | No, fix the request |
rate_limit_error | Too many requests | Yes, after Retry-After |
api_error | We failed | Yes |
Status codes
Section titled “Status codes”| Code | When |
|---|---|
400 | The body is not valid JSON, or contains a field the endpoint does not accept |
401 | Authentication failed |
403 | Missing scope, disallowed IP, or an inactive subscription |
404 | No such resource in your organization |
409 | An idempotency conflict, or a duplicate contact |
422 | The request parsed but a field is missing or wrong |
429 | Rate limited |
500 | Our fault, safe to retry |
503 | A dependency is unavailable, safe to retry |
A 404 means “not in your organization”, which covers both a resource that
does not exist and one belonging to another customer. The two are deliberately
indistinguishable: a 403 on someone else’s record would confirm the id is
real and let anyone map out other tenants’ data by guessing.
Authentication codes
Section titled “Authentication codes”missing_credentials
Section titled “missing_credentials”No key was supplied. Send Authorization: Bearer km_live_....
invalid_key
Section titled “invalid_key”The key is malformed or was never issued. Check for a truncated copy and paste, which is the usual cause.
key_expired
Section titled “key_expired”The key passed its expiry date, which the message names. Create a new one.
key_revoked
Section titled “key_revoked”The key was deliberately revoked. This is permanent; create a new one.
key_inactive
Section titled “key_inactive”The key was switched off. Someone can turn it back on in the dashboard.
key_wrong_surface
Section titled “key_wrong_surface”An older whm_ key was used against /v1. Those keys carry no scopes and are
not accepted on the public API. See Authentication.
ip_not_allowed
Section titled “ip_not_allowed”The key restricts which addresses may use it, and the request did not come from one. The message names the address we saw, which is worth checking against what you expect: behind a NAT or a cloud provider it is often not what you assume.
missing_scope
Section titled “missing_scope”The key is valid but lacks the scope this endpoint needs. The message names it. Add it to the key in the dashboard, or use a key that has it.
subscription_inactive
Section titled “subscription_inactive”The organization’s subscription has lapsed. The API stops with the dashboard; contact whoever manages your account.
Request codes
Section titled “Request codes”invalid_body
Section titled “invalid_body”The body is not valid JSON, or it contains a field this endpoint does not
accept. Unknown fields are rejected rather than ignored: writing phone
instead of to should tell you the field name is wrong, not that to is
missing from a body which visibly contains a number.
missing_field
Section titled “missing_field”A required field is absent. param names it.
invalid_field
Section titled “invalid_field”A field is present but wrong: a malformed id, a bad timestamp, a value outside
the allowed set. param names it.
unsupported_type
Section titled “unsupported_type”type on a message send is not one of text, template, image, video,
audio or document.
contact_not_found
Section titled “contact_not_found”No contact with that id in your organization.
template_not_approved
Section titled “template_not_approved”Meta has not approved this template yet. The message names the template and its
current status. PENDING means wait; REJECTED means fix and resubmit it in
the dashboard.
missing_template_parameter
Section titled “missing_template_parameter”The template declares variables you did not supply. The message lists what is missing and what the template expects.
This check exists because WhatsApp does not do it. A missing variable is delivered to your customer as an empty gap in the sentence and the send reports success, so nobody notices until a customer does.
account_not_found
Section titled “account_not_found”The from value does not match any WhatsApp number in your organization. Call
GET /v1/accounts to see the names available.
no_account_configured
Section titled “no_account_configured”The organization has no WhatsApp number connected. Connect one in the dashboard.
marketing_opt_out
Section titled “marketing_opt_out”The contact opted out of marketing, and the template’s category is MARKETING.
This is refused as a matter of law, not preference. Utility and authentication
templates still reach them.
contact_exists
Section titled “contact_exists”A contact with that phone number already exists. The message includes its id. Nothing is merged automatically, because an integration creating the same customer twice has a bug worth seeing.
idempotency_key_reused
Section titled “idempotency_key_reused”The same Idempotency-Key was used with a different body, or a request with
that key is still running. See Idempotency.
campaign_not_startable
Section titled “campaign_not_startable”The campaign is in a state it cannot start from. The message names the state.
campaign_has_no_recipients
Section titled “campaign_has_no_recipients”Add recipients before starting it.
campaign_not_editable
Section titled “campaign_not_editable”Recipients can only be added while a campaign is draft or paused. Adding to a running campaign would race the workers sending it, so whether the new rows go out would depend on timing.
Rate and server codes
Section titled “Rate and server codes”rate_limited
Section titled “rate_limited”Slow down. Retry-After says by how many seconds. See
Rate limits.
rate_limiter_unavailable
Section titled “rate_limiter_unavailable”We could not check your rate limit, so the request was refused rather than let through unmetered. Retry shortly.
upstream_failure
Section titled “upstream_failure”WhatsApp refused something, usually a media file whose format or size it does not accept.
internal_error
Section titled “internal_error”Our fault. The request is safe to retry, and safer still with an
Idempotency-Key.