Search and filters
Learn how to use search endpoints and construct filters to query data.
Overview
Many Endear resources can be queried through search* endpoints that support full-text search, sorting, pagination, and filtering. All search endpoints return cursor-based paginated connections (see GraphQL pagination).
Basic search query
Each search endpoint accepts a search argument for full-text search, along with sortBy, sortDir, and pagination arguments. The sortBy value is a GraphQL enum specific to each endpoint (e.g. SortAppointmentEventsBy, SortMessagesBy):
query {
searchAppointmentEvents(
first: 10
sortBy: CREATED_AT
sortDir: DESC
search: "john"
) {
edges {
node {
id
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}Discovering available filters
Use the filterDefinitions query to discover which filter keys are available for a given search endpoint and what type each filter expects:
query {
filterDefinitions(context: APPOINTMENT_EVENTS) {
key
type
}
}This returns a list of filter definitions:
{
"data": {
"filterDefinitions": [
{ "key": "eventStatus", "type": "STRING" },
{ "key": "bookerEmail", "type": "STRING" },
{ "key": "eventStartsAt", "type": "ABSOLUTE_DATE" },
{ "key": "isDeleted", "type": "BOOLEAN" },
{ "key": "rescheduledCount", "type": "NUMBER" }
]
}
}The context argument accepts the following values:
| Context | Corresponding search endpoint |
|---|---|
APPOINTMENT_EVENTS | searchAppointmentEvents |
CONVERSATIONS | searchConversations |
DRAFTS | searchDrafts |
MARKETING_EVENTS | searchMarketingEvents |
MARKETING_SUBSCRIPTIONS | searchMarketingSubscriptions |
MESSAGES | searchMessages |
NOTES | searchNotes |
ORDER_ATTRIBUTIONS | searchOrderAttributions |
STORIES | searchStories |
TEMPLATES | searchTemplates |
Constructing filters
Filters are passed through the filters argument as a list of SearchFilterInput objects. Each object in the list is ANDed together, allowing you to combine multiple filter groups.
Each individual SearchFilterInput contains either allOf (AND logic) or anyOf (OR logic), but not both. To combine AND and OR logic, use separate objects in the filters list.
Each SearchFilterEntryInput has a key (the filter key from filterDefinitions) and exactly one filter field matching the filter type. Providing zero or more than one filter field in a single entry will result in an INVALID_FILTERS error.
Filter type mapping
The type returned by filterDefinitions tells you which field to use on SearchFilterEntryInput:
| FilterDefinitionType | SearchFilterEntryInput field |
|---|---|
STRING | stringFilter |
BOOLEAN | booleanFilter |
ABSOLUTE_DATE | absoluteDateFilter |
NUMBER | numberFilter |
MONEY | moneyFilter |
RECURRING_DATE | recurringDateFilter |
LOCATION | locationFilter |
String filters
Use stringFilter for STRING type filters. Provide exactly one of the following operators:
| Operator | Description |
|---|---|
isAnyOf | Matches any of the provided values |
isAllOf | Matches all of the provided values |
isNoneOf | Matches none of the provided values |
containsAnyOf | Contains any of the provided substrings |
containsAllOf | Contains all of the provided substrings |
containsNoneOf | Contains none of the provided substrings |
startsWithAnyOf | Starts with any of the provided prefixes |
startsWithAllOf | Starts with all of the provided prefixes |
startsWithNoneOf | Starts with none of the provided prefixes |
endsWithAnyOf | Ends with any of the provided suffixes |
endsWithAllOf | Ends with all of the provided suffixes |
endsWithNoneOf | Ends with none of the provided suffixes |
isKnown | true if the field has a value, false if it does not |
Boolean filters
Use booleanFilter for BOOLEAN type filters. Provide exactly one operator:
| Operator | Description |
|---|---|
isAnyOf | Matches any of the provided boolean values |
isKnown | true if the field has a value, false if it does not |
Absolute date filters
Use absoluteDateFilter for ABSOLUTE_DATE type filters. The outer input accepts isAnyOf, isAllOf, or isKnown. Each date term accepts exactly one of:
| Operator | Description |
|---|---|
between | A list of two DateTime values defining a range |
moreThanOrEqualTo | A DateTime lower bound |
lessThanOrEqualTo | A DateTime upper bound |
isKnown | true if the field has a value, false if it does not |
Number filters
Use numberFilter for NUMBER type filters. The outer input accepts isAnyOf, isAllOf, or isKnown. Each number term accepts exactly one of:
| Operator | Description |
|---|---|
equals | Matches the exact value |
between | A list of two numbers defining a range |
moreThanOrEqualTo | A numeric lower bound |
lessThanOrEqualTo | A numeric upper bound |
isKnown | true if the field has a value, false if it does not |
Examples
Single filter (AND)
Find appointment events with a specific status:
query {
searchAppointmentEvents(
first: 10
filters: [
{
allOf: [
{ key: "eventStatus", stringFilter: { isAnyOf: ["confirmed", "pending"] } }
]
}
]
) {
edges {
node {
id
}
}
}
}Multiple filters (AND)
Find confirmed appointment events starting after a specific date:
query {
searchAppointmentEvents(
first: 10
filters: [
{
allOf: [
{ key: "eventStatus", stringFilter: { isAnyOf: ["confirmed"] } }
{ key: "eventStartsAt", absoluteDateFilter: { isAnyOf: [{ moreThanOrEqualTo: "2025-01-01T00:00:00Z" }] } }
]
}
]
) {
edges {
node {
id
}
}
}
}OR filters
Find appointment events that are either confirmed or rescheduled:
query {
searchAppointmentEvents(
first: 10
filters: [
{
anyOf: [
{ key: "eventStatus", stringFilter: { isAnyOf: ["confirmed"] } }
{ key: "isRescheduled", booleanFilter: { isAnyOf: [true] } }
]
}
]
) {
edges {
node {
id
}
}
}
}Combining AND and OR groups
You can pass multiple SearchFilterInput objects in the filters list. Each object is ANDed together, while entries within anyOf are ORed.
This example finds non-deleted appointment events where either the status is "confirmed" or the event has been rescheduled:
query {
searchAppointmentEvents(
first: 10
filters: [
{
allOf: [
{ key: "isDeleted", booleanFilter: { isAnyOf: [false] } }
]
}
{
anyOf: [
{ key: "eventStatus", stringFilter: { isAnyOf: ["confirmed"] } }
{ key: "isRescheduled", booleanFilter: { isAnyOf: [true] } }
]
}
]
) {
edges {
node {
id
}
}
}
}Checking if a field exists
Use isKnown to filter by whether a field has a value:
query {
searchMessages(
first: 10
filters: [
{
allOf: [
{ key: "sentAt", absoluteDateFilter: { isKnown: true } }
]
}
]
) {
edges {
node {
id
}
}
}
}Updated about 3 hours ago
