Skip to content

API conventions

Errors

Every failed request returns the same error object. This page explains its fields and lists every error code with its cause and the fix.

The error object#

The API uses conventional HTTP status codes: 2xx for success, 4xx for a problem with the request and 5xx for a problem on the OmniMessage side. Every non-2xx response has a JSON body with a single error object.

400 Bad Request
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_missing",
    "message": "text.body is required.",
    "param": "text.body",
    "details": [
      {
        "param": "text.body",
        "code": "missing_field",
        "message": "Required"
      }
    ],
    "request_id": "req_0aB3cD6eF9gH2iJ5kL8m",
    "doc_url": "https://omnimessage.co/docs/errors#parameter_missing"
  }
}
FieldAlways presentDescription
typeYesThe category of the error. One per HTTP status class; see the table below.
codeYesA stable, machine-readable code. Branch on this.
messageYesA human-readable explanation for logs and developers. The wording may change, so do not parse it or show it to end users.
paramNoThe request field the error relates to, as a path such as text.body.
detailsNoOne entry per validation failure when several fields are wrong, each with param, code and message.
request_idYesThe ID of the request, also sent in the X-Request-Id header. Include it when you contact support.
doc_urlYesA link to the entry for code on this page.

Error types#

Handle errors#

  • Branch on type for the general strategy and on code for specific cases. Never branch on message.
  • Treat unknown codes as their type. New codes can be added to an existing type without a new API version.
  • 4xx errors, except 429 and idempotency_key_in_use, will not succeed if you repeat the same request. Fix the cause first.
  • 429 and 5xx errors are transient. Retry with exponential backoff, and reuse the Idempotency-Key on POST requests so that a retry cannot send twice.
  • Log request_id with every failure.
const response = await fetch('https://api.omnimessage.co/v1/messages', request);
const data = await response.json();

if (!response.ok) {
  const { type, code, message, param, request_id } = data.error;

  switch (type) {
    case 'invalid_request_error': // a bug in the request: log it, do not retry
      throw new Error(`Bad request (${code}${param ? ` at ${param}` : ''}): ${message}`);
    case 'billing_error': // out of funds: pause sending
      await pauseOutboundQueue();
      break;
    case 'rate_limit_error': // slow down
      await sleep(Number(response.headers.get('Retry-After') ?? 1) * 1000);
      break;
    case 'api_error': // transient: retry with the same Idempotency-Key
      await retryLater();
      break;
    default:
      console.error(`OmniMessage error ${code} (${request_id}): ${message}`);
  }
}

invalid_request_error#

HTTP 400. The request is malformed or a parameter is missing or invalid.

parameter_missing#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
A required field, query parameter or content object is absent. param names it; details lists every missing field.
Fix
Add the field. For POST /v1/messages, make sure the content object sits under the key named by type.
Retry
Retry only after fixing the cause.

parameter_invalid#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
A field has the wrong type, format or length, an enum value is unknown, or a limit was exceeded (for example more than 100 messages in a batch).
Fix
Correct the value named in param. Check details for the individual failures.
Retry
Retry only after fixing the cause.

unsupported_message_type#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The message type is not in the capabilities of the channel.
Fix
Read capabilities from GET /v1/channels/{id} and send one of the listed types, or use another channel.
Retry
Retry only after fixing the cause.

invalid_json#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The body could not be parsed, or Content-Type is not application/json.
Fix
Send a UTF-8 JSON body with Content-Type: application/json.
Retry
Retry only after fixing the cause.

webhook_endpoint_limit_reached#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The account already has 500 webhook endpoints in the mode of the API key (live and test are counted separately). Automation tools create one per Zap, workflow or scenario.
Fix
Delete endpoints you no longer need (GET /v1/webhook_endpoints shows source and metadata of each), or subscribe an existing endpoint to more event types.
Retry
Retry only after fixing the cause.

webhook_url_not_public#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The webhook URL points at a loopback, private or link-local address (localhost, 127.0.0.1, 10.x, 192.168.x, *.local, *.internal), or does not use https. Deliveries are sent from our servers and cannot reach such a host.
Fix
Use a public https URL. For a self-hosted tool behind a firewall, expose its webhook path through a tunnel or reverse proxy and set that public address as the tool's webhook URL.
Retry
Retry only after fixing the cause.

integration_source_limit_reached#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The account already has 50 integration sources in the mode of the API key.
Fix
Delete sources of stores or sites that are no longer connected.
Retry
Retry only after fixing the cause.

payload_too_large#

