Create match
Create a manual match between a transaction and an invoice.
This is the call that records a settlement. It allocates matchedAmount from a bank transaction onto an invoice. invoiceId and matchedAmount are required, and either transactionId or pspEventId. Identify the invoice and transaction with their codes. Invoice and transaction amounts are updated by database triggers. Incompatible direction, currency, or capacity returns 400.
To deny a suggestion, POST the same codes with status rejected. matchedAmount is then omitted. To point an existing payment at another invoice, unlink first with Reconcile transaction unlink_all, then create the new allocation here. Do not use Update match to change the invoice.
See also
- Matches . Which call to use for each change.
- Reconcile transaction (POST
/api/v1/transactions/:code/reconcile). Ignore a bank line, bring it back into matching, or unlink its matches. - List matches (GET
/api/v1/matches). Find an existing allocation by invoice number and transaction code.
Body parameters
| Name | Type | Required |
|---|---|---|
invoiceIdInvoice number. Example: INV-2026-0042 | string No maximum | Required |
transactionIdBank transaction code (`transaction_code`). Required unless pspEventId is sent. Example: TX-8891 | string No maximum | Required |
pspEventIdPSP event UUID, used instead of a bank transaction. Example: 3fa85f64-5717-4562-b3fc-2c963f66afa6 | string No maximum | Optional |
matchedAmountPositive amount to allocate. Omit when status is rejected. Example: 1000 | number | Required |
confidenceScore0-100. Default 100 for a manual match. Example: 100 | number | Optional |
notesFree-text reason, stored as ai_reasoning. Example: Manual match INV-2026-0042 | string Max 500 | Optional |
statusSet to rejected to deny this invoice and transaction pair without allocating. invoiceId and transactionId are required; matchedAmount is not. Allowed values rejected | string No maximum | Optional |
Errors
This operation can return the following errors in addition to generic HTTP failures.
| Error | HTTP code | Description |
|---|---|---|
Missing Bearer token | 401 | The Authorization header is missing or is not a Bearer token. |
Invalid or revoked API key | 401 | The API key is unknown, malformed, expired, or has been revoked. |
API key is missing scope matches:write | 403 | The key does not include matches:write. A write scope does not imply the matching read scope. |
Too many requests, please try again later | 429 | Wait and retry. The Retry-After header is the number of seconds to wait. |
Validation failed | 400 | The body failed schema validation. details lists field and message. |
matchedAmount is required | 400 | matchedAmount was omitted on a live create. |
Either transactionId or pspEventId must be provided | 400 | Neither source was sent. |
Transaction not found | 400 | transactionId does not exist in this tenant. |
An ignored transaction cannot be matched | 400 | The transaction was ignored. Include it before matching. |
Invoice not found | 400 | invoiceId does not exist in this tenant. |
Invoice cannot be matched while its status is paid | 400 | The invoice is already paid. cancelled invoices have a similar message. |
Transaction and invoice currencies must match | 400 | Currencies differ and no conversion applies. |
Invoice is already fully paid | 400 | open_amount is already zero. |
A match already exists between this source and invoice | 400 | A live match already links this pair. |
Internal server error | 500 | Unexpected server error. The JSON body includes correlationId. Retry with backoff. |