1. API
  2. Records

Records: Search

Description

List and filter records of a single record type. Search supports filtering, sorting, sparse fieldsets, relationship includes, time-range narrowing, and keyset pagination.

This endpoint supersedes the v1 List record endpoint: use Search to retrieve and filter individual records, and the Aggregate records API to compute counts, sums, and grouped breakdowns.

Despite the POST verb, Search is a read — no records are created. The search criteria travel in the request body because they are richer than a query string comfortably carries.

To discover the record_type_id and field IDs to reference, use the List record types API.

Request

HTTP Method: POST

URL: https://<tenant-domain>/api/v2/records/search

The request body is a JSON object with the following fields:

Body parameter Required Description
record_type_id Yes* Numeric record type ID. Provide this or record_type_name.
record_type_name Yes* Record type name or slug. An alternative to record_type_id; if the name matches multiple types, use record_type_id.
filter No A filter tree that narrows which records are returned (see Filters).
sort No An array of sort keys applied in priority order (see Sort).
fields No Sparse fieldset: { "record": "status,severity" } (or { "record": ["status", "severity"] }). Omit for all fields.
include No An array of accepted to-one relationships: record_type and/or parent_record (see Includes).
ids No Hydrate specific record IDs (max 500). Cannot be combined with after. When limit is omitted, all unique IDs are returned in one page.
time_range No An array of up to two timestamp windows, ANDed together, narrowing which records match (see Time range).
timezone No IANA timezone name (for example America/New_York) applied to time-range presets and timestamp output. Defaults to UTC.
limit No Page size, between 1 and 500. Defaults to 20, or the number of unique ids when ids is supplied.
after No Opaque forward pagination cursor from a previous response's page.next_page_token.
include_total No When true, include page.total (the count of all matching records). Defaults to false.

*Provide at least one of record_type_id or record_type_name. If you send both, record_type_id takes precedence and record_type_name is ignored. If record_type_name matches more than one record type, the request fails with a 400; supply record_type_id to disambiguate.

An unknown body parameter returns a 400 — a misspelled key such as filters (v1's name) is rejected rather than silently ignored.

Field identification

Every field reference in filter and sort accepts one of:

Key Type Description
field_id Integer The numeric ID of the record field. Stable across renames — recommended for automation.
field_name String The exact, case-sensitive name or canonical snake-case slug of the field within the record type. More readable, but breaks if the field is renamed.

Tip: Use field_id for stable automation that won't break if fields are renamed. Use field_name for ad-hoc queries where readability matters.

Filters

The filter field is a filter tree. It is either a single condition:

{ "field_name": "Status", "operator": "EQUAL", "value": "open" }

or an AND group of conditions:

{
  "op": "AND",
  "nodes": [
    { "field_name": "Status", "operator": "EQUAL", "value": "open" },
    { "field_id": 102, "operator": "IS_ANY_OF", "value": ["high", "critical"] }
  ]
}

Only AND is supported. Groups may nest, up to a maximum depth of 4 and 50 total nodes.

Each condition has:

Key Required Description
field_id or field_name Yes The field to filter on.
operator Yes See the operator matrix below.
value Depends The value to compare against. For IS_ANY_OF/IS_NONE_OF, provide an array. For IS_EMPTY, IS_NOT_EMPTY, IS_TRUE, and IS_FALSE, omit value.

Operators are gated by the field's result_type:

Operator Applicable field types
EQUAL, NOT_EQUAL TEXT, TEXT_ENUM, NUMBER, TIMESTAMP
GREATER_THAN, GREATER_THAN_OR_EQUAL_TO, LESS_THAN, LESS_THAN_OR_EQUAL_TO NUMBER, TIMESTAMP
IS_ANY_OF, IS_NONE_OF TEXT, TEXT_ENUM, NUMBER
CONTAINS, STARTS_WITH, ENDS_WITH TEXT, TEXT_ENUM
IS_EMPTY, IS_NOT_EMPTY TEXT, TEXT_ENUM, NUMBER, TIMESTAMP, BOOLEAN
IS_TRUE, IS_FALSE BOOLEAN

ARTIFACT and JSON fields cannot be used in filters.

Sort

The optional sort orders the returned records. It is an array of sort keys applied in priority order: records are ordered by the first key, ties broken by the next, and so on. When omitted, records are ordered newest first. Record id is always appended as a final tiebreaker, so paging stays stable even when your keys do not fully disambiguate.

Each key has:

Key Required Description
field_id or field_name Yes The field to sort by. created_at is accepted as a field_name alias for the creation timestamp.
direction No ASC or DESC (case-insensitive). Defaults to ASC.

Sorting is supported on TEXT, TEXT_ENUM, NUMBER, BOOLEAN, and TIMESTAMP fields; sorting on an ARTIFACT or JSON field returns a 400. A field may appear only once — repeating it returns a 400 — and an unknown key member is rejected with a Did you mean? hint.

[
  { "field_name": "Severity", "direction": "DESC" },
  { "field_name": "created_at", "direction": "ASC" }
]

The pagination cursor pins the sort it was created under: replaying an after token with a different sort returns a 400. When you supply sort, the response's applied.sort echoes those resolved keys with each field represented by its field_id. When sort is omitted, applied.sort is an empty array even though the default newest-first order is used.

Time range

The optional time_range narrows records by timestamp. It is an array of up to two windows, ANDed together, with at most one window per field. Each window is an object:

Key Description
field Timestamp column to filter: created_at (default) or updated_at.
rolling_date_range A rolling preset: TODAY, YESTERDAY, LAST_7_DAYS, LAST_31_DAYS, LAST_365_DAYS, or ALL_TIME (ALL_TIME applies no bound).
range_start Inclusive ISO 8601 lower bound of a custom range.
range_end Inclusive ISO 8601 upper bound of a custom range.

Within a window, supply either a rolling_date_range preset or range_start/range_end bounds, not both, and range_start must not be after range_end — otherwise the request returns a 400. Equal bounds select that single instant. A second window on the same field also returns a 400. Use one window per column to combine conditions, for example "created before 2026 and updated in the last seven days":

[
  { "field": "created_at", "range_end": "2026-01-01T00:00:00Z" },
  { "field": "updated_at", "rolling_date_range": "LAST_7_DAYS" }
]

Includes

Search accepts these to-one include values:

Value Effect
record_type Embeds the record type once under the top-level included object (every record on a page shares one record type).
parent_record The bare parent reference is always returned in each record's relationships; supplying this value does not change the response.

The to-many relationships (child_records, cases) are not available on Search — use the List child records API and List linked cases API, or the include on the Get record API.

Sparse fieldsets

By default every field of the record type is returned. To return only some fields, set fields to { "record": "<comma-separated field keys>" }. The keys are the same slug keys that appear in each record's fields object. An unknown key returns a 400.

Example request

curl --proto '=https' --tlsv1.2 \
  -X POST \
  "https://<tenant-domain>/api/v2/records/search" \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "record_type_id": 42,
    "filter": {
      "field_name": "Status",
      "operator": "EQUAL",
      "value": "open"
    },
    "sort": [
      { "field_name": "Severity", "direction": "DESC" }
    ],
    "include": ["record_type"],
    "include_total": true,
    "limit": 20
  }'

