KasarKasar Docs
API Reference

Errors

The error envelope, every error code with its HTTP status and when it happens, and which errors are worth retrying.

Every error the API answers uses the same envelope, and its HTTP status follows from the machine-readable code:

{
  "error": {
    "code": "INVALID_ENUM_VALUE",
    "message": "Invalid value(s) for 'type': prospect. Allowed values: new_business, upsell, cross_sell, renewal, migration.",
    "available_values": ["new_business", "upsell", "cross_sell", "renewal", "migration"]
  }
}
MemberAlwaysMeaning
codeyesMachine-readable code, listed below. Branch on it.
messageyesFor people. Its wording can change, and a few messages follow the request's Accept-Language (English by default). Never parse it.
suggestionnoWhat to do next
available_valuesnoThe allowed values, on INVALID_ENUM_VALUE
field_errorsno[{ "field", "message" }], one entry per refused field
existing_idnoOn DUPLICATE_RECORD: the record that already holds the value, when the integration can read it
matching_idsnoOn AMBIGUOUS_IDENTITY: every record the identity matches

Timing never travels in the body: when the API wants you to wait, it says so in the standard Retry-After header (seconds).

X-Request-Id

Every response carries an X-Request-Id header, errors included: a unique id for that request. Log it with the request on your side, and quote it when you contact support.

Codes by status

A code that is not listed in a status below is a 400. If you meet a code you do not know, act on its HTTP status.

400 Bad Request

CodeWhen
INVALID_BODYThe body is missing, is not valid JSON, or is not a JSON object
INVALID_PARAMETERA query parameter has the wrong shape: limit outside 1 to 100, sort_dir other than asc or desc, include_total other than true or false, filters that is not a JSON filter group, sort that is not a JSON array of { "field", "direction" }, since missing, unreadable, or older than 30 days. Also sent when Idempotency-Key and X-Idempotency-Key carry different values.
INVALID_CURSORThe cursor is not one the API issued. Also: a cursor of a list sorted by creation date (or of /deleted) replayed with other filters, another direction, another object or another since; or such a cursor sent to a search or a custom sort.
DEEP_PAGINATION_LIMITA search or a custom sort paged past 10,000 rows. Narrow the query with filters.
INVALID_QUERY_FIELDA filter, a sort or a grouping names a field the object does not have, a field the integration cannot read (outside its field list, or confidential), or one that cannot be filtered or sorted. These answer the same, so an error never tells that a field exists. The message names it.
INVALID_FILTER_OPERATORA filter uses an unknown operator, or one the field's type cannot carry (contains on a boolean, greater_than on a multi-select…). The message names the field.
INVALID_FILTER_VALUEA filter value does not fit its field (text on a date, a non-UUID on a relation...)
ORGANIZATION_HEADER_NOT_SUPPORTEDThe request carries X-Kasar-Organization. An integration belongs to one workspace: remove the header.
DESTINATION_LIMIT_REACHEDThe workspace already has the maximum number of webhook destinations

401 Unauthorized

CodeWhen
UNAUTHENTICATEDNo Authorization: Bearer ... header
INVALID_TOKENThe key is malformed, unknown, closed or expired, or its integration was revoked. One code for all, on purpose.

403 Forbidden

CodeWhen
FORBIDDEN_GRANTThe integration lacks the right the route needs. The message names it: contacts:write, schema:read...
PERMISSION_DENIEDA write touches a field the integration can read but not write: a pipeline whose status alone was chosen. The message names the field. A field it cannot read at all is 422 UNKNOWN_FIELD.
WORKSPACE_INACCESSIBLEThe integration's workspace no longer exists
PLAN_LIMIT_REACHEDThe workspace plan's limit is reached (records of an object...). suggestion gives the upgrade path when there is one.
PLAN_FEATURE_NOT_INCLUDEDThe workspace plan does not include the feature the request needs

404 Not Found

CodeWhen
INVALID_OBJECTOn the schema routes, an object that does not exist, that the API does not serve, or that the integration cannot read: the three answer the same. On the record routes, an object that is granted but not served by the API.
RECORD_NOT_FOUNDThe record in the path does not exist, or its id is not a UUID. A DELETE of a record already deleted answers it too.
FIELD_NOT_FOUNDThe {field} of /records/{id}/{field} is not an email, phone or LinkedIn field of the object, or one the integration cannot read: the two answer the same. suggestion lists the ones it can.
NOT_FOUNDA webhook destination or a delivery that does not exist; an email, phone or LinkedIn entry that does not exist or belongs to another record; or an unknown action segment in the path (the message lists the valid ones)

409 Conflict

CodeWhen
DUPLICATE_RECORDThe write would create a second record with the same identity (a contact's email, a company's domain...) or the same unique value, or it adds to a record an email, a phone or a LinkedIn profile that identifies another record. existing_id names that record when the integration can read it.
AMBIGUOUS_IDENTITYA PUT upsert matches several records. Nothing is written; matching_ids lists them.
DELETE_BLOCKEDThe record cannot be deleted: records point to it through a required relation. The message names them.
IDEMPOTENCY_KEY_REUSEDThe idempotency key was already used, in the last 24 hours, with a different request
IDEMPOTENCY_KEY_IN_PROGRESSA request with the same idempotency key is still running. Comes with Retry-After: 2.
DESTINATION_IN_USEThe webhook destination is used by published workflows, named in the message
CONFLICTThe resource is not in the state the gesture needs: a delivery that is not abandoned, and similar