HTTP status
413 Payload too large
Type
invalid_request_error
Cause
The body exceeds the limit of the endpoint: 256 KB for automation events and native webhooks, 1 MB elsewhere.
Fix
Send less in one request: fewer events per batch, or a smaller data object.
Retry
Retry only after fixing the cause.

contact_limit_reached#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The account holds as many contacts as its limit allows, so a new one cannot be created. Updates of existing contacts still work.
Fix
Delete contacts you no longer message, or ask support to raise the limit of the account.
Retry
Retry only after fixing the cause.

campaign_limit_exceeded#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
The audience of the campaign is larger than the per-campaign limit, or launching it would exceed the number of recipients the account may reach within 24 hours.
Fix
Narrow the audience or split it over several campaigns and days. Support can raise the limits of an account.
Retry
Retry only after fixing the cause.
HTTP status
400 Bad request
Type
invalid_request_error
Cause
A campaign was launched, or a contact import started, without the declaration that the recipients gave permission to be contacted.
Fix
Send consent_declared: true once you have that permission. The declaration is recorded with the campaign or import.
Retry
Retry only after fixing the cause.

import_file_invalid#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
Console only. An uploaded contact file is empty, too large, not a CSV or .xlsx file, has no header or data rows, has more rows than allowed, or is an archive that unpacks to more data than is accepted.
Fix
Upload a .csv or .xlsx file with a header row, within the size and row limits shown in the import dialog.
Retry
Retry only after fixing the cause.

token_invalid#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
Console only. An email verification, password reset or invitation link was already used, has expired or is malformed.
Fix
Request a new link.
Retry
Retry only after fixing the cause.

oauth_pending_expired#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
Console only. A social sign-in that still needed an email address or a password confirmation was not finished within 15 minutes, was already finished, or was started in another browser.
Fix
Start the sign-in again from the login or signup page.
Retry
Retry only after fixing the cause.

api_key_limit#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
Console only. The account already has 50 API keys that are not revoked.
Fix
Revoke a key that is no longer used, then create the new one.
Retry
Retry only after fixing the cause.

signature_invalid#

HTTP status
400 Bad request
Type
invalid_request_error
Cause
Payment provider callback only. The Stripe-Signature header is missing or does not match the request body.
Fix
Not returned to API clients. Check the webhook signing secret configured for the payment provider.
Retry
Retry only after fixing the cause.

authentication_error#

HTTP 401. The API key is missing or not usable.

webhook_signature_invalid#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
Native platform webhooks only (POST /v1/ingest/{platform}/{source_id}). The signature header (X-WC-Webhook-Signature, X-Shopify-Hmac-Sha256) is missing or was not computed over this body with the signing secret of the source.
Fix
Paste the signing secret of the source into the platform's webhook settings (WooCommerce), or store the platform's own signing key on the source (Shopify). Rolling the source key issues a new secret.
Retry
Retry only after fixing the cause.

api_key_missing#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
The Authorization header is absent or does not use the Bearer scheme.
Fix
Add Authorization: Bearer om_live_... (or om_test_...) to the request.
Retry
Retry only after fixing the cause.

api_key_invalid#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
The key is malformed, truncated or does not exist.
Fix
Copy the key again from where you stored it. Keys are shown once at creation; if it is lost, create a new one in the console.
Retry
Retry only after fixing the cause.

api_key_revoked#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
The key was revoked in the console.
Fix
Create a new key and deploy it.
Retry
Retry only after fixing the cause.

api_key_expired#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
The key was created with an expiry date that has passed.
Fix
Create a new key and deploy it.
Retry
Retry only after fixing the cause.

session_required#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
Console only (session cookie authentication; never returned by /v1). There is no valid session: the user is signed out or the session expired.
Fix
Sign in again.
Retry
Retry only after fixing the cause.

invalid_credentials#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
Console only (never returned by /v1). The email and password do not match an active user. Also returned with status 400 when the current password is wrong while changing it.
Fix
Check the credentials, or reset the password.
Retry
Retry only after fixing the cause.

hook_token_invalid#

HTTP status
401 Unauthorized
Type
authentication_error
Cause
Delivery layer callback only (never returned by /v1). The callback did not carry the shared gateway token.
Fix
Not returned to API clients.
Retry
Do not retry.

billing_error#

HTTP 402. The account cannot pay for the message.

insufficient_balance#

HTTP status
402 Payment required
Type
billing_error
Cause
No applicable package has credits left and the wallet holds less than the per-message price for the channel type.
Fix
Top up the wallet or buy a package in the console, or enable auto-recharge. The message was not created; send it again once funds are available.
Retry
Retry only after fixing the cause.

permission_error#

HTTP 403. The key is valid but not allowed to do this.

scope_missing#

