API Overview
The Kasar REST API v1. Base URL, versioning, response envelope, JSON conventions, and the path of a typical integration.
The Kasar API lets an external system (an ERP, a planning tool, a data warehouse, a script) read and write the records of one Kasar workspace. It speaks JSON over HTTPS and authenticates with the key of an integration: a machine identity created by a workspace admin, with its own rights. See Authentication.
Base URL
https://platform.kasar.app/api/v1Every path in this reference is relative to it. Use HTTPS.
curl https://platform.kasar.app/api/v1/objects \
-H "Authorization: Bearer ksr_svc_..."Versioning
The version is in the path. What v1 promises is its envelope: authentication, the response and error shapes, pagination, rate-limit headers, and the shape in which a record is read and written.
The objects and fields are not part of that promise: they belong to your workspace and change when its data model changes. The API has no /contacts or /companies route; the same routes serve every object, standard or custom, and you discover them at runtime with GET /objects.
Responses can gain keys over time. Ignore keys you do not know; do not fail on them.
Endpoints
Data model and records
| Method | Path | What it does |
|---|---|---|
GET | /objects | The objects the integration can read |
GET | /objects/{object} | One object's fields, in the exact shape records are read and written |
GET | /objects/{object}/records | List records: filters, sort, search, cursor pagination |
POST | /objects/{object}/records | Create a record (201) |
PUT | /objects/{object}/records | Upsert by identity: created (201) or updated (200) |
GET | /objects/{object}/records/{id} | Read one record |
PATCH | /objects/{object}/records/{id} | Partial update: absent keys are untouched, null clears |
DELETE | /objects/{object}/records/{id} | Delete a record |
GET | /objects/{object}/deleted?since= | Records deleted since a date (kept 30 days) |
GET / POST | /objects/{object}/records/{id}/{field} | List / add a record's emails, phones or LinkedIn profile |
PATCH / DELETE | /objects/{object}/records/{id}/{field}/{entry_id} | Make an entry the primary one, change its label / remove it |
Details: Records, Upsert and idempotency.
Webhook destinations
| Method | Path | What it does |
|---|---|---|
GET / POST | /webhooks/destinations | List / create outbound destinations |
GET / PATCH / DELETE | /webhooks/destinations/{id} | Read / change / delete one destination |
POST | /webhooks/destinations/{id}/{action} | pause, resume, rotate-secret, test |
GET | /webhooks/destinations/{id}/deliveries | Recent delivery attempts |
POST | /webhooks/destinations/{id}/deliveries/{deliveryId}/retry | Replay an abandoned delivery |
Details: Webhooks.
Automations and sequences
Not available through the API: automations and email sequences are built and run by the workspace's members, in the app. An integration reacts to what they do through webhooks.
Response envelope
A single resource comes back under data:
{
"data": {
"id": "0b8f6a52-6f3e-4c1e-9d2a-3f1c2e7d9a10",
"name": "Acme renewal 2027",
"amount": { "amount": 1250.5, "currency_code": "EUR" },
"status": "open",
"created_at": "2026-10-13T09:30:00.000Z"
}
}A list puts the page under data, and adds next_cursor when another page exists:
{ "data": [ { "id": "..." }, { "id": "..." } ], "next_cursor": "k1.WyIyMDI2LTEwLTEz..." }An error always has the same shape, and the HTTP status follows from its code:
{
"error": {
"code": "UNKNOWN_FIELD",
"message": "Unknown field(s) on 'contacts': firstname.",
"suggestion": "Check the object schema for the exact field names (kasar://schema/contacts, or GET /api/v1/objects/contacts)."
}
}Every code, its status and when it happens: Errors.
Headers on every response
| Header | Meaning |
|---|---|
X-Request-Id | A unique id for this request. Log it, and quote it when you contact support. |
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Policy | The state of the rate limit that leaves this request the least room. See Rate limits. |
Retry-After is added on 429, on 503, and on 409 IDEMPOTENCY_KEY_IN_PROGRESS. Timing never travels in the body.
JSON conventions
- Envelope keys are snake_case (
next_cursor,field_errors). Record keys are the keysGET /objects/{object}lists for your data model. - Date-times are ISO-8601 in UTC, with milliseconds:
"2026-10-13T09:30:00.000Z". A date-only field is"YYYY-MM-DD". - Numbers are JSON numbers, never strings:
"amount": 1250.5,"probability": 40. This covers number, integer, decimal, percent and rating fields, numeric computed fields, and the numeric parts of composites such as a currency amount. - Empty is
null, never"". An empty list is[]. - Ids are UUIDs. A relation is read and written as the id of the record it points to.
- Unknown keys are to be ignored by your client.
- Request bodies are a JSON object, at most 256 KB (
413 PAYLOAD_TOO_LARGEbeyond).
Pagination
Lists are paginated by cursor. Pass the next_cursor of a page as cursor to get the next one, with the same filters and sort; there is no more data when a page has no next_cursor. limit is between 1 and 100; a larger value is refused, not truncated. The list sorted by creation date (the default) is paginated by key, with no depth limit. The details, and total, are in Records.
A typical integration
- Create the integration. A workspace admin creates it in Settings → API and webhooks → Integrations, ticks the objects (and if needed the fields) it may read or write, and the Read the data model right. The key is shown once. See Authentication.
- Read the data model.
GET /objects, thenGET /objects/{object}for each object you sync: it gives every key, its type, whether it is writable, and an example of its value. See Objects. - Import. Page through
GET /objects/{object}/records?limit=100, followingnext_cursoruntil it is absent. Sorted by creation date, this list is paginated by key: no record is skipped or returned twice, even if the data changes while you page. A record created after the import started may not be in it: create the webhook destination of step 4 before you start, and nothing falls between the two. - Follow changes with webhooks. Create a destination subscribed to
<object>.created,<object>.updatedand<object>.deleted. Each delivery carries the record in the same shape asGET. See Webhooks. - Read what you do not know yet. A webhook or a relation points to an id you have never seen:
GET /objects/{object}/records/{id}. - Catch up on deletions. After downtime, or if you poll instead of using webhooks,
GET /objects/{object}/deleted?since=<ISO-8601>lists what was deleted (id,deleted_at,merged_into). Deletions are kept 30 days. - Write back.
PUT /objects/{object}/recordsto create or update by identity (email, domain...),PATCH .../{id}for a partial update,POSTwith anIdempotency-Keyto create safely. See Upsert and idempotency.
Stay within the rate limits: 10 requests per second per integration.
Reference
- Authentication: integrations, keys, rights, field selection
- Objects: discovering the data model
- Records: list, read, create, update, delete
- Upsert and idempotency: identity-based upsert, safe retries
- Webhooks: outbound destinations, deliveries, signatures
- Errors: the envelope, every code and its status
- Rate limits: ceilings, headers, and how to back off