Use the receipt list to locate financial documents by supported properties, then retrieve a specific receipt when you need its complete detail response. The list operation reads data; it does not create invoices or collect payments.
The main distinction is between a named view, free-text searchTerm, an explicit selection, and the structured userFilter. They are combined by the server, so a correct filter can still return no rows if another criterion excludes them.
What you’ll have at the end
A checked list of unpaid invoices, with a baseline request that isolates filter mistakes.
- Baseline list
- JSON-encoded filter
- Matching receipts
- Detail read
Before you start
- Mandates Read and Invoices and receipts Read
- One known matching invoice and one nonmatching receipt
On this page
Replace YOUR_API_KEY, YOUR_ORGANIZATION_ID, and YOUR_MANDATE_ID directly in your chosen example. Each tab is complete and uses only cURL or the runtime’s built-in functions. The key needs Mandates → Read and Invoices and receipts → Read.
POST /{organizationId}/{mandateId}/finance/receipts/list accepts a JSON body and returns receipts and count. Start with limit: 1, offset: 0, and returnCount: true, without optional filters. This establishes that your key and mandate can read receipts before you debug a more specific selection.
Example
curl --fail-with-body --silent --show-error --max-time 30 --globoff \
--request POST 'https://cloud.campai.com/api/YOUR_ORGANIZATION_ID/YOUR_MANDATE_ID/finance/receipts/list' \
--header 'X-API-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"limit": 1,
"offset": 0,
"returnCount": true
}'The financial receipt routes differ from accounting receipts. Choose the record family you actually need; do not switch paths merely because both contain the word “receipts.”
userFilter is a string containing JSON. Its outer object combines groups; each group combines fields. Use API property paths and enum values, not translated UI labels.
Choose the same key and IDs in this filtered request:
Example
curl --fail-with-body --silent --show-error --max-time 30 --globoff \
--request POST 'https://cloud.campai.com/api/YOUR_ORGANIZATION_ID/YOUR_MANDATE_ID/finance/receipts/list' \
--header 'X-API-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"userFilter": "{\"operator\":\"and\",\"groups\":[{\"operator\":\"and\",\"fields\":[{\"path\":\"type\",\"comparator\":\"equals\",\"value\":\"invoice\"},{\"path\":\"paymentStatus\",\"comparator\":\"equals\",\"value\":\"unpaid\"}]}]}",
"limit": 25,
"offset": 0,
"returnCount": true,
"sort": {
"receiptDate": "desc"
}
}'The filter is already a JSON string in these examples; the surrounding request body is encoded separately. Sending the filter object directly fails to meet the contract. The Node.js and PHP examples also verify the returned records. With cURL, check that every returned receipt has type: invoice and paymentStatus: unpaid.
This example means exactly “type invoice and payment status unpaid.” It is not a complete definition of outstanding debt: partially paid, canceled, draft, or overdue documents may need different treatment. Use a named view whose behavior matches your task, or add reviewed conditions.
The reference lists the supported view values, including draft, overdue, and paid. Views can include several conditions, such as document type, due date, cancellation state, and payment status. Do not assume a view name is equivalent to comparing one field with the same word.
Use selection.ids to select known receipt IDs, or selection.excludeIds to exclude a set. When supplied alongside userFilter or view, the selection further restricts the result. Omit the property when you do not need a selection; do not send an empty ID array where the schema requires at least one item.
For a complete filter reference with date, string, and nested-field examples, see Handling user filters. A GET response helps identify field values, but it does not establish that every computed field supports filtering.
Use a returned _id with GET /{organizationId}/{mandateId}/finance/receipts/{receiptId}. A missing record or a record outside your permitted scope can produce a not-found response; do not infer that the ID is globally invalid.
The sample reads the first 25 matches. For a complete export, repeat the same filter and sort with offsets 25, 50, and so on until a page has fewer than 25 rows. Upsert by _id and reconcile if documents change while you page through them. Never interpret a default count of zero as a reliable total when you did not request returnCount: true.
Signed receipt-file URLs can expire. Store the receipt ID and retrieve a fresh resource URL when needed instead of using the signed URL as a permanent file identifier.
Compare a known matching invoice and a known nonmatching receipt. Check the returned property values, not just the total. Then test an empty result and a later page.
If the baseline succeeds but the filter does not, remove extra views, selections, and search text. Add one filter field at a time. Check the exact spelling of userFilter, its string encoding, the comparator, and the API's enum value. If both requests return 403, use Resolve 403 errors and empty results before changing the filter.
Check your result
- Unpaid invoice
- Appears when all other criteria also match.
- Nonmatching receipt
- Does not appear.
- Empty result
- Is distinguished from an HTTP or encoding error.