HTTP status
403 Forbidden
Type
permission_error
Cause
The key lacks the scope the endpoint requires.
Fix
Use a key that has the scope, or create one in the console. Scopes of an existing key cannot be read back other than through GET /v1/me.
Retry
Retry only after fixing the cause.

ip_not_allowed#

HTTP status
403 Forbidden
Type
permission_error
Cause
The key has an IP allowlist and the request came from an address outside it.
Fix
Call from an allowed address or update the allowlist of the key in the console.
Retry
Retry only after fixing the cause.

account_suspended#

HTTP status
403 Forbidden
Type
permission_error
Cause
The account was suspended.
Fix
Contact support.
Retry
Do not retry.

live_mode_required#

HTTP status
403 Forbidden
Type
permission_error
Cause
The operation exists in live mode only and was called with a test key. POST /v1/channels is the only such operation: test mode uses the built-in sandbox channels.
Fix
Call the endpoint with an om_live_ key. In test mode, send on a sandbox channel such as ch_test_whatsapp.
Retry
Retry only after fixing the cause.

test_mode_required#

HTTP status
403 Forbidden
Type
permission_error
Cause
The operation exists in test mode only and was called with a live key. POST /v1/test/inbound_messages is the only such operation.
Fix
Call the endpoint with an om_test_ key. Live inbound messages come from your customers through the connected channel.
Retry
Retry only after fixing the cause.

source_disabled#

HTTP status
403 Forbidden
Type
permission_error
Cause
The integration source the request belongs to was disabled in the console. Its source key and its native webhooks are refused until it is enabled again.
Fix
Enable the source in the console (Integrations → the source → Settings). A plugin should stop sending and show a reconnect notice.
Retry
Retry only after fixing the cause.

csrf_header_missing#

HTTP status
403 Forbidden
Type
permission_error
Cause
Console only (never returned by /v1). A state-changing console request did not carry the X-Requested-With: omni-web header.
Fix
Send the header with every POST, PUT, PATCH and DELETE request to the console API.
Retry
Retry only after fixing the cause.

account_required#

HTTP status
403 Forbidden
Type
permission_error
Cause
Console only (never returned by /v1). The signed-in user is not a member of a customer account.
Fix
Sign in with a user that belongs to an account.
Retry
Do not retry.

role_not_allowed#

HTTP status
403 Forbidden
Type
permission_error
Cause
Console only (never returned by /v1). The role of the signed-in member does not allow the action.
Fix
Ask an owner or admin of the account to perform the action or to change your role.
Retry
Do not retry.

admin_required#

HTTP status
403 Forbidden
Type
permission_error
Cause
Console only (never returned by /v1). The endpoint is reserved for OmniMessage staff.
Fix
Not available to customer accounts.
Retry
Do not retry.

impersonation_restricted#

HTTP status
403 Forbidden
Type
permission_error
Cause
Console only (never returned by /v1). The action is not allowed in a support session opened on behalf of the account.
Fix
Perform the action from the account owner's own session.
Retry
Do not retry.

reauthentication_required#

HTTP status
403 Forbidden
Type
permission_error
Cause
Console only. Linking or unlinking a sign-in provider, or setting a first password, needs a sign-in or password confirmation from the last 10 minutes.
Fix
Confirm the password (POST /console/auth/reauthenticate) or sign in again with a linked provider, then repeat the request.
Retry
Retry only after fixing the cause.

not_found_error#

HTTP 404. The object does not exist in this account and mode.

resource_missing#

HTTP status
404 Not found
Type
not_found_error
Cause
The ID does not exist, belongs to another account, or belongs to the other mode (a live key cannot see test objects and vice versa).
Fix
Check the ID and that the key mode matches the object.
Retry
Retry only after fixing the cause.

conflict_error#

HTTP 409. The request conflicts with another request or an existing object.

idempotency_key_in_use#

HTTP status
409 Conflict
Type
conflict_error
Cause
A request with the same Idempotency-Key is still in flight.
Fix
Wait briefly and retry with the same key. You then receive the stored response of the first request.
Retry
Safe to retry, with backoff.

idempotency_key_reused#

HTTP status
409 Conflict
Type
conflict_error
Cause
The Idempotency-Key was used in the last 24 hours with a different body.
Fix
Generate a new key for each distinct operation. Reuse a key only to retry the identical request.
Retry
Retry only after fixing the cause.

channel_identifier_taken#

HTTP status
409 Conflict
Type
conflict_error
Cause
A channel of the same type with the same identifier is already connected.
Fix
Use the existing channel, or delete it before connecting the identifier again.
Retry
Retry only after fixing the cause.

email_taken#

