# API Overview



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](/docs/api-reference/authentication).

Base URL [#base-url]

```
https://platform.kasar.app/api/v1
```

Every path in this reference is relative to it. Use HTTPS.

```bash
curl https://platform.kasar.app/api/v1/objects \
  -H "Authorization: Bearer ksr_svc_..."
```

Versioning [#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`](/docs/api-reference/objects).

Responses can gain keys over time. **Ignore keys you do not know**; do not fail on them.

Endpoints [#endpoints]

Data model and records [#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](/docs/api-reference/records), [Upsert and idempotency](/docs/api-reference/upsert).

Webhook destinations [#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`, `replay` (every failure of a period) |
| `GET`                      | `/webhooks/destinations/{id}/deliveries`                    | Deliveries, filtered by status, event and period, paginated by cursor            |
| `GET`                      | `/webhooks/destinations/{id}/deliveries/{deliveryId}`       | One delivery with all its attempts                                               |
| `POST`                     | `/webhooks/destinations/{id}/deliveries/{deliveryId}/retry` | Replay a failed or expired delivery                                              |

Details: [Webhooks](/docs/api-reference/webhooks).

Automations and sequences [#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](/docs/api-reference/webhooks).

Response envelope [#response-envelope]

A single resource comes back under `data`:

```json
{
  "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:

```json
{ "data": [ { "id": "..." }, { "id": "..." } ], "next_cursor": "k1.WyIyMDI2LTEwLTEz..." }
```

An error always has the same shape, and the HTTP status follows from its `code`:

```json
{
  "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](/docs/api-reference/errors).

Headers on every response [#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](/docs/api-reference/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 [#json-conventions]

* **Envelope keys are snake\_case** (`next_cursor`, `field_errors`). Record keys are the keys `GET /objects/{object}` lists for your data model.
* **Date-times are ISO-8601 in UTC**, to the microsecond, always with six decimals: `"2026-10-13T09:30:00.123456Z"`. The fixed width makes two values from Kasar compare as strings in time order. 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_LARGE` beyond).

Pagination [#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) or by `updated_at` is paginated by key, with no depth limit. The details, and `total`, are in [Records](/docs/api-reference/records).

A typical integration [#a-typical-integration]

1. **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](/docs/api-reference/authentication).
2. **Read the data model.** `GET /objects`, then `GET /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](/docs/api-reference/objects).
3. **Import.** Page through `GET /objects/{object}/records?limit=100`, following `next_cursor` until 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.
4. **Follow changes with webhooks.** Create a destination subscribed to `<object>.created`, `<object>.updated` and `<object>.deleted`. Each delivery carries the record in the same shape as `GET`. See [Webhooks](/docs/api-reference/webhooks).
5. **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}`.
6. **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.
7. **Write back.** `PUT /objects/{object}/records` to create or update by identity (email, domain...), `PATCH .../{id}` for a partial update, `POST` with an `Idempotency-Key` to create safely. See [Upsert and idempotency](/docs/api-reference/upsert).

Stay within the [rate limits](/docs/api-reference/rate-limits): 10 requests per second per integration.

Reference [#reference]

* [Authentication](/docs/api-reference/authentication): integrations, keys, rights, field selection
* [Objects](/docs/api-reference/objects): discovering the data model
* [Records](/docs/api-reference/records): list, read, create, update, delete
* [Upsert and idempotency](/docs/api-reference/upsert): identity-based upsert, safe retries
* [Webhooks](/docs/api-reference/webhooks): outbound destinations, deliveries, signatures
* [Errors](/docs/api-reference/errors): the envelope, every code and its status
* [Rate limits](/docs/api-reference/rate-limits): ceilings, headers, and how to back off
