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.
Authorization: Bearer om_live_xxxxxxxxxxxxxxxxxxxxxxxxcurl 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.
{
"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.
| Prefix | Mode | Behaviour |
|---|---|---|
om_live_ | Live | Real delivery through your connected channels. Outbound messages are billed. |
om_test_ | Test | Sandbox. 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.
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.
- Create a new key with the same mode, scopes and allowlist.
- Deploy the new key to every service that used the old one.
- Watch "last used" on the old key in the console until it stops changing.
- Revoke the old key. Requests with it fail from that moment with
401 api_key_revoked.
Authentication errors#
| HTTP | Code | Cause |
|---|---|---|
| 401 | webhook_signature_invalid | 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. |
| 401 | api_key_missing | The Authorization header is absent or does not use the Bearer scheme. |
| 401 | api_key_invalid | The key is malformed, truncated or does not exist. |
| 401 | api_key_revoked | The key was revoked in the console. |
| 401 | api_key_expired | The key was created with an expiry date that has passed. |
| 403 | scope_missing | The key lacks the scope the endpoint requires. |
| 403 | ip_not_allowed | The key has an IP allowlist and the request came from an address outside it. |
| 403 | account_suspended | The account was suspended. |
| 403 | live_mode_required | 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. |
| 403 | test_mode_required | The operation exists in test mode only and was called with a live key. POST /v1/test/inbound_messages is the only such operation. |
| 403 | source_disabled | 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. |
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.