Skip to content

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.

typeMeaningRetry?
authentication_errorThe key is missing, malformed, unknown, expired or revokedNo, fix the key
permission_errorThe key is valid but lacks the scope, or the subscription lapsedNo
invalid_request_errorSomething about the request is wrongNo, fix the request
rate_limit_errorToo many requestsYes, after Retry-After
api_errorWe failedYes
CodeWhen
400The body is not valid JSON, or contains a field the endpoint does not accept
401Authentication failed
403Missing scope, disallowed IP, or an inactive subscription
404No such resource in your organization
409An idempotency conflict, or a duplicate contact
422The request parsed but a field is missing or wrong
429Rate limited
500Our fault, safe to retry
503A 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.

No key was supplied. Send Authorization: Bearer km_live_....

The key is malformed or was never issued. Check for a truncated copy and paste, which is the usual cause.

The key passed its expiry date, which the message names. Create a new one.

The key was deliberately revoked. This is permanent; create a new one.

The key was switched off. Someone can turn it back on in the dashboard.

An older whm_ key was used against /v1. Those keys carry no scopes and are not accepted on the public API. See Authentication.

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.

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.

The organization’s subscription has lapsed. The API stops with the dashboard; contact whoever manages your account.

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.

A required field is absent. param names it.

A field is present but wrong: a malformed id, a bad timestamp, a value outside the allowed set. param names it.

type on a message send is not one of text, template, image, video, audio or document.

No contact with that id in your organization.

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.

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.

The from value does not match any WhatsApp number in your organization. Call GET /v1/accounts to see the names available.

The organization has no WhatsApp number connected. Connect one in the dashboard.

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.

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.

The same Idempotency-Key was used with a different body, or a request with that key is still running. See Idempotency.

The campaign is in a state it cannot start from. The message names the state.

Add recipients before starting it.

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.

Slow down. Retry-After says by how many seconds. See Rate limits.

We could not check your rate limit, so the request was refused rather than let through unmetered. Retry shortly.

WhatsApp refused something, usually a media file whose format or size it does not accept.

Our fault. The request is safe to retry, and safer still with an Idempotency-Key.