Authentication
Bearer API keys scoped to one account: key shape, scopes and what they imply, IP allowlists, expiry, revocation and regeneration.
The bearer header
Every request carries the key in an Authorization: Bearer header. There is no query-string or cookie alternative — a key in a URL ends up in a log somewhere.
curl "https://your-crm.example.com/api/v1/me" \
-H "Authorization: Bearer $RAABTA_API_KEY"A key is raabta_hq_live_ followed by 43 URL-safe characters (58 in total). The prefix is not secret; it exists so a leaked key is recognisable to secret scanners. A header that does not have that shape never reaches the database.
One answer for every bad key
401 unauthorized. That is deliberate: distinguishing them would tell an attacker which strings were once real. The dashboard's key audit log records the real reason for the account's admins.One key, one account
A key belongs to exactly one account and can never see another, at any scope. Every record it reads or writes is that account's; a resource that exists in a different account returns 404 not_found, not 403.
Writes are attributed to a person: the member who created the key, or the account owner if that member has since left. That is who appears as the author of a note or the sender of a message in the dashboard, and it is what author_user_id fields report.
If the account is suspended the key stays valid but every call returns 403 account_suspended. Rotating the key does not help; the account owner needs to contact support.
Scopes
What a key may do is decided entirely by the scopes granted when it was created, not by the role of whoever created it. A key minted by an owner is not an owner. Each endpoint requires one scope; calling it with a key that lacks that scope returns 403 forbidden with reason: scope.
Write scopes include the reads they need, one level deep: contacts:write lets a key read the contact it just created, and messages:send lets it list the templates it can send. GET /me reports both the granted list and the effective one.
| Scope | Grants | Includes | Used by |
|---|---|---|---|
contacts:read | View contacts, tags, custom fields and notes | — | |
contacts:write | Create and update contacts, tags, custom-field values and notes | contacts:read | |
conversations:read | View conversations and team members | — | |
conversations:write | Assign, change status, mark read, hand over to or from the AI | conversations:read | |
messages:read | Read messages and delivery status | — | |
messages:send | Send text, template and media messages | templates:read | |
templates:read | List WhatsApp message templates | — | |
broadcasts:read | View broadcasts and per-recipient delivery | — | |
broadcasts:send | Create, schedule and cancel template broadcasts | broadcasts:readtemplates:read | |
deals:read | View pipelines, stages and deals | — | |
deals:write | Create, update, move, close and delete deals | deals:read | |
inquiries:read | View website-form inquiries and their notes | — | |
inquiries:write | Submit inquiries, change their status, and add notes | inquiries:read | |
assistants:read | View assistants, the tools they may use, the instruction library and stored memories | — | |
assistants:write | Create and edit assistants, their instructions, memories and triggers | assistants:read |
|
runs:read | View assistant runs, their event timelines and pending asks | — | |
runs:start | Start assistant runs with a goal you supply, and cancel them. A run bound to a contact or conversation can send messages on your channels | runs:read | |
asks:read | View the approvals and questions an assistant is waiting on | — | |
asks:answer | Approve, decline or answer an assistant's questions | asks:read | |
ai:usage | View AI spend and the monthly AI budget | — | |
webhooks:manage | Create and manage outbound webhook subscriptions | — |
|
These endpoints need a valid key and no scope at all:
IP allowlists
A key can be restricted to a list of IPv4 or IPv6 addresses and CIDR ranges. With a list set, a request from any other address returns 403 forbidden with reason: ip_not_allowed — the key is recognised, which is why this is not a 401.
Fails closed
Expiry
Expiry is optional. The dashboard offers 30 days, 90 days, 180 days or 365 days (defaulting to 90) or never, up to 365 days from now on an edit. An expired key returns 401 unauthorizedlike any other bad key; the dashboard notifies the account's admins a week before it happens.
GET /me returns expires_at — poll it if your integration needs to warn its own operators.
Revocation and regeneration
- Revoke — the key stops working on its next request. It stays listed, greyed, with who revoked it and why, so the audit trail survives the key.
- Regenerate — the key keeps its name, scopes, allowlist and rate limit and receives a new secret, shown once. The old secret stops working the moment the new one is issued. Use this to rotate a key that may have leaked without re-creating its configuration.
Both are immediate and both are logged. If you are automating rotation, the idempotency store is per account, not per key, so a request retried with the new key still replays correctly.