Search conversations
Search conversations with a structured filter AST and an optional full-text query.
The endpoint has two modes that are picked automatically from the body:
- Filter mode (
queryset,searchomitted): structured filtering against indexed conversation fields. Sorted bylastActivityAtdesc by default; setsorttolastActivityAt:ascfor the inverse. - Search mode (
searchset,queryoptional): full-text search across conversation messages, then narrowed by the samequeryAST so the same filters apply. Results are ranked by relevance by default; setsortto order bylastActivityAtinstead (the same query string still gates which conversations appear, only their ordering changes).
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": "state", "operator": "=", "value": "open" },
{ "field": "adminAssigneeId", "operator": "=", "value": "507f1f77bcf86cd799439011" }
]
}
}Top-level OR groups and nested groups are not supported in this version.
Supported fields and operators
Field names are camelCase, matching the rest of the v2 public API. Snake_case names that this endpoint originally shipped with (admin_assignee_id, team_assignee_id, brand_id, tag_ids, mentioned_admin_ids, user_id, participant_id, company_id, created_at) are still accepted for back-compat but are deprecated — please migrate to camelCase in new code.
| Field | Type | Operators |
|---|---|---|
state | enum (open / closed / snoozed) | =, !=, IN, NIN |
priority | boolean | =, != |
adminAssigneeId | id or null (unassigned) | =, !=, IN, NIN |
teamAssigneeId | id or null (unassigned) | =, !=, IN, NIN |
brandId | id or null | =, !=, IN, NIN |
tagIds | id (matches any conversation containing this tag) | =, !=, IN, NIN |
userId / participantId | id of a conversation participant | =, !=, IN, NIN |
companyId | company id (matches conversations whose participants belong to the company) | =, !=, IN, NIN |
mentionedAdminIds | admin id | =, !=, IN, NIN |
createdAt | unix seconds | =, >, < |
customAttributes.<attributeId> | conversation custom attribute (see below) | =, !=, IN, NIN; plus >, <, >=, <= for number / date |
Filtering by custom attributes
Any non-archived conversation custom attribute can be used as a filter field via customAttributes.<attributeId>, where <attributeId> is the attribute's id from GET /v2/conversation_attributes. The accepted value shape follows the attribute's fieldSubType:
select/multi-select— pass an option label (e.g."Product question") or an option id; both are matched interchangeably, and id-based filters keep working after options are renamedtext— stringnumber— number; range operators (>,<,>=,<=) are supporteddate— ISO 8601 string or unix seconds; range operators are supportedcheckbox— boolean
Example — all conversations where the "Issue type" select attribute is "Product question":
{
"query": { "field": "customAttributes.6863d3fca1234b0d5e15c86c", "operator": "=", "value": "Product question" },
"include": ["customAttributeDefinitions"]
}Referencing an attribute id that does not exist (or has been archived) returns a field_unknown error. Set include to ["customAttributeDefinitions"] to get each returned conversation's attribute values resolved with names and types alongside the ID-keyed customAttributes map.
For unbounded fields a query consisting only of != or NIN clauses is rejected with query_too_broad to prevent full-org scans. Combine the negation with at least one positive clause (=, IN, >, <) instead. Bounded enum fields (state, priority) are exempt because their negation is naturally bounded.
Inbox visibility
An open-state filter uses the same visibility rule as the Featurebase dashboard inbox. Workflow and outbound-message shells are excluded until a customer or lead interacts with them. As a result, state = "open" (or an IN/NIN filter that resolves exclusively to open) returns the conversations shown in the dashboard's open inbox rather than unreplied campaign or automation shells. Searches without an open-only state filter keep their existing organization-wide semantics.
Pagination
Cursor-based, limit between 1 and 100 (default 10). Pagination cursors are mode-specific - reusing a filter-mode cursor in search mode (or vice versa) returns 400. totalCount is approximate and capped at 1000; totalCountCapped is true when the real count may be higher.
Response
Returns a slim conversation row optimised for inbox / list rendering - just the fields you need to render a row and link to the conversation. Fetch the full conversation via GET /v2/conversations/{id} when the user opens one.
Search-specific additions on each row:
conversationUser- the customer / lead the conversation is with. Use this for the row identity.surroundingConversationParts- context window of human messages around the matched message (≈15 before + 15 from the match onwards in search mode, sliding to first / last 30 if the match is near the conversation boundaries; falls back to the latest 30 messages in filter mode). EachpreviewMarkdownvalue is truncated server-side; fetch the full conversation for complete history and full bodies.matchingPartAuthor- author of the specific message that matched the search query (search mode only). May be a teammate or bot.matchingPart- reference to the matched message (search mode only).matchingBodyPreviewMarkdown- plain-text / markdown preview of the matched body (search mode only).relevanceScore- aggregate score (search mode only).
Version Availability
This endpoint is only available in API version 2026-01-01.nova and newer.
Body
An opaque cursor for pagination. Use the nextCursor value from a previous response to fetch the next page of results.
512eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9Optional expansions. Use customAttributeDefinitions to return customAttributeValues with names and types alongside ID-keyed customAttributes.
A limit on the number of objects to be returned, between 1 and 100.
1 <= x <= 10010Structured filter AST. Either a single filter clause or one top-level AND group. Use this for filtering by state, tagIds, adminAssigneeId, customAttributes.<attributeId>, etc. (Legacy snake_case names are accepted for back-compat — see the OpenAPI route description for the full alias map.)
Plain-text full-text search across conversation messages. Server-managed - no operators, no field selection.
1 - 500refund stripe checkoutResult ordering. When omitted, filter mode defaults to lastActivityAt:desc and search mode defaults to relevance ranking. When set, both modes order by lastActivityAt in the requested direction (so you can ask a search query to return its hits in chronological order). Snake_case last_activity_at:* is accepted for back-compat — prefer camelCase in new code.
lastActivityAt:desc, lastActivityAt:asc, last_activity_at:desc, last_activity_at:asclastActivityAt:descResponse
Success
Cursor for fetching the next page (null if no more results)
eyJpZCI6IjEyMzQ1In0=Total number of conversations matching the query, capped at 200. When the actual total is at or above the cap, totalCountCapped is true and the value is exactly the cap.
42