Authentication
Integrations, their keys, and the rights that decide which objects and fields each one can read and write.
The REST API is called by an integration: a machine identity that belongs to exactly one workspace. An integration is not a member. It has no account, no seat and no role; it can do what its rights say, and nothing else.
Creating an integration
- In Kasar, open Settings → API and webhooks. The Integrations section is shown to workspace admins.
- Click New integration, give it a name (unique in the workspace), tick its rights, and choose an expiration: never, 30 days, 90 days or 1 year.
- Copy the key. It is shown once and never stored in clear: to get another one, create a new key on the integration.
Sending the key
Keys start with ksr_svc_. Send them as a bearer token:
curl https://platform.kasar.app/api/v1/objects \
-H "Authorization: Bearer ksr_svc_eyJhbGciOiJIUzI1NiJ9..."The scheme is Bearer followed by one space. A personal key that starts with ksr_ but not ksr_svc_ belongs to the MCP connector and is refused by the REST API (401 INVALID_TOKEN).
One integration, one workspace
The key decides the workspace; nothing in the request can change it. The X-Kasar-Organization header is refused, not ignored:
{
"error": {
"code": "ORGANIZATION_HEADER_NOT_SUPPORTED",
"message": "'x-kasar-organization' is not accepted: an integration belongs to exactly one workspace.",
"suggestion": "Remove the header. To reach another workspace, create an integration there."
}
}The status is 400. To work with several workspaces, create one integration in each.
Keys, rotation and revocation
- An integration can hold several active keys. New key creates another one and leaves the current one working: switch your configuration, then Close this key on the old one. The list shows when each key was last used.
- Revoke stops the integration and every one of its keys immediately.
- Rights belong to the integration, not to a key. A new key changes nothing else: same rights, same rate limits, same idempotency keys.
- Rights, revocation and expiration are read again on every request: a change applies to the next call.
Rights
Rights are deny by default: anything not ticked is refused, including an object created after the integration.
For each object, an integration can be granted:
| Right | Allows |
|---|---|
Read ({object}:read) | GET .../records, GET .../records/{id}, GET .../deleted; the object appears in GET /objects |
Write ({object}:write) | POST and PUT .../records, PATCH .../records/{id} |
Delete ({object}:delete) | DELETE .../records/{id} |
And two general rights:
| Right | Label in Settings | Allows |
|---|---|---|
schema:read | Read the data model | GET /objects and GET /objects/{object} |
webhooks:manage | Manage webhook destinations | Everything under /webhooks/destinations, for the destinations attached to the integration |
Automations and email sequences are not managed through the API: they are built and run by the workspace's members, in the app.
What a right covers:
- An integration that reads an object reads all its records. The visibility rules of roles (own records, team records) do not apply to a machine.
- Write without Read is allowed. The write happens, and its response is
{ "data": null }: an integration does not receive what it cannot read. - The objects you can grant are the ones the API serves: standard and custom CRM objects. List entries and the platform's internal objects cannot be granted.
A request outside the rights is refused with 403 FORBIDDEN_GRANT, which names the missing right:
{
"error": {
"code": "FORBIDDEN_GRANT",
"message": "This integration is not granted 'contacts:write'.",
"suggestion": "A workspace admin can grant it in Settings → API, on the integration 'ERP sync'."
}
}On the record routes, the right is checked before the object is looked up: an object the integration was not granted answers 403 FORBIDDEN_GRANT, whether it exists or not. The schema routes answer 404 INVALID_OBJECT instead (see Objects).
Choosing fields
For each object it reads, an integration sees either:
- All fields, including those created later. This is the default.
- A list of fields. Only those, and nothing created later. A pipeline's status (
status) is chosen like a field.
Either way, a field the workspace marked confidential is never part of it: Settings shows it under a lock, and a list cannot name it.
With a list:
- Records carry only the chosen keys, plus
id: inGETresponses, in the record returned by a write, and in webhook deliveries to a destination attached to the integration. GET /objects/{object}describes only the chosen keys, andfield_countcounts only them.- Writing a key outside the list is refused with
422 UNKNOWN_FIELD, like a key the object does not have. - Filtering, sorting or grouping on a field outside the list is refused with
400 INVALID_QUERY_FIELD, like a field that does not exist. - A composite (an address, a name, an amount) is chosen as a whole. Choosing a pipeline gives its two columns (
pipeline_id,pipeline_step_id) and its status; the status can also be chosen alone, and is read-only either way. - A chosen field later deleted from the data model, or later marked confidential, simply disappears from the output, without an error.
Authentication errors
The checks run in this order, and the first one that fails answers:
- the per-IP rate limit (
429); - the
X-Kasar-Organizationheader (400 ORGANIZATION_HEADER_NOT_SUPPORTED); - the presence and format of the key (
401); - the per-integration burst limit (
429); - the key itself: known, not closed, not expired, its integration not revoked (
401, or503if it cannot be checked); - the workspace (
403 WORKSPACE_INACCESSIBLE); - the per-minute rate limits (
429); - the rights of the route (
403 FORBIDDEN_GRANT).
| Status | Code | When |
|---|---|---|
401 | UNAUTHENTICATED | No Authorization: Bearer ... header |
401 | INVALID_TOKEN | The key is malformed, unknown, closed or expired, or its integration was revoked. One code for all of them, on purpose: the response does not say which. |
400 | ORGANIZATION_HEADER_NOT_SUPPORTED | The request carries X-Kasar-Organization |
403 | WORKSPACE_INACCESSIBLE | The integration's workspace no longer exists |
403 | FORBIDDEN_GRANT | The integration lacks the right the route needs; the message names it |
403 | PERMISSION_DENIED | A write touches a field the integration reads but cannot write (a pipeline whose status alone was chosen) |
503 | SERVICE_UNAVAILABLE | Kasar could not check the key because its database is unavailable. Comes with Retry-After: 5. |
A 503 is not a verdict on your key: its message says "Kasar is temporarily unavailable. Your key is still valid." Retry after Retry-After; do not disable the integration or alert on a revoked key. Only 401 INVALID_TOKEN means the key does not work.
All codes: Errors.