Skip to content

Get started

Authentication

Authenticate with an API key sent as a bearer token. Keys are live or test, carry scopes and can be restricted to IP addresses.

Send the key as a bearer token#

Every request must carry an API key in the Authorization header. There are no other authentication schemes, and keys are never sent in the URL or the body.

Header
Authorization: Bearer om_live_xxxxxxxxxxxxxxxxxxxxxxxx
curl https://api.omnimessage.co/v1/me \
  -H "Authorization: Bearer om_live_xxxxxxxxxxxxxxxxxxxxxxxx"

GET /v1/me works with any valid key, whatever its scopes, and reports the account, the mode and the scopes of the key. It is the quickest way to check a credential.

200 OK
{
  "object": "account",
  "id": "acc_8nM3bV6cX9zL2kJ5hG1f",
  "name": "Acme Logistics",
  "mode": "live",
  "api_key": {
    "id": "key_1qW4eR7tY0uI3oP6aS9d",
    "name": "Production backend",
    "scopes": [
      "messages:write",
      "messages:read",
      "channels:read"
    ]
  },
  "capabilities": [
    "automation_events",
    "webhook_filters",
    "test_inbound",
    "events_feed"
  ]
}

Key types#

The prefix of a key tells you its mode. The mode is fixed when the key is created and applies to everything the key does: the objects it sees, the channels it can send on and whether messages are delivered and billed.

PrefixModeBehaviour
om_live_LiveReal delivery through your connected channels. Outbound messages are billed.
om_test_TestSandbox. Nothing is delivered, nothing is billed and statuses are simulated. See Test mode.

Live and test data are separate. A test key cannot retrieve a live message and a live key cannot send on a sandbox channel; in both cases the API answers 404 resource_missing.

Create and store keys#

Create keys in the console under API keys. The full key is shown once, at creation. OmniMessage stores only a SHA-256 hash, so a lost key cannot be recovered: create a new one instead. The console shows the first characters and the last four so that you can tell keys apart.

  • Give each service or environment its own key, named after where it runs.
  • Keep keys in a secret manager or environment variable, never in source control or client-side code.
  • A key may have an optional expiry date. After that date requests fail with api_key_expired.

Scopes#

A scope allows a group of endpoints. Choose the scopes when you create the key; a key created with full access has all of them. A request to an endpoint outside the scopes of the key fails with 403 scope_missing.

ScopeAllowsEndpoints
messages:writeSend messages, alone or in batches.POST /v1/messages, POST /v1/messages/batch, POST /v1/test/inbound_messages
messages:readRetrieve and list messages and their status history.GET /v1/messages, GET /v1/messages/{id}, GET /v1/messages/{id}/events
channels:readList and retrieve channels and WhatsApp templates.GET /v1/channels, GET /v1/channels/{id}, GET /v1/channels/{id}/templates
channels:writeConnect, rename and delete channels.POST /v1/channels, PATCH /v1/channels/{id}, DELETE /v1/channels/{id}
webhooks:readList and retrieve webhook endpoints.GET /v1/webhook_endpoints, GET /v1/webhook_endpoints/{id}, GET /v1/events, GET /v1/events/{id}
webhooks:writeCreate, update, delete and test webhook endpoints, and roll their secrets.POST /v1/webhook_endpoints, PATCH /v1/webhook_endpoints/{id}, DELETE /v1/webhook_endpoints/{id}, POST /v1/webhook_endpoints/{id}/roll_secret, POST /v1/webhook_endpoints/{id}/test
billing:readRead the balance, pricing and usage.GET /v1/balance, GET /v1/pricing, GET /v1/usage
contacts:readList and retrieve contacts, tags, lists and segments.GET /v1/contacts, GET /v1/contacts/{id}, GET /v1/contact_tags, GET /v1/contact_lists, GET /v1/contact_lists/{id}, GET /v1/segments, GET /v1/segments/{id}, GET /v1/segments/{id}/preview
contacts:writeCreate, update, delete and tag contacts, and manage lists.POST /v1/contacts, POST /v1/contacts/upsert, POST /v1/contacts/tags, PATCH /v1/contacts/{id}, DELETE /v1/contacts/{id}, POST /v1/contact_lists, PATCH /v1/contact_lists/{id}, DELETE /v1/contact_lists/{id}, POST /v1/contact_lists/{id}/members, POST /v1/contact_lists/{id}/members/remove
campaigns:readList and retrieve campaigns and their recipients.GET /v1/campaigns, GET /v1/campaigns/{id}, GET /v1/campaigns/{id}/recipients
campaigns:writeCreate, launch, pause, resume and cancel campaigns.POST /v1/campaigns, POST /v1/campaigns/{id}/launch, POST /v1/campaigns/{id}/pause, POST /v1/campaigns/{id}/resume, POST /v1/campaigns/{id}/cancel
integrations:readList and retrieve integration sources.GET /v1/integration_sources, GET /v1/integration_sources/{id}
integrations:writeRegister, update, roll the key of and delete integration sources.POST /v1/integration_sources, PATCH /v1/integration_sources/{id}, DELETE /v1/integration_sources/{id}, POST /v1/integration_sources/{id}/roll_key
events:readList and retrieve automation event receipts.GET /v1/automation_events, GET /v1/automation_events/{id}
events:writePush automation events for a source of the account.POST /v1/automation_events, POST /v1/automation_events/batch

Grant the narrowest set that works. A backend that only sends messages needs messages:write; add messages:read only if it also polls for status.

IP allowlist#

A key can be limited to a list of IP addresses or CIDR ranges, set in the console. With an allowlist in place, a request from any other address fails with 403 ip_not_allowed, even if the key is otherwise valid. An empty allowlist accepts requests from any address.

Use allowlists for keys that run on infrastructure with stable egress addresses. If you call the API through a NAT gateway or proxy, allow the address OmniMessage sees, which is the public egress address.

Rotate a key#

Several keys can be active at once, so rotation needs no downtime.

  1. Create a new key with the same mode, scopes and allowlist.
  2. Deploy the new key to every service that used the old one.
  3. Watch "last used" on the old key in the console until it stops changing.
  4. Revoke the old key. Requests with it fail from that moment with 401 api_key_revoked.

Authentication errors#

HTTPCodeCause
401webhook_signature_invalidNative 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.
401api_key_missingThe Authorization header is absent or does not use the Bearer scheme.
401api_key_invalidThe key is malformed, truncated or does not exist.
401api_key_revokedThe key was revoked in the console.
401api_key_expiredThe key was created with an expiry date that has passed.
403scope_missingThe key lacks the scope the endpoint requires.
403ip_not_allowedThe key has an IP allowlist and the request came from an address outside it.
403account_suspendedThe account was suspended.
403live_mode_requiredThe 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.
403test_mode_requiredThe operation exists in test mode only and was called with a live key. POST /v1/test/inbound_messages is the only such operation.
403source_disabledThe 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.

Security practices#

Keep live keys on servers#

Call the API from your backend only. A key embedded in a mobile app, a browser bundle or a public repository must be treated as compromised.

Separate duties#

Use a send-only key in the service that sends, a read-only key in reporting jobs and a billing:read key in finance dashboards. A leaked reporting key then cannot send messages.

Use test keys outside production#

CI, staging and local development should use om_test_ keys. They exercise the same request validation and webhook flow without delivering or billing.

Verify webhooks#

Webhook requests are authenticated separately, with a signature computed from the endpoint secret. Always verify the signature before acting on an event.

    Loading