# Reconcile transaction

Ignore, include, or unlink matches on a bank transaction. To allocate a payment to an invoice, use Create match.

This is inbox control for one transaction, identified by `transaction_code`. It does not create a settlement.

`ignore` cancels live matches and hides the line from invoice matching. `include` brings an ignored line back and re-runs matching. `unlink_all` cancels every live match on the line; set `ignore` to true to exclude it afterwards. `unlink_match` cancels one match. `save` only updates an existing live match (invoice, customer, payment method, and amount together) for the Invunion workbench. `matchId` is the match UUID, not the transaction code.

## Use this instead

To allocate a bank transaction to an invoice, use [Create match](https://www.invunion.com/knowledge-base/api/create-match/) (`POST /api/v1/matches`).

## See also

- [Update match](https://www.invunion.com/knowledge-base/api/update-match/) (`PUT /api/v1/matches/:id`). Change the amount or cancel one match without deleting the row.
- [Cancel match](https://www.invunion.com/knowledge-base/api/cancel-match/) (`DELETE /api/v1/matches/:id`). Permanently delete one match.
- [Matches](https://www.invunion.com/knowledge-base/api/matches/). Which call to use for each change.

- HTTP method: `POST`
- Path: `/api/v1/transactions/:code/reconcile`
- URL: `https://api.invunion.com/api/v1/transactions/:code/reconcile`
- Required scope: `transactions:write`
- HTML docs: https://www.invunion.com/knowledge-base/api/reconcile-transaction/
- Markdown docs: https://www.invunion.com/knowledge-base/api/reconcile-transaction.md

## Path parameters

| Name | Type | Required | Description | Allowed values | Example |
| --- | --- | --- | --- | --- | --- |
| `code` | string, max 50 | required | Transaction code (`transaction_code`). |  | `TX-8891` |


## Body parameters

| Name | Type | Required | Description | Allowed values | Example |
| --- | --- | --- | --- | --- | --- |
| `action` | string, no maximum | required | ignore, include, and unlink_all are the usual public actions. save updates an existing live match and cannot create one; use Create match. | `ignore`, `include`, `unlink_all`, `unlink_match`, `save` |  |
| `matchId` | string, no maximum | optional | Required for save and unlink_match. UUID of a live match on this transaction. Not used for ignore, include, or unlink_all. |  | `1c9e6679-7425-40de-944b-e07fc1f90ae7` |
| `counterpartyId` | string, no maximum | optional | Required for save. Counterparty code (`account_code`). |  | `CPT-042` |
| `paymentMethodId` | string, no maximum | optional | Required for save. Catalog payment-method code that belongs to the counterparty. |  | `PM-001` |
| `invoiceId` | string, no maximum | optional | Required for save. Invoice number billed to the counterparty. |  | `INV-2026-0042` |
| `matchedAmount` | number | optional | Required for save. Positive amount allocated on this match. |  | `1000` |
| `ignore` | boolean | optional | For unlink_all only. If true, also ignore the transaction after unlinking. | `true`, `false` |  |

## Errors

| 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 transactions:write` | `403` | The key does not include transactions: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. |
| `Tenant required` | `400` | The authenticated credential is not bound to a tenant. |
| `An ignored transaction cannot be reconciled` | `400` | save and unlink_match are refused on an ignored transaction. Use include first. |
| `Invoice does not belong to the selected counterparty` | `400` | invoiceId is not billed to counterpartyId. |
| `Payment method does not belong to the selected counterparty` | `400` | paymentMethodId is not linked to counterpartyId. |
| `Transaction and invoice currencies must match` | `400` | Cross-currency matching needs a conversion rate, or the currencies differ when no rate applies. |
| `No applicable currency conversion rate is available` | `400` | The match needs FX and no rate is stored for that date. |
| `A live match already exists for this transaction and invoice` | `400` | Another active match already links this pair. |
| `Transaction not found` | `404` | No transaction with this code exists in the authenticated tenant. |
| `Match not found` | `404` | matchId is missing, not on this transaction, or not live. |
| `Invoice not found` | `404` | invoiceId does not exist in this tenant. |
| `Internal server error` | `500` | Unexpected server error. The JSON body includes correlationId. Retry with backoff. |

## Request (curl)

```bash
curl --request POST \
  --url https://api.invunion.com/api/v1/transactions/TX-8891/reconcile \
  --header 'accept: application/json' \
  --header 'authorization: Bearer uk_live_YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '{
  "action": "ignore"
}'
```

## Request (Python)

```python
import requests

url = "https://api.invunion.com/api/v1/transactions/TX-8891/reconcile"
headers = {
    "Accept": "application/json",
    "Authorization": "Bearer uk_live_YOUR_API_KEY",
    "Content-Type": "application/json",
}
payload = {
    "action": "ignore"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

## Request (Ruby)

```ruby
require 'net/http'
require 'json'
require 'uri'

uri = URI("https://api.invunion.com/api/v1/transactions/TX-8891/reconcile")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Accept'] = 'application/json'
request['Authorization'] = 'Bearer uk_live_YOUR_API_KEY'
request['Content-Type'] = 'application/json'
request.body = "{\n  \"action\": \"ignore\"\n}"
response = http.request(request)
puts response.body
```

## Request (JavaScript)

```javascript
const response = await fetch("https://api.invunion.com/api/v1/transactions/TX-8891/reconcile", {
  method: "POST",
  headers: {
    "Accept": "application/json",
    "Authorization": "Bearer uk_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "action": "ignore"
}),
});
const data = await response.json();
```

## Request (Go)

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload := []byte(`{
  "action": "ignore"
}`)
	req, err := http.NewRequest("POST", "https://api.invunion.com/api/v1/transactions/TX-8891/reconcile", bytes.NewBuffer(payload))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", "Bearer uk_live_YOUR_API_KEY")
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
```

## Request (Node)

```javascript
const response = await fetch("https://api.invunion.com/api/v1/transactions/TX-8891/reconcile", {
  method: "POST",
  headers: {
    "Accept": "application/json",
    "Authorization": "Bearer uk_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "action": "ignore"
}),
});
console.log(await response.json());
```

## Success (200)

```json
{
  "success": true,
  "data": {
    "transaction": {
      "id": "2d0e6679-7425-40de-944b-e07fc1f90ae7",
      "transaction_code": "TX-8891",
      "counterparty_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "payment_method_id": "8a1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
      "amount": 1000,
      "currency": "EUR",
      "direction": "in",
      "status": "ignored",
      "remaining_amount": 1000
    },
    "match": null,
    "cancelledMatchIds": [
      "1c9e6679-7425-40de-944b-e07fc1f90ae7"
    ]
  }
}
```
