---
title: "Confirm Bank Transaction"
method: POST
path: "/api/autopilot/bank/transactions/{transaction_id}/confirm"
tags: ["autopilot"]
---

# Confirm Bank Transaction

`POST /api/autopilot/bank/transactions/{transaction_id}/confirm`

## Request

- Base URL: `https://api.taxcompass.it` (the document declares no server; this is the origin it was published from)
- URL: `https://api.taxcompass.it/api/autopilot/bank/transactions/{transaction_id}/confirm`
- Auth: HTTP bearer

Record one movement as an incasso, optionally against an invoice (AP-112).

**The user's way to write a movement to the ledger.** The import records
only the *certain* matches by itself (``bank_matching.certain_match``);
everything else lands here. The matcher ranks and explains; two invoices of
the same amount both come back suggested precisely so the question reaches
the person who knows the answer. An empty body means standalone income.

The incasso takes the movement's own amount and booked date rather than
anything the client sends: the bank is the record of what was received and
when, and a body that could restate them would let a typo move the cash-basis
estimate away from the statement it is supposed to reconcile with.

409 rather than a second payment when the movement is already confirmed —
a double-tap on a slow connection must not double the year's revenue. 422
carries the ledger's own refusal (a debit, an amount outside what an incasso
may hold, a booked date in the future).

## Path parameters

- `transaction_id` string, uuid, required

## Request body

- BankTransactionConfirm: ``POST /api/autopilot/bank/transactions/{id}/confirm`` body. ``invoice_id`` is the user's answer to "which invoice is this?": set, the payment is linked to that document; omitted, the credit is standalone income — a transfer with no e-invoice behind it, which a forfettario still declares. An empty body is therefore a valid, meaningful request, which is why every field has a default.
  - `invoice_id` string, uuid, nullable

## Response `200`

Successful Response

- BankTransactionRead: One imported bank movement, as the reconciliation inbox sees it. ``amount`` keeps the statement's own sign — a credit is positive, a debit negative — because only a credit can be an incasso, and flipping debits to positives here would let the matcher read a refund as income. ``counterparty`` and ``causale`` come back decrypted (they are a Fernet envelope at rest); ``incasso_id`` is set once a movement has been tied to a payment in the ledger, which one confirmation writes. ``suggestions`` is recomputed on every read of the inbox rather than stored (AP-112), so it is empty on a row nobody is matching any more — a confirmed or ignored movement, or a debit.
  - `id` string, uuid, required
  - `booked_date` string, date, required
  - `amount` string, required
  - `side` 'credit' | 'debit', required
  - `match_status` 'unmatched' | 'suggested' | 'confirmed' | 'ignored', required
  - `import_batch` string, uuid, required
  - `incasso_id` string, uuid, nullable
  - `counterparty` string, nullable
  - `causale` string, nullable
  - `suggestions` MatchSuggestionRead[]
    - `invoice_id` string, uuid, required
    - `score` integer, required
    - `reasons` string[]
    - `signals` string[]
    - `certain` boolean
  - `invoice_id` string, uuid, nullable
  - `auto_recorded_at` string, date-time, nullable

## Other responses

- `404`: No such movement, or no such invoice, for this user
- `409`: The movement is already tied to a payment, or the payment would overpay the invoice
- `422`: The movement cannot be recorded as a payment

---

[API](https://skmtc.dev/taxcompass/apis/taxcompass-api.md) · [All operations](https://skmtc.dev/taxcompass/apis/taxcompass-api/llms.txt) · [OpenAPI document](https://skmtc.dev/taxcompass/apis/taxcompass-api/revisions/fef5fc6a9b91?raw)
