API keys and rate limits
Server-to-server calls to the KauZap API use an API key with only the scopes it needs.
Create a key
- A tenant admin opens Settings, Developers, and creates a key with a name and its scopes.
- The key is shown once. Store it in your secret manager; we keep only a hash and its first characters.
- If the create response is lost and you retry with the same
Idempotency-Key, the replay shows the key masked. Create a new key and revoke the one you never saw. - Revoke a key there at any time. Requests with it fail straight away.
Keys look like kz_ followed by 40 letters and digits. Send them in the Authorization header:
curl https://YOUR_API/v1/conversations \
-H "Authorization: Bearer kz_…"
Never put an API key in a browser, a mobile app or a repository. Use signed URLs when something outside your server needs to write.
Scopes
| Scope | Allows |
|---|---|
contacts:read, contacts:write | Read, create and update contacts |
conversations:read, conversations:write | Read and start conversations |
messages:read, messages:write | Read messages and send bot or system messages |
tags:read, tags:write | Read tags and tag conversations |
groups:read | Read chat groups |
agents:read | Read agents |
webhooks:manage | Manage webhook endpoints |
signed_urls:write | Mint signed URLs |
tickets:read, tickets:write | Read tickets and create a ticket from a conversation |
A request without the scope it needs gets 403 with code forbidden, naming the missing scope.
Rate limits
Each key has its own limit. Over it, you get 429 with a Retry-After header in seconds. Wait that long, then retry; add jitter if several workers share a key.
Safe retries
- Send an
Idempotency-Keyheader (a UUID) on anyPOSTthat creates something. Repeating the request with the same key returns the first result instead of creating a duplicate. - Message sends also need a
client_idin the body, unique per message.
Pagination and errors
Lists are paginated with a cursor: pass the next_cursor from one page to get the next. Page sizes have a maximum.
Every error has the same shape, with a stable code to branch on:
{ "error": { "code": "forbidden", "message": "…", "details": {} } }
See the API reference for every endpoint, its scopes and schemas.