Pagination

The Records v2 API uses keyset (cursor) pagination rather than the page/per_page numbering of v1. When more results exist, the response's page object contains a next_page_token; pass it back as the after body parameter to fetch the next page. On the last page, page has no next_page_token.

curl --proto '=https' --tlsv1.2 \
  -X POST \
  "https://<tenant-domain>/api/v2/records/search" \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{ "record_type_id": 42, "after": "<<next_page_token>>", "limit": 20 }'

Response

A successful request returns a JSON object with a data array of records, a field_ids map, a page pagination cursor, and (when requested) included relationships and an applied echo.

Field description

The response contains these top-level fields:

Field Description
data Array of record objects (see below).
field_ids Map of each returned field's slug key to its immutable field ID, so callers can pin to the ID across renames.
included Present only when include is used. record_type is a one-element array of { id, name, slug }.
page Pagination cursor and optional total. Empty on the last page (see Pagination).
applied Echoes the filter and sort actually applied, so you can diff them against what you sent.

Each object in data contains:

Field Description
id Integer ID of the record.
fields Field values keyed by field slug (field_<id> when two fields share a slug). Scalar fields hold their typed value; TIMESTAMP fields are ISO 8601 strings; artifact-backed fields return a handle (see Artifact-backed fields).
relationships record_type ({ "id" }) and parent_record ({ "id" }, or null when the record has no parent).
created_at ISO 8601 creation timestamp (UTC unless a timezone is supplied).
updated_at ISO 8601 last-updated timestamp (UTC unless a timezone is supplied).

The page object contains:

Field Description
next_page_token Opaque forward cursor; present only when another page exists. Pass it back as after.
total Total matching records; present only when include_total is true.

Artifact-backed fields

JSON and ARTIFACT field values are never inlined by Search, whatever their size. Instead, each is returned as a handle:

{ "expandable": true, "href": "/api/v2/records/<id>/fields/<field_id>" }

Follow the href with the Get record field API to retrieve the full value. (The single-record Get record API can also hydrate these fields inline via its expand parameter.)

Sample response

{
  "data": [
    {
      "id": 555,
      "fields": {
        "status": "open",
        "severity": "high",
        "payload": {
          "expandable": true,
          "href": "/api/v2/records/555/fields/103"
        }
      },
      "relationships": {
        "record_type": {
          "id": 42
        },
        "parent_record": null
      },
      "created_at": "2026-07-02T00:00:00Z",
      "updated_at": "2026-07-02T00:00:00Z"
    }
  ],
  "field_ids": {
    "status": 101,
    "severity": 102,
    "payload": 103
  },
  "included": {
    "record_type": [
      {
        "id": 42,
        "name": "Alerts",
        "slug": "alerts"
      }
    ]
  },
  "page": {
    "total": 1
  },
  "applied": {
    "filter": {
      "field_id": 101,
      "operator": "EQUAL",
      "value": "open"
    },
    "sort": [
      {
        "field_id": 102,
        "direction": "DESC"
      }
    ]
  }
}

Errors

Errors are returned as a JSON object with an error field, along with an appropriate HTTP status code. Every Records v2 endpoint uses this shared shape:

{
  "error": {
    "type": "bad_request",
    "message": "record_type_id or record_type_name is required",
    "field": "record_type_id"
  }
}
Status Meaning
400 Invalid request — malformed body, unknown parameter, invalid timezone, disallowed operator, or invalid cursor.
403 The tenant does not have access to records.
404 Record type not found, or the calling token does not have access to it.
422 A field value could not be read for the requested representation.
503 Search is temporarily unavailable. Retry the request or restart from the first page using the response's hint.

The error object always carries type and message; validation errors add a field pointer, and some add a hint suggesting a correction.

Was this helpful?
Records: Search | API | Tines