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 | =, >, < |
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.
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 ParametersJSON
An opaque cursor for pagination. Use the nextCursor value from a previous response to fetch the next page of results.
A limit on the number of objects to be returned, between 1 and 100.
Search conversations
curl https://do.featurebase.app/v2/conversations/search \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $FEATUREBASE_API_KEY" \
-d '{
"cursor": "eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9",
"limit": 10,
"search": "refund stripe checkout",
"sort": "lastActivityAt:desc"
}'{
"data": [
{
"id": "12345",
"adminAssigneeId": "507f1f77bcf86cd799439011",
"brandId": "507f1f77bcf86cd799439011",
"createdAt": "2025-01-15T10:30:00.000Z",
"lastActivityAt": "2025-01-15T12:30:00.000Z",
"object": "conversation",
"participants": [
{
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer"
}
],
"priority": false,
"prioritySetAt": "2025-01-15T10:30:00.000Z",
"state": "open",
"tags": [
{
"id": "67ec1234abcd5678ef901234",
"name": "Churn",
"type": "tag"
}
],
"teamAssigneeId": "507f1f77bcf86cd799439012",
"updatedAt": "2025-01-15T12:30:00.000Z",
"awaitingCustomerReply": true,
"conversationUser": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer",
"companies": [
{
"id": "id",
"name": "name"
}
],
"email": "jane@example.com",
"name": "Jane Doe",
"profilePicture": "https://cdn.example.com/avatar.png"
},
"matchingBodyPreviewMarkdown": "I would like to request a refund for my last invoice...",
"matchingPart": {
"id": "676f0f6765bdaa7d7d760f88",
"createdAt": "2025-01-15T10:30:00.000Z",
"partType": "user_msg"
},
"matchingPartAuthor": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer",
"companies": [
{
"id": "id",
"name": "name"
}
],
"email": "jane@example.com",
"name": "Jane Doe",
"profilePicture": "https://cdn.example.com/avatar.png"
},
"relevanceScore": 0.873,
"source": {
"bodyHtml": "<p>Hi, I have a question about your enterprise plan...</p>",
"bodyMarkdown": "Hi, I have a question about your enterprise plan...",
"channel": "desktop",
"author": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer"
},
"deliveredAs": "customer_initiated",
"subject": "Question about pricing",
"url": "https://example.com/pricing"
},
"surroundingConversationParts": [
{
"id": "676f0f6765bdaa7d7d760f88",
"previewMarkdown": "Just issued a refund for your other purchase.",
"previewMarkdownTruncated": false,
"author": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer",
"companies": [
{
"id": "id",
"name": "name"
}
],
"email": "jane@example.com",
"name": "Jane Doe",
"profilePicture": "https://cdn.example.com/avatar.png"
},
"createdAt": "2025-01-15T10:30:00.000Z",
"partType": "user_msg"
}
],
"title": "Question about pricing"
}
],
"nextCursor": "eyJpZCI6IjEyMzQ1In0=",
"object": "list",
"totalCount": 42,
"totalCountCapped": false
}Returns Examples
{
"data": [
{
"id": "12345",
"adminAssigneeId": "507f1f77bcf86cd799439011",
"brandId": "507f1f77bcf86cd799439011",
"createdAt": "2025-01-15T10:30:00.000Z",
"lastActivityAt": "2025-01-15T12:30:00.000Z",
"object": "conversation",
"participants": [
{
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer"
}
],
"priority": false,
"prioritySetAt": "2025-01-15T10:30:00.000Z",
"state": "open",
"tags": [
{
"id": "67ec1234abcd5678ef901234",
"name": "Churn",
"type": "tag"
}
],
"teamAssigneeId": "507f1f77bcf86cd799439012",
"updatedAt": "2025-01-15T12:30:00.000Z",
"awaitingCustomerReply": true,
"conversationUser": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer",
"companies": [
{
"id": "id",
"name": "name"
}
],
"email": "jane@example.com",
"name": "Jane Doe",
"profilePicture": "https://cdn.example.com/avatar.png"
},
"matchingBodyPreviewMarkdown": "I would like to request a refund for my last invoice...",
"matchingPart": {
"id": "676f0f6765bdaa7d7d760f88",
"createdAt": "2025-01-15T10:30:00.000Z",
"partType": "user_msg"
},
"matchingPartAuthor": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer",
"companies": [
{
"id": "id",
"name": "name"
}
],
"email": "jane@example.com",
"name": "Jane Doe",
"profilePicture": "https://cdn.example.com/avatar.png"
},
"relevanceScore": 0.873,
"source": {
"bodyHtml": "<p>Hi, I have a question about your enterprise plan...</p>",
"bodyMarkdown": "Hi, I have a question about your enterprise plan...",
"channel": "desktop",
"author": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer"
},
"deliveredAs": "customer_initiated",
"subject": "Question about pricing",
"url": "https://example.com/pricing"
},
"surroundingConversationParts": [
{
"id": "676f0f6765bdaa7d7d760f88",
"previewMarkdown": "Just issued a refund for your other purchase.",
"previewMarkdownTruncated": false,
"author": {
"id": "676f0f6765bdaa7d7d760f88",
"type": "customer",
"companies": [
{
"id": "id",
"name": "name"
}
],
"email": "jane@example.com",
"name": "Jane Doe",
"profilePicture": "https://cdn.example.com/avatar.png"
},
"createdAt": "2025-01-15T10:30:00.000Z",
"partType": "user_msg"
}
],
"title": "Question about pricing"
}
],
"nextCursor": "eyJpZCI6IjEyMzQ1In0=",
"object": "list",
"totalCount": 42,
"totalCountCapped": false
}