Complyance Logo

Invoice Query API

Search documents in the authenticated workspace. The same endpoint supports sales invoices, purchase invoices, and receipts in every supported country and in both sandbox and production.

Endpoint

POST /invoices/query

Authentication

Send a bearer token or API key issued for the workspace. Never place a live key in source control.

export COMPLYANCE_API_KEY='YOUR_API_KEY'

Every example below uses:

Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json

The workspace is derived from authentication. Documents from another workspace are never returned.

Request body

FieldTypeRequiredDescription
countrystringYesTwo-letter country code, such as MY, AE, or SA.
typestringYessales, purchases, or receipts.
environmentstringYesTarget environment, normally sandbox or production.
pagenumberNoOne-based page number. Defaults to 1.
limitnumberNoNumber of records per page. Defaults to 20.
filtersarrayNoExact-match, multi-value, range, or existence filters.
searchesarrayNoCase-insensitive text searches. Every supplied search must match.
columnsarrayNoFields to return in addition to document identifiers and type.
sortobjectNoA field and asc or desc order.

Basic query: sales invoices

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "sales",
    "environment": "sandbox",
    "page": 1,
    "limit": 20
  }'

Query a date range

Use getsDocument.header.issueDate for sales and purchases. The gte and lte bounds are inclusive and use YYYY-MM-DD values.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "sales",
    "environment": "sandbox",
    "page": 1,
    "limit": 20,
    "filters": [
      {
        "id": "getsDocument.header.issueDate",
        "range": {
          "gte": "2026-08-01",
          "lte": "2026-08-31"
        }
      }
    ],
    "sort": {
      "field": "header.issueDate",
      "order": "desc"
    }
  }'

Filter by document type and currency

This returns Malaysian tax invoices in MYR. Omit documentType.modifiers when no modifier is required; an empty array adds no filter.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "sales",
    "environment": "sandbox",
    "filters": [
      {
        "id": "documentType.base",
        "value": ["tax_invoice"]
      },
      {
        "id": "getsDocument.header.currency",
        "value": ["MYR"]
      }
    ],
    "sort": {
      "field": "header.issueDate",
      "order": "desc"
    }
  }'

Filter by status

Use NEEDS_CORRECTION to retrieve invoices that failed validation and appear as INVALID in their status history. Include NON_COMPLIANT to also return invoices that reached a final rejected or non-compliant state.

This example demonstrates only the status filter. Add country-specific fields, date ranges, or document-type filters when your use case requires them.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "sales",
    "environment": "sandbox",
    "filters": [
      {
        "id": "meta.statuses.document.current",
        "value": ["NEEDS_CORRECTION", "NON_COMPLIANT"]
      }
    ]
  }'

Search by invoice number

Search is case-insensitive. Each object in searches is an additional condition, so use one object to search one field. Adding a seller-name search means the seller name must also contain the same term.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "sales",
    "environment": "sandbox",
    "searches": [
      {
        "field": "getsDocument.header.documentNumber",
        "term": "INV"
      }
    ],
    "sort": {
      "field": "header.issueDate",
      "order": "desc"
    }
  }'

Filter by total amount

Use a numeric range on getsDocument.totals.totalAmountIncludingTax.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "AE",
    "type": "sales",
    "environment": "production",
    "filters": [
      {
        "id": "getsDocument.totals.totalAmountIncludingTax",
        "range": {
          "gte": 100,
          "lte": 500
        }
      }
    ]
  }'

Query purchase invoices

Use purchases to query purchase invoices. Sales and purchase documents use the same nested getsDocument.* filter and search fields.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "purchases",
    "environment": "sandbox",
    "filters": [
      {
        "id": "getsDocument.header.issueDate",
        "range": {
          "gte": "2026-08-01",
          "lte": "2026-08-31"
        }
      }
    ]
  }'

Query receipts

Receipts use flat paths without the getsDocument. prefix.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "SA",
    "type": "receipts",
    "environment": "sandbox",
    "filters": [
      {
        "id": "header.issueDate",
        "range": {
          "gte": "2026-08-01",
          "lte": "2026-08-31"
        }
      },
      {
        "id": "countryDocumentStatus",
        "value": ["ISSUED"]
      }
    ]
  }'

Select columns and paginate

When columns is supplied, the API returns those fields plus documentId and documentType. Sort fields use accessor keys such as header.issueDate, without the getsDocument. prefix.