413 Payload Too Large

CodeWhen
PAYLOAD_TOO_LARGEThe body is over 256 KB (262,144 bytes). Refused before it is read when Content-Length announces it.

422 Unprocessable Entity

CodeWhen
VALIDATION_ERROROne or more values are refused: a malformed email, a value of the wrong type, a missing required value, a body that does not match the expected shape, a pipeline step that does not belong to the pipeline written with it. Details in field_errors.
UNKNOWN_FIELDThe body names a key the object does not have, or one the integration cannot read (outside its field list, or confidential): the two answer the same. The message names it.
READONLY_FIELDThe body names a read-only key: id, created_at, updated_at, a computed field, a pipeline status... Leave them out of writes, even unchanged.
INVALID_ENUM_VALUEA select value outside the field's options. available_values lists the allowed ones.
INVALID_REFERENCEA record cited in the body (a relation id, a link, a pipeline or a pipeline step) does not exist. For a link of a many-to-many field, also a record the integration cannot read: the two answer the same. Same code on create, update and upsert.
MISSING_REQUIRED_FIELDSMoving a record to this pipeline step requires fields that are empty. field_errors lists them.
IDENTITY_KEY_NOT_SATISFIEDA PUT upsert body does not fully provide any identity key of the object, or the object has no identity rule

429 Too Many Requests

CodeWhen
RATE_LIMITEDA rate limit is exceeded. Retry-After gives the wait, X-RateLimit-Policy the limit. See Rate limits.

500 Internal Server Error

CodeWhen
WRITE_FAILEDA write failed for a reason that is not a refusal of your data, or a delete failed for another reason than a required relation
INTERNAL_ERRORAn unexpected error. A database error is reported as "A database error occurred.", without its SQL.

503 Service Unavailable

CodeWhen
SERVICE_UNAVAILABLEKasar could not check the key because its database is unavailable. Comes with Retry-After: 5. Your key is still valid.

Examples

A duplicate, with the record that holds the value:

{
  "error": {
    "code": "DUPLICATE_RECORD",
    "message": "A record already exists with the same value for: emails",
    "existing_id": "3c1d6f0a-8b2e-4f57-9a61-0e4b7c2d5f18"
  }
}

An ambiguous upsert:

{
  "error": {
    "code": "AMBIGUOUS_IDENTITY",
    "message": "2 records match emails: none was written.",
    "suggestion": "Merge the duplicates, or update the right one by id (PATCH).",
    "matching_ids": ["3c1d6f0a-8b2e-4f57-9a61-0e4b7c2d5f18", "9e0b2a47-1d3c-4f86-b5a2-7c6e1f0d3b94"]
  }
}

What the API will not tell you

Some silences are deliberate:

  • 401 INVALID_TOKEN never says whether a key was unknown, closed or expired.
  • On the schema routes, an object the integration cannot read answers 404 INVALID_OBJECT exactly like one that does not exist.
  • On the record routes, an object the integration was not granted answers 403 FORBIDDEN_GRANT, whether it exists or not.
  • DUPLICATE_RECORD carries existing_id only when the integration can read that record.
  • A filter or a sort on a field the integration cannot read answers INVALID_QUERY_FIELD, like a field that does not exist; writing it answers UNKNOWN_FIELD, like a misspelled key. A many-to-many link to a record it cannot read answers INVALID_REFERENCE, like a record that does not exist.

A path that is not an API route, or a method a route does not define, is answered 404 or 405 by the web server, outside this envelope.

Retrying

AnswerRetry?
429 RATE_LIMITEDYes, after Retry-After seconds
503 SERVICE_UNAVAILABLEYes, after Retry-After seconds (5)
409 IDEMPOTENCY_KEY_IN_PROGRESSYes, after Retry-After seconds (2), with the same key and the same body: you then receive the stored response of the first request
500 WRITE_FAILED, 500 INTERNAL_ERROR, network errors, timeoutsYes, with exponential backoff and jitter. For a create, retry safely: see below.
409 DUPLICATE_RECORDNot as is: on a create, update existing_id with PATCH, or write with PUT (upsert by identity); on an added email, phone or profile, the value belongs to existing_id: merge the two records in Kasar, or leave the value where it is
409 AMBIGUOUS_IDENTITYNot as is: merge the duplicates in Kasar, or PATCH the right record by id
404 RECORD_NOT_FOUND on a retried DELETENo: the first attempt deleted the record
Any other 4xxNo: the request itself is wrong. Fix it first.

Retrying a write safely:

  • POST and PUT accept an Idempotency-Key header (or X-Idempotency-Key). If the first request succeeded and you lost its response, a retry with the same key and body replays the stored response, with X-Idempotency-Replayed: true, and writes nothing. An error response is not stored: a retry with the same key runs the request again.
  • PUT recognizes an existing record by its identity, so a retried upsert updates the record the first attempt created.
  • PATCH sets the same values again.

Details: Upsert and idempotency.

On this page