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:

ParameterMeaning
limitPage size. Default 50, at most 250; larger values are treated as 250.
cursorThe 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_cursor or validation_failed.
  • Lists have no total count, except the query endpoint below. The first page of GET /v1/leads also has summary with the count and sum of amount of 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": "…"
}
  • query is a group: op (and or or), conditions (each with field, op, value and optional not) and nested groups, 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}/fields lists the fields you can filter and sort by, with their types.
  • Limits: 50 conditions, 3 regex conditions, 100 values in a list and 5 sort keys. Breaking one answers 422 with a code such as too_many_conditions and limit.
  • count: true adds total. Above 100,000 matches it is an estimate and total_estimated is true.
  • 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.