Get started
Pagination and filtering
Lists return one page at a time. Pass the cursor from one page to get the next, until there is none.
Cursors
List endpoints take two query parameters:
| Parameter | Meaning |
|---|---|
limit | Page size. Default 50, at most 250; larger values are treated as 250. |
cursor | The next_cursor of the previous page. Leave it out for the first page. |
The response has the page in data and the cursor for the next page in next_cursor. When next_cursor is null (or an empty string, see below), you have the last page.
json
{
"data": [ { "id": "lead_01j8z7c2d4f6g8h0j2k4m6n8p0", "number": 1042, "name": "Website order #1042" } ],
"next_cursor": "eyJpZCI6IjAxOTI2Y2Q..."
}Node.js: read every lead
async function* allLeads(key) {
let cursor = ""
do {
const url = new URL("https://api.kweko.uz/v1/leads")
url.searchParams.set("limit", "250")
if (cursor) url.searchParams.set("cursor", cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } })
if (!res.ok) throw new Error((await res.json()).error.code)
const page = await res.json()
yield* page.data
cursor = page.next_cursor
} while (cursor)
}- Cursors are opaque: pass them back exactly as received (URL-encoded) and do not build them yourself. A changed cursor answers
invalid_cursororvalidation_failed. - Lists have no total count, except the query endpoint below. The first page of
GET /v1/leadsalso hassummarywith thecountandsumofamountof all matching leads. - Leads, contacts and companies are ordered newest first by default, which stays stable while you page. Conversations are ordered by activity, so a busy inbox can shift between pages.
- A few administrative lists (API keys, webhook deliveries) use a slightly different envelope:
{"object": "list", "data": [...], "next_cursor": "", "has_more": false}, with an empty string when there is no next page.
Filters on lists
Most lists take simple filters as query parameters, documented with each endpoint. For leads:
sh
curl "https://api.kweko.uz/v1/leads?pipeline=pipeline_01j8…&status=open&owner=unassigned&q=karimova" \
-H "Authorization: Bearer $KWEKO_API_KEY"The query endpoint
For filters that simple parameters cannot express, POST /v1/query/{entity} (with leads, contacts or companies) takes a filter tree and returns the same objects with cursor pagination. It needs the read scope of the entity.
Request
curl https://api.kweko.uz/v1/query/leads \
-H "Authorization: Bearer $KWEKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "op": "and", "conditions": [
{ "field": "amount", "op": "gte", "value": 100000000 },
{ "field": "source", "op": "in", "value": ["website", "instagram"] }
] },
"sort": [ { "field": "amount", "dir": "desc" } ],
"limit": 100,
"count": true
}'Response
{
"data": [ { "id": "lead_…", "amount": 250000000 } ],
"next_cursor": "…",
"total": 318,
"q": "…"
}queryis a group:op(andoror),conditions(each withfield,op,valueand optionalnot) and nestedgroups, at most 2 levels deep.- Operators:
eq,neq,in,not_in,all,contains,not_contains,starts,ends,empty,not_empty,regex,sounds_like,gt,gte,lt,lte,between,duplicate,weekday,hour. Which ones apply depends on the field's type. GET /v1/query/{entity}/fieldslists the fields you can filter and sort by, with their types.- Limits: 50 conditions, 3
regexconditions, 100 values in a list and 5 sort keys. Breaking one answers422with a code such astoo_many_conditionsandlimit. count: trueaddstotal. Above 100,000 matches it is an estimate andtotal_estimatedistrue.- The response echoes the filter as text in
q. The same text syntax is accepted as"q"instead of"query". - Slow filters are stopped with
query_too_slow: add a selective condition and try again.