Where the token comes from, what it can reach, how it rotates, and what to do when one leaks.
One credential, one header:
Authorization: Bearer YOUR_ACCESS_TOKEN
That token is the connected number's WhatsApp access token. It is issued by Meta when the number is connected, and it is the same secret Ghala uses to send on your behalf. There is no separate, narrower API credential to mint for /api/v2.
Two consequences follow, and both matter:
/api/v2 endpoint is scoped to it. There is no way to reach a second number, list your numbers, or act at the account level.Dashboard → Developer → Credentials. Each connected number has an entry, and each entry has an explicit reveal action.
Lists and detail views never carry the secret. They carry a masked preview — the first six and last four characters — so you can tell two tokens apart in the UI without either of them being readable over someone's shoulder. Revealing the full token is a deliberate, separate action.
Copy it into a secret manager or an environment variable. Never into code, a browser bundle, a mobile app, or git.
| Question | Answer |
|---|---|
| Who issues it? | Meta, when the number is connected through Embedded Signup |
| What does it authenticate? | Exactly one connected number |
| Who can read it? | Members of the Ghala team that owns the number, through the explicit reveal action |
| Does it have scopes? | No Ghala-side scopes. It grants everything /api/v2 can do for that number |
| Does it expire on a schedule? | It is not published as having a fixed lifetime. Assume it lives until the number is reconnected or disconnected |
| Can I have several tokens per number? | No. One number, one token |
| Is there a read-only variant? | No |
Because there are no scopes, the unit of least privilege is the number itself. If part of your system should only ever touch one number, give it only that number's token.
Reconnecting a number rotates its token. That is the mechanism, and it is the only one.
401 not_authenticated on the very next call.The safe sequence:
GET /api/v2/templates?limit=1 is ideal, because it proves authentication without messaging anybody.If your code caches the token at boot, invalidate that cache on write, or resolve the token per request. The multi-tenant guide shows a per-request resolver.
There is no revoke button for an access token. Reconnecting the number is revocation, because it invalidates the previous token.
If a token leaks:
Disconnecting the number is the stronger option if you need it to stop sending entirely. Note that disconnecting also frees the number to be claimed by another team, so it is not a quiet operation.
In practice this has one overwhelmingly common cause: somebody reconnected the number. Reconnection is a routine fix for other problems, so it often happens without the person doing it realising an integration depends on the old token.
Check, in order:
Authorization header actually being sent? A missing header and an unknown token return the same 401.A missing token and an invalid token are deliberately indistinguishable, so the API cannot be used to probe which tokens exist. That means the response will not tell you which of these it is; the dashboard will.
Ghala has no sandbox and no test-mode credential. A token sends real messages to real handsets, and there is no dry-run flag.
So separation is a matter of using different numbers, not different keys:
.env files that developers have on laptops. Non-production and production should not read from the same secret store path.Because there is no test mode, send your first message to your own handset. It is the cheapest way to discover a mistake.
What is available today:
What is not available: there is no last-used timestamp on a number's access token, and no per-request correlation id in API responses. If you need to attribute a send to a caller, log your own Idempotency-Key — it is meaningful on both sides of the call and is the closest thing to a correlation id the API offers.
A note on dashboard API keys. The dashboard can also mint team-level API keys with a label and a last-used timestamp. Those authenticate Ghala's internal dashboard API, not
/api/v2. The Developer API accepts the number's WhatsApp access token and nothing else; sending an API key instead returns401 not_authenticated.
401 triggers an alert, not a silent retry loop401 and everything else