Skip to content

List & filtering

Every list endpoint in Invora follows one shape, so once you learn it you can list documents, parties, subscriptions, connected businesses, or anything else the same way.

  • Path: POST /api/{area}/{version}/list (e.g. /api/v2/documents/list, /api/v1/simple/invoices/list)
  • Request: filter, sort, pagination, and mask
  • Response: items, totalCount, and an optional nextPageCursor
curl -X POST https://gateway.invora.app/api/v2/documents/list \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": { "textSearch": "Acme", "part": { "editState": { "inValues": ["EDIT_STATE_FROZEN"] } } },
    "sort": { "rules": [ { "createdAt": "SORT_DIRECTION_DESCENDING" } ] },
    "pagination": { "limit": "20" },
    "mask": "key,editState,documentType"
  }'
Response
{
  "items": [
    { "key": "doc_01J7...", "editState": "EDIT_STATE_FROZEN", "documentType": "DOCUMENT_TYPE_INVOICE" }
  ],
  "totalCount": "1",
  "nextPageCursor": null
}

Filtering

filter has three parts, all optional:

Field Purpose
part A structured, typed condition tree (see below).
textSearch Fuzzy search across key, party names, notes, and other text fields.
timeReference Query historical state as of a point in time (see Time reference).

Composable parts

part is a single condition. Each service defines its own leaf conditions (typed filters on specific fields) plus three combinators that nest other parts: and, or, and not.

Get all frozen invoices created after 2026-01-01 and in SAR:

{
  "filter": {
    "part": {
      "and": {
        "operands": [
          { "createdAt": { "fromInclusive": "2026-01-01T00:00:00Z" } },
          { "editState": { "inValues": ["EDIT_STATE_FROZEN"] } },
          { "field": { "fieldPath": "documentCurrencyCode", "op": "FIELD_FILTER_OPERATOR_EQUAL", "value": { "stringValue": "SAR" } } }
        ]
      }
    }
  }
}

Leaf conditions are typed per service — e.g. the Simple invoice surface offers key, issueAt, dueDate, branchId, buyerPartyKey, currencyCode, onlyFrozen, onlyDrafts; the Documents API offers key, createdAt, updatedAt, frozenAt, documentType, editState, branchId, and arbitrary UBL field filters via field / unaryField.

Arbitrary field filters (Documents API)

For services that expose them, field and unaryField filter on any field path:

{ "field": { "fieldPath": "accountingCustomerParty.party.partyTaxScheme.companyId", "op": "FIELD_FILTER_OPERATOR_EQUAL", "value": { "stringValue": "123456879" } } }

op is a FieldFilterOperatorEQUAL, LESS_THAN(_OR_EQUAL), GREATER_THAN(_OR_EQUAL), IN, CONTAINS, STARTS_WITH, ENDS_WITH, REGEX, ARRAY_CONTAINS, ARRAY_CONTAINS_ALL (each prefixed FIELD_FILTER_OPERATOR_). unaryField takes IS_NULL, IS_SET, or IS_EMPTY.

Sorting

sort.rules is an ordered list — earlier rules take priority. Each rule is one field set to SORT_DIRECTION_ASCENDING or SORT_DIRECTION_DESCENDING:

{ "sort": { "rules": [ { "frozenAt": "SORT_DIRECTION_DESCENDING" }, { "createdAt": "SORT_DIRECTION_ASCENDING" } ] } }

The sortable fields are defined per service (e.g. createdAt, updatedAt, issueDate, dueDate, payableAmount, frozenAt, taxAmount).

Pagination

One pagination object supports both offset and cursor modes; limit, skip, and cursor are strings:

Field Mode Notes
limit both Max items per page.
skip offset Skip N items from the start. Page n of size x: skip = (n - 1) * x.
cursor cursor Pass the previous response's nextPageCursor.

skip and cursor are mutually exclusive — use limit + skip for page-by-page UIs, limit + cursor for forward iteration over large or live data. When nextPageCursor is absent or empty, there are no more pages.

{ "pagination": { "limit": "50", "skip": "100" } }

Field selection (mask)

mask is a single comma-separated string of camelCase field paths — restrict a response to just the fields you need:

{ "mask": "key,editState,calculations.payableAmount" }

See Field masks for the full rules (read and write masks).

Time reference

timeReference reads entities as they were at a point in time rather than their current state — useful for audit, reconciliation, and reproducing a past report. Its two modes are alternatives — set one or the other, not both:

State as of a moment
{ "filter": { "timeReference": { "referenceDate": "2026-01-01T00:00:00Z" } } }
Full version history
{ "filter": { "timeReference": { "historicalData": true } } }
  • referenceDate — returns each matching entity as it existed at that instant.
  • historicalData — when true, returns all versions over time; group by id.key in your application to reconstruct each entity's timeline.

This pairs with per-entity versioning — see Entity versioning for reading a specific historical version of a single entity.

Response

Response
{
  "items": [ /* entities, projected by `mask` */ ],
  "totalCount": "123",
  "nextPageCursor": "eyJ..."
}

totalCount is the full match count (ignoring pagination). Real-time variants (ListStream) push change events for a filtered list instead of a single page.