Skip to main content
Help Centers

Search articles

Search help-center articles by structured filters and/or hybrid full-text search, with sort and cursor pagination.

POST
/v2/help_center/articles/search

This endpoint mirrors POST /v2/conversations/search. Two modes, dispatched off whether you pass search:

  • Filter mode (no search): the structured query AST is translated to a Mongo predicate and paginated by sort + cursor. Same semantics as POST /v2/companies/search and POST /v2/contacts/search.
  • Search mode (search set): the free-text query runs as a hybrid (vector + BM25) search against the same article namespace the help-center copilot uses. Hits are then survivor-pruned in Mongo against the structured query AST and organizationId.

query and search are independent and may be combined freely. An empty body lists everything for the org.

Hybrid full-text search (search)

{ "search": "how to set up sso" }
  • Free-form text, 1–500 chars. Server-managed: no operators, no field selection.
  • Hybrid (vector embedding + BM25 keyword) ranking. Results are returned in relevance order unless you also pass sort, in which case the relevance-matched survivor set is reordered along that axis.
  • Top 200 ranked articles are kept per request and paginated in-memory; the same ranking is stable across pages.
  • May be combined with query to scope (e.g. search + { help_center_id = "..." }).

Structured query AST

Each clause has the shape { field, operator, value }. You can pass a single clause or wrap up to 15 in a top-level AND group:

{
  "query": {
    "operator": "AND",
    "value": [
      { "field": "help_center_id", "operator": "=", "value": "j7c5g8ah3ewxp4lo" },
      { "field": "state", "operator": "=", "value": "draft" },
      { "field": "updated_at", "operator": ">", "value": 1735689600 }
    ]
  },
  "sort": "updated_at:desc",
  "limit": 20
}

Top-level OR groups and nested groups are not supported in this version.

Supported AST fields and operators

FieldTypeOperators
idstring (article id)=, !=, IN, NIN
help_center_idstring=, !=, IN, NIN
parent_idstring (collection id)=, !=, IN, NIN
author_idstring=, !=, IN, NIN
slugstring=, !=, IN, NIN
stateenum (live | draft)=, !=, IN, NIN
created_atunix seconds=, !=, >, <, >=, <=
updated_atunix seconds=, !=, >, <, >=, <=

There is no AST-level full-text field. The string operators ~, !~, ^, $ are reserved for future structured-text use and currently return 400 - use the top-level search sibling for free-text matching.

state is derived from each help center's default-locale translation: state = "live" matches articles with a published live translation in their HC's default locale; state = "draft" matches the draft equivalent. Same semantics as the state query param on GET /v2/help_center/articles.

Sort

Allowed values: created_at:desc (default in filter mode), created_at:asc, updated_at:desc, updated_at:asc.

In search mode, omitting sort returns results in relevance order. Passing sort reorders the relevance survivor set chronologically.

The chosen mode + sort axis is encoded in the cursor; switching mode or axis mid-pagination returns 400 invalid_cursor. Restart pagination without a cursor when changing.

Pagination

Cursor-based, limit between 1 and 100 (default 10). In filter mode, totalCount is approximate and capped at 5 000. In search mode, totalCount is exact within the 200-row top-K window and totalCountCapped is true when that cap is hit.

Response

Returns a standard list envelope with article rows identical to GET /v2/help_center/articles/{id} (including translations, hydrated against each article's parent help center default locale).

Version Availability

This endpoint is only available in API version 2026-01-01.nova and newer.

Authorizationstringheaderrequired

API key as Bearer token. Use: Authorization: Bearer sk_...

Featurebase-Versionenum<string>header

API version for this request. Defaults to your organization's configured API version if not specified.

Available options: 2026-08-19.orbit, 2026-01-01.nova, 2025-12-12.clover
Example: 2026-08-19.orbit

Body

application/json
cursorstring

An opaque cursor for pagination. Use the nextCursor value from a previous response to fetch the next page of results. The pagination keyset (relevance score in search mode, sort-axis value in filter mode) is encoded in the cursor; switching mode mid-pagination returns 400 invalid_cursor.

Maximum string length: 512
Example: eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9
limitintegerdefault:10

A limit on the number of objects to be returned, between 1 and 100.

Required range: 1 <= x <= 100
Example: 10
queryobject

Structured filter AST. Either a single filter clause or one top-level AND group (max 15 clauses). Top-level OR groups are not yet supported.

Plain-text full-text search across article title, description, and body. Hybrid (vector + BM25) ranking via the same Turbopuffer namespace the help-center copilot uses - server-managed, no operators, no field selection. When set, results come back ranked by relevance unless sort is also passed (in which case the relevance-matched set is reordered along that axis).

Required string length: 1 - 500
Example: how to set up sso
sortenum<string>

Sort field + direction. Defaults to created_at:desc. The chosen sort axis is encoded in the cursor; switching sort mid-pagination returns 400 invalid_cursor.

Available options: created_at:desc, created_at:asc, updated_at:desc, updated_at:asc
Example: created_at:desc

Response

application/json

Success

dataobject[]required

Array of search results

nextCursorstring | nullrequired

Cursor for fetching the next page (null if no more results)

Example: eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9
objectenum<string>required

Object type identifier

Available options: list
Example: list
totalCountinteger

Total number of articles matching the query, capped at 5000. When the actual total is at or above the cap, totalCountCapped is true and the value is exactly the cap.

Example: 42
totalCountCappedboolean

True when totalCount is exactly the cap and the real count may be higher. UI can render as e.g. "5000+".

Example: false