Search Conversations (v3)

Search conversations with a query that combines several conditions. Use the threads resource link inside the Conversation entity to load conversation threads.

Use this endpoint only for filters that List Conversations (v3) cannot express; see Choosing between List and Search.

Request

GET /v3/conversations/search?query=status%3Aactive%20AND%20tag%3A%22VIP%22 HTTP/1.1
Authorization: Bearer oauth_token

The whole search is expressed in the query parameter. It searches specific fields of the conversation (field:value) and combines those conditions with the boolean operators AND, OR and NOT, grouped with parentheses; see Query syntax.


curl "https://api.helpscout.net/v3/conversations/search?query=subject:refund%20AND%20NOT%20tag:processed"

Only conversations in the inboxes you have access to are returned, sorted by conversation id (id) from newest to oldest, which is equivalent to sorting by creation date (createdAt). There is no default status: a query without a status clause matches conversations in every status, including closed and spam. A request with invalid parameters, or a query that can’t be parsed or uses a field or construct not described on this page, is rejected with a 400 Bad Request whose message explains the problem.

Note: If using the embed=threads parameter with this call, you will see truncated chat threads. This is by design. To view Beacon chat threads in full, call the List Threads (v3) endpoint for that conversation instead.

URL Parameters

Parameter Type Examples Description
query string query=status:active AND tag:"VIP"
query=subject:refund AND NOT inboxId:123
Search query: field clauses (field:value) combined with AND, OR, NOT and parentheses. Quote values containing spaces. At most 500 characters. The only filter on this endpoint - the typed list filters are not accepted alongside it.

See Query syntax and Supported fields
embed enumeration embed=threads

Allows embedding/loading of sub-entities, allowed values are:
threads
cursor string cursor=eyJpZCI6MTIzfQ==
Cursor for pagination. See Pagination

Start with List Conversations (v3). It covers most filters with typed parameters, and a parameter that takes a comma-separated list matches any of its values, so ?tag=vip,urgent&status=active,pending doesn’t need search. Custom-field filters are only available on List.

Use this endpoint when List can’t express the filter: OR across different fields, NOT, full-text matching on subject or body, or a field List has no parameter for (see Supported fields).

Query syntax