curl --location 'http://localhost:4000/invoices/query' \
  --header "Authorization: Bearer ${COMPLYANCE_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "country": "MY",
    "type": "sales",
    "environment": "sandbox",
    "page": 2,
    "limit": 50,
    "columns": [
      "header.documentNumber",
      "header.issueDate",
      "header.currency"
    ],
    "sort": {
      "field": "header.issueDate",
      "order": "desc"
    }
  }'

Supported filter and search fields

Document typeDate, amount, and currency filter fieldsText-search fields
sales, purchasesgetsDocument.header.issueDate, getsDocument.header.currency, getsDocument.totals.totalAmountIncludingTaxgetsDocument.header.documentNumber, getsDocument.parties.seller.name, getsDocument.parties.buyer.name
receiptsheader.issueDate, header.currency, totals.totalAmountIncludingTaxheader.documentNumber, parties.seller.name, parties.buyer.name

All document types also support documentId, documentType.base, documentType.modifiers, documentType.variants, meta.statuses.document.current, meta.statuses.delivery.current, meta.statuses.business.current, and meta.statuses.userfacing filters. The route rejects unsupported field names with 400 Bad Request.

Sample successful response

The API returns up to the requested limit of matching documents. This example shows three documents with different current lifecycle states. The statusHistory array can contain additional events in an actual response.

{
  "status": "success",
  "data": {
    "documents": [
      {
        "documentId": "01JQEXAMPLE8YQVK1T8DD98VY6N",
        "documentType": {
          "base": "tax_invoice",
          "modifiers": []
        },
        "getsDocument": {
          "header": {
            "documentNumber": "MY-INV-1001"
          }
        },
        "statusHistory": [
          {
            "internalStatus": "INTERNAL_VALIDATION_FAILED",
            "userFacingStatus": "INVALID",
            "documentStatus": "ON_HOLD",
            "countryDocumentStatus": "VALIDATION_FAILED",
            "occurredAtUtc": "2026-08-19T09:01:25.563Z"
          }
        ],
        "meta": {
          "statuses": {
            "document": { "current": "NEEDS_CORRECTION" },
            "delivery": { "current": "NOT_STARTED" },
            "business": { "current": "NOT_STARTED" },
            "internal": "NEEDS_CORRECTION"
          }
        }
      },
      {
        "documentId": "01JQEXAMPLE8YQVK1T8DD98VY6P",
        "documentType": {
          "base": "tax_invoice",
          "modifiers": []
        },
        "getsDocument": {
          "header": {
            "documentNumber": "MY-INV-1002"
          }
        },
        "statusHistory": [
          {
            "internalStatus": "LHDN_VALIDATED",
            "userFacingStatus": "VALID",
            "documentStatus": "APPROVED",
            "countryDocumentStatus": "VALIDATED",
            "occurredAtUtc": "2026-08-18T08:36:44.285Z"
          }
        ],
        "meta": {
          "statuses": {
            "document": { "current": "COMPLIANT" },
            "delivery": { "current": "NOT_STARTED" },
            "business": { "current": "NOT_STARTED" },
            "internal": "COMPLIANT"
          }
        }
      },
      {
        "documentId": "01JQEXAMPLE8YQVK1T8DD98VY6Q",
        "documentType": {
          "base": "tax_invoice",
          "modifiers": []
        },
        "getsDocument": {
          "header": {
            "documentNumber": "MY-INV-1003"
          }
        },
        "statusHistory": [
          {
            "internalStatus": "FLOW_INITIATED",
            "userFacingStatus": "DRAFT",
            "documentStatus": "DRAFT",
            "countryDocumentStatus": "DRAFT",
            "occurredAtUtc": "2026-08-11T11:08:05.956Z"
          }
        ],
        "meta": {
          "statuses": {
            "document": { "current": "READY_TO_PROCESS" },
            "delivery": { "current": "NOT_STARTED" },
            "business": { "current": "NOT_STARTED" },
            "internal": "READY_TO_PROCESS"
          }
        }
      }
    ]
  },
  "pagination": {
    "page": 1,
    "perPage": 20,
    "total": 663,
    "totalPages": 34
  }
}

data.documents is the result array. An empty array with pagination.total: 0 means that no document in the authenticated workspace matches every supplied condition.

Errors

StatusMeaning
400Invalid JSON, unsupported filter or search field, invalid request shape, or an invalid sort order.
401Missing or invalid authentication.
403The request is not authorized for the workspace.
500Unexpected service or storage error.