Skip to content

Authentication

Every request carries an API key in the Authorization header:

Terminal window
curl https://k-message.kerneltics.com/v1/me \
-H "Authorization: Bearer km_live_a1b2c3d4e5f6..."

X-API-Key: km_live_... is accepted as an alternative. Bearer is documented because it is what HTTP clients and API tools default to.

There is no login step, no token to refresh, and no expiry unless you set one.

Settings → API Keys → Create API key in the dashboard.

You choose a name, the scopes it carries, and optionally an expiry date, a rate limit and a list of IP addresses it may be used from.

The full key appears once, in the response to creating it, and is never retrievable again. We store only a SHA-256 hash, so we could not show it to you a second time even if asked. If you lose it, create another and revoke the old one.

A key can only do what you ticked when you created it. Scopes are exact: there is no hierarchy and no wildcard, and contacts:write does not imply contacts:read. That is deliberate, because implied permissions are where scope systems quietly grow holes, and ticking both boxes costs you nothing.

ScopeAllows
account:readRead the organization and its WhatsApp numbers
messages:sendSend text, template and media messages
messages:readRead conversations, history, delivery status and media
contacts:readList, search and read contacts
contacts:writeCreate and edit contacts and their tags
contacts:deleteDelete contacts, and their conversation history with them
templates:readList templates and the variables each expects
templates:writeSubmit templates for approval and sync their status
campaigns:readRead campaigns and their progress
campaigns:writeCreate campaigns, add recipients, start and pause them
webhooks:readRead webhook endpoints and their delivery history
webhooks:writeCreate, edit, delete and test webhook endpoints

Calling an endpoint without its scope returns 403 and names the scope you are missing, so the fix does not require reading this table:

{
"error": {
"type": "permission_error",
"code": "missing_scope",
"message": "This API key is missing the 'messages:send' scope. Add it in Developers, or use a key that has it.",
"doc_url": "https://developers.k-message.kerneltics.com/errors#missing-scope"
}
}

You cannot grant a scope you do not hold yourself. Someone with no campaign permission cannot mint a key that runs campaigns, because that would be an escalation route around their own role.

Server-side only. A key in a browser bundle or a mobile app is a key anyone can read. Anything reaching customers must go through your own backend.

In the environment, not the repository. A key committed to git stays in the history after it is deleted.

One key per system. Separate keys for your store, your ERP and your internal tooling means you can revoke one without an outage in the others, and the “last used” timestamps tell you what is still running.

Restrict where it can be used. If your integration calls from fixed addresses, add them to the key’s allowlist. A leaked key is then unusable from anywhere else.

Set an expiry for anything temporary. A key made for a migration or an agency should stop working on its own.

Two different things, on purpose:

Disable flips the key off and back on. Use it to test whether something still depends on a key before removing it.

Revoke is permanent. The key can never be used again and cannot be re-enabled. Use it when a key may have leaked. They are separate controls so nobody kills a key for good by flipping a switch they thought was reversible.

Either way, a request with that key gets a 401 naming which happened:

{
"error": {
"type": "authentication_error",
"code": "key_revoked",
"message": "This API key was revoked and can no longer be used."
}
}

Keys beginning whm_ were issued before scopes existed. They still work with the dashboard API but are refused on /v1, with key_wrong_surface, because they carry no scopes and there is no safe way to guess what they should be allowed to do.

Create a new key. There is no migration path, and that is the point: the reason these keys are refused is exactly the reason they should be replaced.