RaabtaHQ
Authentication

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.

Request
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

A missing header, a malformed one, an unknown key, a revoked key and an expired key all return the same 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.

ScopeGrantsIncludesUsed by
contacts:readView contacts, tags, custom fields and notes
contacts:writeCreate and update contacts, tags, custom-field values and notescontacts:read
conversations:readView conversations and team members
conversations:writeAssign, change status, mark read, hand over to or from the AIconversations:read
messages:readRead messages and delivery status
messages:sendSend text, template and media messagestemplates:read
templates:readList WhatsApp message templates
broadcasts:readView broadcasts and per-recipient delivery
broadcasts:sendCreate, schedule and cancel template broadcastsbroadcasts:readtemplates:read
deals:readView pipelines, stages and deals
deals:writeCreate, update, move, close and delete dealsdeals:read
inquiries:readView website-form inquiries and their notes
inquiries:writeSubmit inquiries, change their status, and add notesinquiries:read
assistants:readView assistants, the tools they may use, the instruction library and stored memories
assistants:writeCreate and edit assistants, their instructions, memories and triggersassistants:read
runs:readView assistant runs, their event timelines and pending asks
runs:startStart assistant runs with a goal you supply, and cancel them. A run bound to a contact or conversation can send messages on your channelsruns:read
asks:readView the approvals and questions an assistant is waiting on
asks:answerApprove, decline or answer an assistant's questionsasks:read
ai:usageView AI spend and the monthly AI budget
webhooks:manageCreate 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

If an allowlist is set and the client address cannot be determined, the request is refused. Do not set one for a client behind a proxy whose address you do not control.

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.