Skip to main content

Filter Documents

Search and filter documents by extracted field values via API. Compose equality, comparison, range, containment, and emptiness conditions in one call.

The POST /v1/documents/filter endpoint lets you search documents by their extracted field values: compose conditions on any field Talonic has extracted (supplier names, invoice dates, contract amounts) and get back the matching documents with the tested values inline. Each condition targets a field by its registry UUID and applies an operator to test its value; multiple conditions are AND-combined. The endpoint also supports free-text search over materialized field values and sorting by any field.

Conditions execute against the materialized value store, typed per field: numeric fields compare as numbers, date fields as dates, so gt/lt/between behave correctly without client-side casting. between is inclusive on both bounds (value and valueTo); contains is a case-insensitive substring match on text values with %/_ escaped, so user input is safe to pass through verbatim. Results are filtered by Sources-IAM visibility, evaluated as the API key's minting user.

Matched documents come back as full document rows — id, filename, display_name, status, source_connection_id, tags, inferred type, counts — plus a field_values map carrying the values of exactly the fields your conditions tested, keyed by field registry UUID. Fields not part of any condition are not inlined; read them via the documents or extraction endpoints when you need the full record.

Each condition's fieldId must be a field registry UUID — resolve names with the field autocomplete endpoint. A fieldId that matches no registry field simply matches no documents, and a condition with an unknown operator is skipped rather than erroring, so validate both client-side.
POST/v1/documents/filter

Body parameters

source_idstringScope to a specific source connection (UUID).
conditionsarrayArray of filter conditions, AND-combined. Each has fieldId, operator, and optional value / valueTo.
searchstringFree-text search, matched case-insensitively against materialized field values.
sortobjectSort by a field: { fieldId, direction: "asc" | "desc" }.
pageinteger1-based page number. Default: 1
limitintegerResults per page (max 500). Default: 50

Operators: eq, neq, gt, gte, lt, lte, between, contains, is_empty, is_not_empty

Request body

{
  "conditions": [
    { "fieldId": "6f1e9a2b-4c3d-4e5f-8a7b-9c0d1e2f3a4b", "operator": "eq", "value": "Acme Corp" },
    { "fieldId": "8a2b3c4d-5e6f-4a1b-9c8d-7e6f5a4b3c2d", "operator": "between", "value": "2026-01-01", "valueTo": "2026-12-31" }
  ],
  "sort": { "fieldId": "8a2b3c4d-5e6f-4a1b-9c8d-7e6f5a4b3c2d", "direction": "desc" },
  "page": 1,
  "limit": 25
}

Response

Response fields

dataarrayArray of matching document rows.
data[].idstringDocument UUID.
data[].filenamestringStored filename of the document.
data[].display_namestring | nullDerived readable label, when the workspace produced one.
data[].statusstringProcessing status (completed, extracting, error, …).
data[].source_connection_idstring | nullSource the document was ingested through.
data[].created_atstringISO 8601 ingestion timestamp.
data[].tags / system_tagsstring[]User tags and system (email/zip grouping) tags.
data[].document_type_inferredstring | nullInferred document type classification.
data[].field_count / resolved_field_countintegerExtracted and materialized field counts for the document.
data[].field_valuesobjectValues of the fields your conditions tested, keyed by field registry UUID. Each entry carries value_text, value_number, value_date, value_boolean (the typed slot for the field's data type is set).
totalintegerTotal number of visible documents matching all conditions, independent of page/limit.
links.selfstringLink to this endpoint.

Response

{
  "data": [
    {
      "id": "b8b00d51-eecc-49b3-affc-89fee95b9518",
      "filename": "Invoice-2026-001.pdf",
      "display_name": "Acme Corp — June invoice",
      "status": "completed",
      "source_connection_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "created_at": "2026-06-15T10:32:00.000Z",
      "tags": [],
      "system_tags": [],
      "document_type_inferred": "Invoice",
      "field_count": 14,
      "resolved_field_count": 12,
      "field_values": {
        "6f1e9a2b-4c3d-4e5f-8a7b-9c0d1e2f3a4b": {
          "value_text": "Acme Corp",
          "value_number": null,
          "value_date": null,
          "value_boolean": null
        }
      }
    }
  ],
  "total": 47,
  "links": { "self": "/v1/documents/filter" }
}

Errors

Error responses

400validation_errorMalformed body: source_id not a UUID, a condition missing fieldId/operator, sort direction not asc/desc, or page/limit below 1.
401unauthorizedMissing or invalid API key.
429rate_limitedToo many requests. Retry after the period indicated in the Retry-After header.

Build conditions programmatically by first calling GET /v1/search/autocomplete to resolve field IDs, then GET /v1/search/field-values to populate value pickers. An identical route exists at POST /v1/search/filter for integrations that prefer everything under the search namespace — same body, same response, only links.self differs.

Frequently asked questions

How do I search documents by extracted field values?+
POST conditions to `/v1/documents/filter`. Each condition names a field by registry UUID (resolve IDs via the autocomplete endpoint), an operator such as `eq` or `between`, and a value. The response returns matching document rows with the tested values inline under `field_values`.
How do I use the between operator?+
Provide both `value` (lower bound, inclusive) and `valueTo` (upper bound, inclusive) in the condition. Works with dates and numbers, compared in the field's own type. Example: `{ "fieldId": "...", "operator": "between", "value": "2026-01-01", "valueTo": "2026-12-31" }`.
What happens if a document does not have a value for a filtered field?+
Documents missing the filtered field are excluded from results unless you use the `is_empty` operator, which explicitly matches documents where the field has no materialized value.
Can I combine free-text search with field conditions?+
Yes. Set the `search` parameter alongside `conditions`. Both are AND-combined, so documents must carry a field value matching the search text and satisfy all conditions.
Why does field_values only contain some fields?+
The map inlines exactly the fields your conditions referenced — enough to render why each document matched. For the full extracted record, follow up via the documents or extractions endpoints, or use a pipeline's results endpoint for schema-shaped rows.