Skip to main content
CornectAPI Docsv1
Sign inGet an API tokenGet started free

Changelog

Notable changes to the Cornect API. Additive changes land in v1 continuously and are not all listed here; anything that can affect a working integration is. Dates are the day a change shipped, not the day it was written up here.

2026-08-05#

Pagination cursors are signed — hand-built cursors now rejected#

You may see this as a one-off 422
If you were mid-scan when this shipped, the next page of a lookalike search returns 422 INVALID_CURSOR once. Restart that scan from the first page.

Lookalike search previously returned a cursor that was a readable offset, and the API accepted any value a caller wrote by hand. Cursors are now signed and tamper-checked. A cursor that was not issued by the API — edited, constructed, or replayed from a different sort — is rejected rather than silently returning a different page than the one you asked for.

json
{
  "detail": {
    "code": "INVALID_CURSOR",
    "message": "This pagination cursor is not valid. Cursors are issued by the API in next_cursor and must be passed back unmodified — they cannot be constructed or edited by the caller. Restart from the first page."
  }
}

What to do: pass next_cursor back exactly as received and never build one yourself. If you see this error, start the scan again from the first page. See Pagination.

Search cursors are bound to the sort that produced them#

Changing sort mid-scan is now a 422
A next_cursor from a search sorted one way, replayed against a different sort_by or sort_order, returns 422 CURSOR_SORT_MISMATCH. Send the original sort with each page, or restart from the first page.

A cursor carries the previous page's sort values, so it only means anything against the sort that produced it. Paging an employee_count cursor into a name sort used to compare an integer against a keyword — an error, or silently wrong ordering. The cursor now records the sort it was minted under, and a mismatch is reported rather than guessed at. relevance counts as two different sorts: with a query it means score, without one it means data quality.

json
{
  "detail": {
    "code": "CURSOR_SORT_MISMATCH",
    "message": "This cursor was created under a different sort (employee_count:desc:f) than this request (name:asc:f). A cursor is only valid for the sort that produced it — restart from the first page, or re-send the original sort_by/sort_order.",
    "details": {
      "cursor_sort": "employee_count:desc:f",
      "request_sort": "name:asc:f"
    }
  }
}

API tokens default to a 90-day expiry#

New tokens expire; existing ones do not change
A token created without an explicit expiry used to never expire. New tokens now last 90 days by default. Existing tokens are unchanged — this affects tokens created from now on.

Tokens are created in the app, at Account → API tokens, where you can set the expiry. Token management is not part of the public API: those endpoints authenticate with a logged-in session, not with a cornect_ token, so a token cannot mint another one.

A token that never expires is a permanent workspace credential, and it was what you got by leaving a field alone. The safe option is now the default and the permanent one has to be asked for by name:

json
// 90 days (the new default)
{ "name": "ci" }

// an explicit lifetime, 1-365 days
{ "name": "ci", "expires_in_days": 30 }

// never expires — now requires the explicit flag
{ "name": "ci", "never_expires": true }

Passing both never_expires and expires_in_days is a 422: there is no safe reading of that combination. The create response includes expires_at, so you can always confirm what you got.

What to do: if your automation relies on a non-expiring token, add "never_expires": true to the create call. Otherwise rotate on the 90-day cycle — read expires_at from the response and diary it.

Unknown request fields are rejected#

Request bodies used to ignore fields they did not recognise. A misspelled filter therefore vanished silently — and on POST /exports that meant the export widened to the full candidate set and was billed accordingly. Unknown fields now return a 422 naming the field.