Skip to content

Conventions

Pagination, filtering and sorting

Every list endpoint pages with cursors, filters on the fields its reference documents, sorts by one field and searches with q. The rules are the same everywhere.

Pagination

Lists return up to limit records — 50 unless you ask for between 1 and 100 — and a meta object. When meta.has_more is true, pass meta.next_cursor back as cursor to get the next page. On the last page next_cursor is null.

GET /api/v1/deals?limit=1
{
  "data": [
    {
      "id": "8f2d6c1a-4b7e-4f0a-9c3d-2e1b5a6f7c80",
      "title": "Northwind Analytics — annual plan",
      "value": 24000,
      "currency": "EUR",
      "status": "in_progress",
      "pipeline": {
        "id": "9d4b2f7a-3c8e-4b1d-a6f2-5e7c9b0d1a34",
        "name": "New business"
      },
      "stage": {
        "id": "5b1e9f3c-2a7d-4c6e-8f1b-3d9a0c4e7b21",
        "name": "Proposal",
        "type": "open"
      },
      "pipeline_id": "9d4b2f7a-3c8e-4b1d-a6f2-5e7c9b0d1a34",
      "stage_id": "5b1e9f3c-2a7d-4c6e-8f1b-3d9a0c4e7b21",
      "probability": 60,
      "expected_close_date": "2026-11-27",
      "closed_at": null,
      "forecast_category": "best_case",
      "loss_reason": null,
      "competitor": null,
      "contact_id": "3f1c9a7e-2b4d-4c8e-9f60-1a2b3c4d5e6f",
      "company_id": "2b7e4c9a-5d1f-4a8e-9b3c-7f0a1d2e4c56",
      "owner_id": "0c4f8e2a-7b3d-4a1f-9e6c-5d2b8a1f3e47",
      "custom_fields": {
        "seats": 40
      },
      "created_at": "2026-10-01T10:12:00Z",
      "updated_at": "2026-10-09T15:40:22Z"
    }
  ],
  "meta": {
    "limit": 1,
    "has_more": true,
    "next_cursor": "eyJpZCI6IjNmMWM5YTdlIiwiY3JlYXRlZF9hdCI6IjIwMjYtMDktMTRUMDk6MzA6MDBaIn0"
  }
}
  • limitinteger

    The page size used.

  • has_moreboolean

    Whether more records follow.

  • next_cursorstringNullable

    Pass as cursor for the next page; null on the last page.

  • totalinteger

    The number of matching records, when the endpoint can count them cheaply. It may be absent; never use it to decide whether to fetch more.

Cursors are opaque: do not build or change them. A cursor carries the sort of the request that made it, so keep sort, order, filters and q the same while you page.

all-deals.mjs
async function* allDeals(params = {}) {
  let cursor;
  do {
    const query = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) });
    const response = await fetch(`https://acme.nextora.co/api/v1/deals?${query}`, {
      headers: { Authorization: `Bearer ${process.env.NEXTORA_API_KEY}` },
    });
    const { data, meta } = await response.json();
    yield* data;
    cursor = meta.next_cursor;
  } while (cursor);
}

for await (const deal of allDeals({ status: 'in_progress' })) {
  console.log(deal.title, deal.value);
}

Older integrations may page with offset instead of cursor; it is still accepted and returns the same records. New code should use cursors.

Filtering

Filter with a query parameter per field: ?status=in_progress matches exactly. Add an operator in brackets for anything else: ?value[gte]=10000. Filters combine with AND.

OperatorMatchesExample
eqEqual — the same as no operator.status[eq]=won
neqNot equal.status[neq]=lost
gt, gteGreater than; greater than or equal.value[gte]=10000
lt, lteLess than; less than or equal.expected_close_date[lte]=2026-12-31
likeContains the text, ignoring case.name[like]=north
inAny of a comma-separated list.status[in]=won,lost

Each list endpoint documents the fields it filters on, under Filters. Any other field — or a value of the wrong type — is a 400 validation_failed that names the parameter, never a silently unfiltered list.

Open deals over 10,000 closing this year
curl -G https://acme.nextora.co/api/v1/deals \
  -H "Authorization: Bearer $NEXTORA_API_KEY" \
  -d status=in_progress \
  -d 'value[gte]=10000' \
  -d 'expected_close_date[lte]=2026-12-31'

The JSON filter

For OR logic, emptiness checks, date windows and custom fields, pass filter: a JSON object of conditions, URL-encoded. It works alongside the field filters.

filter — new or contacted leads from the last 30 days, with an email
{
  "conjunction": "and",
  "conditions": [
    {
      "field": "status",
      "operator": "any_of",
      "value": [
        "new",
        "contacted"
      ]
    },
    {
      "field": "created_at",
      "operator": "last_n_days",
      "value": 30
    },
    {
      "field": "email",
      "operator": "is_not_empty"
    }
  ]
}
  • conjunctionenumRequired

    How the conditions combine.

    One ofandor

  • conditionsobject[]Required

    The conditions.

    • fieldstring

      A documented field of the resource, or a custom field’s key.

    • See the operators below.

    • The value to compare with, in the format the operator expects.

OperatorValueMatches
eq, neqA valueEqual, not equal. Text compares without case.
contains, not_containsTextContains the text, or does not.
starts_with, ends_withTextStarts or ends with the text.
any_of, none_ofAn arrayEquals one of the values, or none of them.
gt, gte, lt, lteA number or dateComparisons.
between[from, to]Inclusive range.
last_n_daysA numberA date or time within the last N days.
is_empty, is_not_emptyNoneHas no value, or has one.
checked, not_checkedNoneA checkbox field is on, or off.

Negative operators — neq, not_contains, none_of — also match records where the field is empty. A filter that is not valid JSON, or that uses an unknown field or operator, is a 400 validation_failed.

Leads matching the JSON filter
curl -G https://acme.nextora.co/api/v1/leads \
  -H "Authorization: Bearer $NEXTORA_API_KEY" \
  --data-urlencode 'filter={"conjunction":"and","conditions":[{"field":"status","operator":"any_of","value":["new","contacted"]},{"field":"created_at","operator":"last_n_days","value":30},{"field":"email","operator":"is_not_empty"}]}'
Response200 OK
{
  "data": [
    {
      "id": "a7c9e1b3-5f2d-4a6c-8e9b-2d4f6a8c0e13",
      "name": "Grace Whitfield",
      "email": "[email protected]",
      "phone": "+44 161 496 0732",
      "company_name": "Harbor Logistics",
      "status": "new",
      "source": "website",
      "score": 0,
      "owner_id": "0c4f8e2a-7b3d-4a1f-9e6c-5d2b8a1f3e47",
      "custom_fields": {
        "budget": 15000
      },
      "created_at": "2026-10-10T07:58:31Z",
      "updated_at": "2026-10-10T07:58:31Z"
    }
  ],
  "meta": {
    "limit": 50,
    "has_more": false,
    "next_cursor": null
  }
}

sort takes one of the fields listed on the endpoint, and order is asc or desc. The default is the newest first: sort=created_at&order=desc. An unlisted field is a 400 validation_failed.

Deals closing soonest first
curl -G https://acme.nextora.co/api/v1/deals \
  -H "Authorization: Bearer $NEXTORA_API_KEY" \
  -d status=in_progress \
  -d sort=expected_close_date \
  -d order=asc