KasarKasar Docs
API Reference

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/v1

Every 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

MethodPathWhat it does
GET/objectsThe objects the integration can read
GET/objects/{object}One object's fields, in the exact shape records are read and written
GET/objects/{object}/recordsList records: filters, sort, search, cursor pagination
POST/objects/{object}/recordsCreate a record (201)
PUT/objects/{object}/recordsUpsert 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

MethodPathWhat it does
GET / POST/webhooks/destinationsList / 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}/deliveriesRecent delivery attempts
POST/webhooks/destinations/{id}/deliveries/{deliveryId}/retryReplay 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

HeaderMeaning
X-Request-IdA 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-PolicyThe 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 keys GET /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_LARGE beyond).

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

  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.
  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.
  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.
  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.

Stay within the rate limits: 10 requests per second per integration.

Reference

On this page