Authentication and API Tokens

How Wapito Bearer tokens work: issuing and revoking them, the scope of a channel token, rotating a leaked key and the errors a bad token returns.

Updated authenticationtokenssecurity

Every call to the Wapito API is authenticated by one credential: the channel token. There are no API keys at the account level, no OAuth dance and no per-request signing. You send the token as a Bearer header, and the token decides which WhatsApp number the request acts on. This page explains what that token is, what it can and cannot do, how to rotate it, and the three responses a bad one gets.

What a channel token is

A token is issued when a channel is created and looks like this:

Authorization: Bearer wpt_9f2c4a7b1de35086c4b2e7a0f3d8c1b6a9e2f5d4

The wpt_ prefix is followed by exactly forty characters from A–Z, a–z and 0–9, drawn from a cryptographic random source — about 238 bits of entropy, which is far more than anyone will guess. The prefix exists so a secret scanner can recognise the string on sight; if one turns up in a commit, treat it as leaked and rotate it.

The token is shown once, in the dashboard, at the moment the channel is created. Wapito stores only a SHA-256 hash of it, the way a password is stored. After that the dashboard can show a masked preview (wpt_9f2c…f5d4) so you can tell two tokens apart, but nothing on our side can display or e-mail the token itself. If it is gone, rotate.

The scope of a token

A token belongs to exactly one channel, and a channel is exactly one linked WhatsApp number. That has three practical consequences:

  • No channel id in any path. POST /messages/text sends from the number the token belongs to. If you run five numbers, you hold five tokens and pick the one for the number that should speak.
  • Everything the channel can do, the token can do. There are no read-only tokens and no scopes in v1. Sending, reading chats, changing settings, creating webhooks and logging the phone out are all one credential. Guard it accordingly.
  • A token cannot reach another channel, even another one in the same account. A leaked token exposes one number, not your whole workspace.

The dashboard itself does not use channel tokens: it signs in with your account and talks to a separate set of endpoints with a short-lived identity token. That is why creating a channel, issuing its token and rotating it are dashboard actions rather than API calls — the API cannot mint the credential that authorises it.

Making an authenticated request

Send the header on every request. Check a token the same way the dashboard does, by asking for the channel it belongs to:

curl https://api.wapito.com/v1/channel \
  -H "Authorization: Bearer $WAPITO_TOKEN"

Read the token from the environment or a secret manager, never from a constant in the source. The generated code on every API reference page follows the same rule, and so do the snippets the dashboard writes for you.

What a bad token returns

Three codes, all in the standard error envelope. Branch on code, not on the message text.

StatuscodeMeaning
401unauthorizedThe header is missing, is not Bearer …, or names a token Wapito has never issued.
401token_revokedThe token was valid once and has been rotated since. The new token is in the dashboard.
403channel_lockedThe token is fine but the channel is locked — details.reason says why, most often plan_required after a subscription lapsed.
{
  "error": {
    "code": "token_revoked",
    "message": "This channel token has been revoked.",
    "request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
  }
}

The request_id is in every error response. Quote it when you write to support: it is how we find the exact request in our logs. A 401 is never retried by a well-behaved client — nothing changes between one attempt and the next — and a 403 channel_locked is fixed in the dashboard (or by paying the invoice), not by code.

Authentication runs before rate limiting, and unauthenticated traffic is deliberately damped, so a script that loops on a 401 is throttled quickly. Fix the token instead.

Rotating a token

Open the channel's API tab and choose Rotate. The dashboard asks you to confirm, then shows the new token once. The old token stops working immediately — there is no overlap window in which both are valid — and every request still carrying it gets 401 token_revoked.

Rotate when:

  • a token appears anywhere it should not: a commit, a log line, a screenshot, a support ticket;
  • someone who had access to the secret store leaves;
  • you are handing a channel from one system to another and want the old one cut off;
  • you simply have not rotated in a while. There is no cost to it.

Because there is no overlap, plan a rotation as a two-step deploy: write the new token into your secret store first, then rotate, then restart whatever reads the store. A service that reads the token once at start-up will see a handful of 401 token_revoked responses between the rotation and its restart; a service that reads it per request sees none.

Keeping the token secret

  • Server-side only. The API does not accept requests from browser origins other than the dashboard's, and a token in a front-end bundle is public the moment the page loads. Put a small backend in between.
  • Headers, never URLs. Query strings end up in access logs, browser histories and referrer headers. The API reads the token from the Authorization header and nowhere else.
  • One token per environment. Use a sandbox channel for development and a separate channel for production, so a leaked development token cannot send from the number customers know.
  • Scan for wpt_. Add the prefix to your secret scanner and pre-commit hooks.

The other two credentials

Two more secrets appear in the API, and neither authenticates a request:

  • A webhook signing secret (whsec_…) is returned once when you create a webhook. Wapito uses it to sign every delivery, and your receiver uses it to verify the X-Wapito-Signature header. It is for checking inbound traffic, not for calling the API — see Webhooks.
  • A signed media link (/media/{id}?exp=…&sig=…) is a time-limited URL for one stored file. It carries its own credential, expires after 24 hours on Sandbox and 7 days on Premium, and is safe to hand to a browser or a downstream service that must never see the channel token — see Media.

Keep all three in the same secret store, and rotate the channel token first if you are ever unsure which one leaked.

Try it on your own number

Create a channel, link a WhatsApp number by QR or pairing code, and call the API in a couple of minutes. The Sandbox plan is free and needs no card.