HTTP status
409 Conflict
Type
conflict_error
Cause
Console only. The email address is already registered.
Fix
Sign in, or reset the password of the existing user.
Retry
Retry only after fixing the cause.

member_exists#

HTTP status
409 Conflict
Type
conflict_error
Cause
Console only. The invited email address already belongs to a member of an account.
Fix
Invite a different email address.
Retry
Retry only after fixing the cause.

identity_exists#

HTTP status
409 Conflict
Type
conflict_error
Cause
Console only. The Google, Apple or Facebook account is already linked to a user, or the user already has an account of that provider linked.
Fix
Unlink the provider from the other user first, or sign in with it directly.
Retry
Retry only after fixing the cause.

last_sign_in_method#

HTTP status
409 Conflict
Type
conflict_error
Cause
Console only. Unlinking the provider would leave the user without a password and without any linked provider.
Fix
Set a password or link another provider, then unlink.
Retry
Retry only after fixing the cause.

contact_exists#

HTTP status
409 Conflict
Type
conflict_error
Cause
Another contact of the account already has the phone number (after normalisation to E.164).
Fix
Update the existing contact, or use POST /v1/contacts/upsert, which creates or updates by phone number.
Retry
Retry only after fixing the cause.

campaign_state_invalid#

HTTP status
409 Conflict
Type
conflict_error
Cause
The action does not apply to the current status of the campaign: only a draft can be launched, only a scheduled, queued or sending campaign can be paused, only a paused one can be resumed, and a finished one cannot be cancelled.
Fix
Retrieve the campaign and act on its current status.
Retry
Retry only after fixing the cause.

import_state_invalid#

HTTP status
409 Conflict
Type
conflict_error
Cause
Console only. A contact import was started or cancelled in a state that does not allow it.
Fix
Reload the import and upload the file again if it needs to be repeated.
Retry
Retry only after fixing the cause.

channel_error#

HTTP 422. The request is valid but the channel cannot carry it out.

channel_not_connected#

HTTP status
422 Unprocessable
Type
channel_error
Cause
The channel connection_status is not connected, or the channel is suspended.
Fix
Reconnect the channel in the console. Subscribe to channel.disconnected to be told when this happens.
Retry
Retry only after fixing the cause.

recipient_blocked#

HTTP status
422 Unprocessable
Type
channel_error
Cause
The recipient is blocked or has opted out on the channel.
Fix
Do not retry. Remove the recipient from your sending list for this channel.
Retry
Do not retry.

policy_violation#

HTTP status
422 Unprocessable
Type
channel_error
Cause
The channel policy does not allow the message, for example free-form WhatsApp content outside the 24-hour customer service window.
Fix
Send content the channel allows in this situation, such as an approved WhatsApp template.
Retry
Retry only after fixing the cause.

upstream_rejected#

HTTP status
422 Unprocessable
Type
channel_error
Cause
The channel provider refused the request, for example invalid credentials when connecting a channel or content it does not accept.
Fix
Read message for the provider reason, correct the request and send it again.
Retry
Retry only after fixing the cause.

template_not_approved#

HTTP status
422 Unprocessable
Type
channel_error
Cause
A live WhatsApp campaign does not use a message template, or the template it names does not exist on the channel or is not approved by WhatsApp.
Fix
List the templates with GET /v1/channels/{id}/templates and use one whose status is APPROVED, with one parameter per variable of its body.
Retry
Retry only after fixing the cause.

rate_limit_error#

HTTP 429. Too many requests in a short time.

rate_limit_exceeded#

HTTP status
429 Too many requests
Type
rate_limit_error
Cause
The API key sent more requests than its per-second limit allows.
Fix
Wait for the number of seconds in Retry-After, then retry. Smooth bursts with a client-side queue.
Retry
Safe to retry, with backoff.

account_locked#

HTTP status
429 Too many requests
Type
rate_limit_error
Cause
Console only. Five failed sign-in attempts lock sign-in for the email address for 15 minutes.
Fix
Wait for the number of seconds in Retry-After, or reset the password.
Retry
Safe to retry, with backoff.

api_error#

HTTP 500 or 503. Something went wrong on the OmniMessage side.

internal_error#

HTTP status
500 Server error
Type
api_error
Cause
Something failed on our side.
Fix
Retry with exponential backoff and the same Idempotency-Key. If it persists, contact support with the request_id.
Retry
Safe to retry, with backoff.

upstream_unavailable#

HTTP status
503 Service unavailable
Type
api_error
Cause
The delivery layer could not be reached. The message was not accepted and any charge was reversed.
Fix
Retry with exponential backoff and the same Idempotency-Key.
Retry
Safe to retry, with backoff.

    Loading