Banking
Bank accounts, imported statements and bank transactions (read-only; statement import via CAMT.053/CODA).
List the organization's bank accounts
GET /api/v1/bank-accounts
Requires authentication (Bearer token) and the X-Organization-Id header.
Banking module (#678). Requires the bank_accounts.view scope. Accounts are created/managed in the UI; the API surface is read-only plus statement import.
Parameters
| Name |
In |
Type |
Required |
Description |
status |
query |
string |
no |
|
per_page |
query |
string |
no |
|
Responses
| Status |
Description |
| 200 |
Paginated bank accounts |
Example request
curl -X GET "https://kworia.com/api/v1/bank-accounts" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Show one bank account
GET /api/v1/bank-accounts/{id}
Requires authentication (Bearer token) and the X-Organization-Id header.
Returns 404 for accounts outside the requesting organization (IDOR guard).
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 200 |
Bank account |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X GET "https://kworia.com/api/v1/bank-accounts/{id}" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Upload and import a bank statement file (CSV, CAMT.053, CODA)
POST /api/v1/bank-accounts/{id}/import-statement
Requires authentication (Bearer token) and the X-Organization-Id header.
Requires the bank_transactions.import scope. Parses the file and ingests transactions with idempotent dedupe (re-importing the same file is a no-op). Returns the import session summary. Archived accounts reject new statements (422).
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 201 |
Import session summary |
| 422 |
|
| 403 |
Token lacks the bank_transactions.import scope |
| 404 |
Account not found in this organization |
Example request
curl -X POST "https://kworia.com/api/v1/bank-accounts/{id}/import-statement" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Reconciliation report for an account and period
GET /api/v1/bank-accounts/{id}/reconciliation-report
Requires authentication (Bearer token) and the X-Organization-Id header.
Matched vs unmatched (count + sum) per status for the period, reconciliation rate, and a closing-balance check: the sum of ALL imported lines up to the period end against the provider-reported balance snapshot (#680). Requires the bank_accounts.view scope.
Parameters
| Name |
In |
Type |
Required |
Description |
from |
query |
string |
yes |
|
to |
query |
string |
yes |
|
Responses
| Status |
Description |
| 200 |
Report |
| 422 |
|
| 404 |
Account not found in this organization |
Example request
curl -X GET "https://kworia.com/api/v1/bank-accounts/{id}/reconciliation-report" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
List bank rules
GET /api/v1/bank-rules
Requires authentication (Bearer token) and the X-Organization-Id header.
All rules of the organization, priority order (#680). Requires the bank_rules.view scope.
Responses
| Status |
Description |
| 200 |
Rules |
Example request
curl -X GET "https://kworia.com/api/v1/bank-rules" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Create a bank rule
POST /api/v1/bank-rules
Requires authentication (Bearer token) and the X-Organization-Id header.
Conditions ALL must match; the action describes the money record to create. Rules suggest by default — auto_add (default false) books matching lines automatically at import, and only when no invoice or expense matches (a rule never shadows a document match). Referenced bank_account_id / actions.counterparty_id must belong to the organization (404 otherwise). Requires the bank_rules.create scope.
Request body
| Field |
Type |
Required |
Description |
name |
string |
yes |
|
is_active |
boolean |
no |
|
bank_account_id |
integer |
no |
|
direction |
string |
no |
|
conditions |
array |
yes |
|
actions |
object |
yes |
|
auto_add |
boolean |
no |
|
priority |
integer |
no |
|
Responses
| Status |
Description |
| 201 |
Created rule |
| 422 |
Validation error (e.g. unknown condition type) |
Example request
curl -X POST "https://kworia.com/api/v1/bank-rules" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"is_active": true,
"bank_account_id": 1,
"direction": "string",
"conditions": [],
"actions": {},
"auto_add": true,
"priority": 1
}'
Show one bank rule
GET /api/v1/bank-rules/{id}
Requires authentication (Bearer token) and the X-Organization-Id header.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 200 |
Rule |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X GET "https://kworia.com/api/v1/bank-rules/{id}" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Update a bank rule
PUT /api/v1/bank-rules/{id}
Requires authentication (Bearer token) and the X-Organization-Id header.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Request body
| Field |
Type |
Required |
Description |
name |
string |
yes |
|
is_active |
boolean |
no |
|
bank_account_id |
integer |
no |
|
direction |
string |
no |
|
conditions |
array |
yes |
|
actions |
object |
yes |
|
auto_add |
boolean |
no |
|
priority |
integer |
no |
|
Responses
| Status |
Description |
| 200 |
Updated rule |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X PUT "https://kworia.com/api/v1/bank-rules/{id}" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"is_active": true,
"bank_account_id": 1,
"direction": "string",
"conditions": [],
"actions": {},
"auto_add": true,
"priority": 1
}'
Delete a bank rule
DELETE /api/v1/bank-rules/{id}
Requires authentication (Bearer token) and the X-Organization-Id header.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 200 |
Deleted |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X DELETE "https://kworia.com/api/v1/bank-rules/{id}" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
List imported bank transactions
GET /api/v1/bank-transactions
Requires authentication (Bearer token) and the X-Organization-Id header.
Requires the bank_transactions.view scope. A bank_account_id filter pointing at another organization's account returns 404 (IDOR guard).
Parameters
| Name |
In |
Type |
Required |
Description |
bank_account_id |
query |
integer |
no |
|
status |
query |
string |
no |
|
date_from |
query |
string |
no |
|
date_to |
query |
string |
no |
|
per_page |
query |
string |
no |
|
Responses
| Status |
Description |
| 200 |
Paginated bank transactions |
Example request
curl -X GET "https://kworia.com/api/v1/bank-transactions" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Bulk confirm suggested bank transactions
POST /api/v1/bank-transactions/bulk-confirm
Requires authentication (Bearer token) and the X-Organization-Id header.
Per line the TOP document suggestion is confirmed; a matching bank rule is the fallback when the matcher is empty; anything ambiguous or already claimed is skipped — bulk never guesses (#680). Concurrency-safe: the reconciliation transition is compare-and-swap, so a concurrent confirm makes the loser a skip, never a double payment. Requires the bank_transactions.update scope.
Request body
| Field |
Type |
Required |
Description |
transaction_ids |
array |
yes |
|
Responses
| Status |
Description |
| 200 |
Counts |
| 422 |
|
Example request
curl -X POST "https://kworia.com/api/v1/bank-transactions/bulk-confirm" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"transaction_ids": []
}'
Show one bank transaction
GET /api/v1/bank-transactions/{id}
Requires authentication (Bearer token) and the X-Organization-Id header.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 200 |
Bank transaction |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X GET "https://kworia.com/api/v1/bank-transactions/{id}" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Create an expense or invoice from a bank transaction
POST /api/v1/bank-transactions/{id}/create-document
Requires authentication (Bearer token) and the X-Organization-Id header.
The one sanctioned reconciliation-drives-documents flow (#693): creates an expense (debit) or a single-line invoice (credit) through the regular Invoicing services and confirms the match in the same database transaction. The document is born reconciled with created_money_record=true, so a later unmatch reverses the payment but keeps the document. A new counterparty is created when counterparty_name is given without counterparty_id (the bank IBAN is stored on it for future matching). Requires the bank_transactions.update scope.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Request body
| Field |
Type |
Required |
Description |
counterparty_id |
integer |
no |
Existing customer/vendor of the requesting organization |
counterparty_name |
string |
no |
Name for a NEW counterparty; defaults to the bank counterparty name |
description |
string |
no |
Defaults to the bank remittance text |
date |
string |
no |
Defaults to the booking date |
category |
string |
no |
Expense category (debits only) |
Responses
| Status |
Description |
| 201 |
Created match (document ids in invoice_id/expense_id, created_money_record=true) |
| 422 |
Already matched, foreign counterparty, or missing customer for an invoice |
| 404 |
Transaction not found in this organization |
Example request
curl -X POST "https://kworia.com/api/v1/bank-transactions/{id}/create-document" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"counterparty_id": 1,
"counterparty_name": "string",
"description": "string",
"date": "string",
"category": "string"
}'
Prefill for creating a document from a bank transaction
GET /api/v1/bank-transactions/{id}/document-prefill
Requires authentication (Bearer token) and the X-Organization-Id header.
Everything a create-expense/create-invoice form can be prefilled with (#693): document type by amount sign (debit=expense, credit=invoice), absolute amount, currency, booking date, remittance text, and a resolved counterparty (exact IBAN match first, then fuzzy name against the org's customers/vendors). Requires the bank_transactions.view scope.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 200 |
Prefill payload |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X GET "https://kworia.com/api/v1/bank-transactions/{id}/document-prefill" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Confirm a reconciliation match
POST /api/v1/bank-transactions/{id}/match
Requires authentication (Bearer token) and the X-Organization-Id header.
Records a Payment (invoice) or marks the expense paid; fee/writeoff deltas post to the ledger. Requires the bank_transactions.update scope. Concurrent confirms on one transaction serialize - losers receive 422. Cross-currency matches are rejected (422).
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Request body
| Field |
Type |
Required |
Description |
invoice_id |
integer |
no |
Exactly one of invoice_id / expense_id / credit_note_id |
expense_id |
integer |
no |
|
credit_note_id |
integer |
no |
Sent credit note settled by an outgoing refund (debit lines) — applied on confirm, reverted on unmatch (#697) |
allocated_amount |
number |
no |
Defaults to the transaction amount (+ fee_adjustment) |
fee_adjustment |
number |
no |
|
type |
string |
no |
|
link_payment_id |
integer |
no |
|
allow_overpayment |
boolean |
no |
Explicitly allow paying beyond the invoice balance — a duplicate customer payment (#699). Default false; without it a payment beyond the balance is rejected (422). Recorded as signals.overpayment=true on the match. |
Responses
| Status |
Description |
| 201 |
Created match |
| 422 |
Already matched, cross-currency, or allocation exceeds the open balance |
| 404 |
Transaction or document not found in this organization |
Example request
curl -X POST "https://kworia.com/api/v1/bank-transactions/{id}/match" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"invoice_id": 1,
"expense_id": 1,
"credit_note_id": 1,
"allocated_amount": 1,
"fee_adjustment": 1,
"type": "string",
"link_payment_id": 1,
"allow_overpayment": true
}'
Ranked reconciliation match suggestions
GET /api/v1/bank-transactions/{id}/suggestions
Requires authentication (Bearer token) and the X-Organization-Id header.
Matching engine output (#679): candidates with confidence bands (suggest-only) and a per-suggestion signal breakdown (structured_reference, amount_exact, counterparty_name, fee_delta, reference_in_description). Requires the bank_transactions.view scope.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Responses
| Status |
Description |
| 200 |
Transaction + ranked suggestions |
| 404 |
Not found (or belongs to another organization) |
Example request
curl -X GET "https://kworia.com/api/v1/bank-transactions/{id}/suggestions" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json"
Undo the active match on a transaction
POST /api/v1/bank-transactions/{id}/unmatch
Requires authentication (Bearer token) and the X-Organization-Id header.
Reverses the payment record and returns the line to the queue; the match row is kept as an audit record. Requires the bank_transactions.update scope.
Parameters
| Name |
In |
Type |
Required |
Description |
id |
path |
integer |
yes |
|
Request body
| Field |
Type |
Required |
Description |
reason |
string |
no |
|
Responses
| Status |
Description |
| 200 |
The match, now status=unmatched |
| 422 |
No active match on this transaction |
Example request
curl -X POST "https://kworia.com/api/v1/bank-transactions/{id}/unmatch" \
-H "Authorization: Bearer {{access_token}}" \
-H "X-Organization-Id: {{organization_id}}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"reason": "string"
}'