Get started
Requests and responses
JSON in, JSON out, over HTTPS. This page covers the conventions every endpoint shares: ids, times, money, partial updates, languages and request ids.
JSON
- Send request bodies as one JSON object with
Content-Type: application/json. Responses areapplication/json; charset=utf-8. - Bodies are limited to 1 MB (
body_too_large). - Unknown fields are rejected, not ignored: a misspelled field answers
400unknown_fieldwith the field named infields. Send only the fields an endpoint documents, never whole objects copied from a response. - A value of the wrong type (a string where a number is expected) answers
400bad_json. - Creating returns
201 Createdwith the new object. Reading and changing return200 OK. Deleting returns200 OKwith a small confirmation such as{"ok": true, "id": "ld_…"}or{"id": "wh_…", "deleted": true}.
Ids
Every id is <prefix>_<26 chars>: a short type prefix, an underscore and 26 lowercase letters and digits, for example ld_01j8z7c2d4f6g8h0j2k4m6n8p0. The prefix tells you what the id points to:
| Prefix | Object | Prefix | Object |
|---|---|---|---|
ld_ | Lead | tk_ | Task |
ct_ | Contact | nt_ | Note |
co_ | Company | invoice_ | Invoice |
pl_ | Pipeline | pr_ | Product |
st_ | Stage | fd_ | Custom field |
cv_ | Conversation | msg_ | Message |
ch_ | Channel | mb_ | Member of the workspace |
wh_ | Webhook | dlv_ | Webhook delivery |
key_ | API key | ws_ | Workspace |
app_ | App (its OAuth client id) | ins_ | App installation |
The API still accepts the older long prefixes (lead_…, contact_…, company_…, task_…, hook_… and so on) everywhere it takes an id, so ids you stored before keep working. Responses always use the short form.
Ids are canonical: store and send ids. They are sortable by creation time and never reused. Treat them as opaque strings: store them as they are and compare them exactly.
Leads, contacts, companies and tasks also have a per-workspace number (shown as #1042). Their single-record routes accept it in place of the id, for example GET /v1/leads/1042. Numbers are a convenience for people; ids are canonical. See record numbers.
An id of the wrong type in a path answers 404 not_found or 400 invalid_id_type.
Record numbers
Leads, contacts, companies and tasks carry a number: an integer, unique per workspace, shown to people as #1042. It is assigned on create and never reused. A restored record keeps its number, and so does a record merged away.
GET, PATCH and DELETE /v1/{leads|contacts|companies|tasks}/{id}, and their sub-routes, accept the plain number in place of the id: GET /v1/leads/1042. The number is resolved only inside the authenticated workspace; an unknown number answers the same 404 as an unknown id.
Numbers are a convenience for people; ids are canonical. Webhooks, forms, portal and share links, and idempotency use ids only.
Times
Times are RFC 3339 strings with fractional seconds, for example "2026-09-27T09:41:12.482913Z". Use a real RFC 3339 parser: the number of fractional digits varies. Fields that can be empty are null, for example closed_at of an open lead. When you send a time (a task's due_at), include the offset.
Money
Amounts are integers in the smallest unit of their currency, next to a three-letter currency code. For Uzbek soʻm that unit is the tiyin: 1 soʻm is 100 tiyin, so 1,500,000 soʻm is sent and returned as 150000000.
{ "amount": 150000000, "currency": "UZS" }This applies to a lead's amount, a product's price, and an invoice's amount, paid_amount and line price. Never send amounts as decimals or strings. A lead without a currency uses the workspace's currency.
Partial updates
PATCH changes only the fields you send; every other field keeps its value.
- To clear a reference such as a lead's
owner_id,contact_idorcompany_id, send it asnull. customholds the values of custom fields, keyed by the field'skey. In a PATCH, only the keys you send change.- Names that Kweko shows in several languages (pipelines, stages, loss reasons, field labels) are objects keyed by locale, for example
{"uz-Latn": "Yangi", "ru": "Новая", "en": "New"}. When you write one, you can send a plain string: it is used for every language.
Deleting and restoring
Deleting a lead, contact or company moves it to the trash: it disappears from lists, reading it answers 410 gone with deleted_at, and POST /v1/{leads|contacts|companies}/{id}/restore brings it back.
Languages
Error messages and a few labels follow the Accept-Language header: English (en, the default), Russian (ru) or Uzbek (uz). Codes such as validation_failed and field names never change with the language, so branch on codes, not on messages.
Request ids
Every response has an X-Request-Id header, and every error body repeats it as request_id. Quote it when you contact support. You can send your own X-Request-Id (up to 64 letters, digits, -, _ or .) to trace a call across your systems; Kweko uses and returns it.
Retries and idempotency
The API has no general Idempotency-Key header yet.
GETrequests are always safe to retry.- Sending a message accepts a
client_id(up to 64 characters): a retry with the sameclient_idreturns the message that was already queued instead of sending it twice. See Conversations. - For other writes, a retry after a timeout can create a duplicate. Look the record up first (for example by your own reference in
sourceor a custom field) or accept the rare duplicate. - Retry
429and5xxanswers with exponential backoff; for429, wait at leastRetry-Afterseconds.
Versioning
The version is in the path (/v1). Within v1, new fields, endpoints, event types and error codes can appear at any time; changes that would break an integration are meant for a new version. Write your code to ignore unknown fields and to handle unknown error codes by their HTTP status. Changes are listed in the changelog.