A query is one or more clauses of the form field:value, with no space after the colon. Operators (AND, OR, NOT) are written in uppercase and surrounded by spaces, and parentheses group clauses. Field names and the values of status, type and attachments are case-insensitive.

  • Operators have no precedence, so use parentheses whenever a query mixes them: (NOT status:closed) AND tag:vip, tag:vip AND (status:pending OR status:active).
  • Always write AND explicitly. Two clauses with no operator between them are joined with an AND that does not bind the way you might expect.
  • To match one of several values of the same field, group the values: status:(active OR pending) is the same as (status:active OR status:pending).
  • Single-word values do not need quotes: id:123456, status:closed, tag:vip. Quote a value that is more than one word: tag:"dogs love carrots", subject:"have a question".
  • To search for a value that contains a character the syntax reserves (+ - ! ( ) { } [ ] ^ " ~ * ? : \ / & |), quote the value or escape the character with a backslash: tag:\+1.
  • Every value belongs to a field; a bare value such as refund on its own is not supported. Wildcards (subject:refund*) and the + / - clause modifiers are not supported either.
  • A query can nest at most 20 levels deep; each NOT, and each group whose operator differs from the one around it, adds a level.
  • URL-encode the query; an HTTP client’s query-parameter encoding does this for you. When building the URL by hand, note that + must be encoded as %2B, or it is decoded as a space. For more info, see this article.

Supported fields

Only the following fields can be used in a query:

Field Value What it matches
subject word or phrase Conversation subject contains the word or phrase (full text; a phrase matches its words in that order, next to each other)
body word or phrase Text of the conversation’s threads, notes included, contains the word or phrase (full text, matched like subject). Only about the first 1 MiB of a conversation’s thread text is searchable
email email address One of the customer’s email addresses (exact)
contact name Customer’s full name (exact)
contactId number Customer id
companyId number Id of the customer’s organization (company)
tag name Tag name
tagId number Tag id
inbox name Inbox name
inboxId number Inbox id
assigned name Assignee’s name, or Unassigned for unassigned conversations
assignedId number Assignee user id
replyFrom name Name of a user who replied in the conversation
replyFromId number Id of a user who replied in the conversation
noteFrom name Name of a user who added a note to the conversation
noteFromId number Id of a user who added a note to the conversation
status enumeration Status: active, pending, closed or spam. open is a synonym of active
type enumeration Type: email, chat, phone, sms, messenger, instagram, whatsapp, slack or ai-answers
number number Conversation number
id number Conversation id
attachments true or false Whether the conversation has attachments
createdAt date range Creation date within the range
modifiedAt date range Last-modified date within the range

Notes:

  • Name fields are matched case-insensitively. inbox matches the full inbox name, tag the tag name with case, spacing and punctuation normalised; assigned, replyFrom and noteFrom match a user’s full name, first name, last name or @mention name, and a name shared by several users matches all of them. A name that matches nothing simply matches no conversations, so NOT tag:ghost matches everything. The fields ending in Id take the id instead and are the more reliable choice for integrations, since a name can change or be shared.
  • Date fields (createdAt, modifiedAt) take a range: [lower TO upper] is inclusive and {lower TO upper} exclusive, and the brackets can be mixed. Each bound is a full UTC timestamp such as 2024-02-27T00:00:00Z, or * for an open bound: [2024-02-27T00:00:00Z TO *] is everything from that timestamp onwards.

Examples

The queries are shown unencoded for readability; URL-encode them when sending the request.

Example What it does
query=(tag:"vip" AND status:"pending") Pending conversations tagged “vip”
query=(subject:"refund" OR subject:"return") Subject contains “refund” or “return”
query=(assigned:"spock" AND (tag:"vip" OR tag:"urgent")) Assigned to Spock and tagged either “vip” or “urgent”
query=((tag:"vip" OR tag:"urgent") AND NOT status:closed) Tagged either “vip” or “urgent”, excluding closed conversations
query=(inbox:"Orders" AND createdAt:[2024-02-27T00:00:00Z TO *]) In the “Orders” inbox and created since 2024-02-27
query=(type:chat AND attachments:true AND modifiedAt:[2024-02-01T00:00:00Z TO 2024-03-01T00:00:00Z}) Chats with attachments, last modified in February 2024

Migrating from v2

If you are migrating a query from the v2 List Conversations endpoint, the clauses and operators look the same, but:

  • The field names follow the v3 vocabulary: mailbox is now inbox, mailboxid is inboxId, customerIds is contactId, and replyUserIds is replyFromId. No space is allowed after the colon.
  • email matches the customer’s email addresses only, not the to, cc and bcc fields of the messages.
  • Date bounds are full UTC timestamps; date math such as NOW-1HOUR and partial dates are not supported.
  • v2 limited a query to active conversations unless you sent status. This endpoint has no default status, so add AND status:active to get the same results.

Pagination

This endpoint uses cursor-based pagination. Follow the next link in the response to load the next page of results.

  • Each page holds up to 25 conversations; the page size can’t be changed.
  • The response _links contains self, first, and, unless this is the last page, next.
  • Every link carries the query, so to page forward, request the URL in _links.next.href unchanged. Alternatively, repeat the same query and embed and pass the link’s cursor value in the cursor parameter.
  • When _links.next is absent, there are no more results.
  • If a cursor is rejected, restart from the first page without one.
  • There are no last or prev links, and no page object.

Response

HTTP/1.1 200 OK
Content-Type: application/hal+json

{
  "_embedded" : {
    "conversations" : [ {
      "_links" : {
        "self" : {
          "href" : "..."
        },
        "mailbox" : {
          "href" : "..."
        },
        "primaryCustomer" : {
          "href" : "..."
        },
        "createdByUser" : {
          "href" : "..."
        },
        "closedBy" : {
          "href" : "..."
        },
        "threads" : {
          "href" : "..."
        },
        "assignee" : {
          "href" : "..."
        },
        "web" : {
          "href" : "..."
        }
      },
      "id" : 10,
      "number" : 12,
      "threads" : 2,
      "type" : "email",
      "folderId" : 11,
      "status" : "closed",
      "state" : "published",
      "subject" : "Help",
      "preview" : "Preview",
      "mailboxId" : 13,
      "assignee" : {
        "id" : 99,
        "type" : "user",
        "first" : "Mr",
        "last" : "Robot",
        "email" : "none@nowhere.com"
      },
      "createdBy" : {
        "id" : 12,
        "type" : "system_user",
        "email" : "bear@acme.com"
      },
      "createdAt" : "2012-03-15T22:46:22Z",
      "closedBy" : 14,
      "closedByUser" : {
        "id" : 14,
        "type" : "user",
        "first" : "Clo",
        "last" : "Ser",
        "photoUrl" : "pic.jpg",
        "email" : "closer@closers.com"
      },
      "closedAt" : "2012-03-16T14:07:23Z",
      "userUpdatedAt" : "2012-03-16T14:07:23Z",
      "customerWaitingSince" : {
        "time" : "2012-07-24T20:18:33Z",
        "friendly" : "20 hours ago"
      },
      "source" : {
        "type" : "email",
        "via" : "customer"
      },
      "tags" : [ {
        "id" : 9150,
        "color" : "#929499",
        "tag" : "vip",
        "styles" : {
          "dark" : {
            "background" : {
              "fill" : "#37A4FF"
            },
            "text" : {
              "fill" : "#161616"
            }
          },
          "default" : {
            "background" : {
              "fill" : "#ACE3FF"
            },
            "text" : {
              "fill" : "#1D2B36"
            }
          }
        }
      } ],
      "cc" : [ "bear@normal.com" ],
      "bcc" : [ "bear@secret.com" ],
      "primaryCustomer" : {
        "id" : 238604,
        "type" : "customer",
        "first" : "Rob",
        "last" : "Robertovic",
        "email" : "rob@acme.com"
      },
      "snooze" : {
        "snoozedBy" : 4,
        "snoozedUntil" : "2024-06-03T12:00:00Z",
        "unsnoozeOnCustomerReply" : true
      },
      "nextEvent" : {
        "time" : "2024-06-03T12:00:00Z",
        "eventType" : "snooze",
        "userId" : 4,
        "cancelOnCustomerReply" : true
      },
      "customFields" : [ {
        "id" : 8,
        "name" : "Account Type",
        "value" : "8518",
        "text" : "Free"
      }, {
        "id" : 6688,
        "name" : "Account Status",
        "value" : "33077",
        "text" : "Trial"
      }, {
        "id" : 9105,
        "name" : "ai-topic",
        "value" : "41194",
        "text" : "Billing",
        "systemType" : "topic"
      } ],
      "_embedded" : {
        "threads" : [ ]
      }
    } ]
  },
  "_links" : {
    "next" : {
      "href" : "..."
    },
    "self" : {
      "href" : "..."
    },
    "first" : {
      "href" : "..."
    }
  }
}

Response fields

Path Type Description
_embedded.conversations Array Array of Conversation (v3) objects
_links.self Object Link to the current page
_links.first Object Link to the first page
_links.next Object Link to the next page (absent on the last page)