Payables
15 operations under /v1/payables (GET POST PATCH): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/payables
https://api.codespar.dev/v1/payablesList payables
This project's payables, each in the same shape GET /v1/payables/{payableId} returns plus due_date_effective (the due date rolled forward to the next bank business day), ordered by PRINTED due date ascending, then by creation, then by id; payables with no due date come last. That order agrees with due_date_effective except between a boleto and a Pix charge around the last business day of the year: a boleto due that day is effectively due on the first business day of January, a Pix charge is not extended, so the boleto can be listed first although it is effectively due later. Order by due_date_effective where that matters. Filter by status (one or several, comma-separated); there is no date filter. Page with before: pass the previous page's next_before as it was returned, and stop when it is null. The cursor is opaque and carries no tenant, so a cursor from another project pages this project's payables and reveals nothing. Payables of other projects and organizations are never listed. Reading never re-runs a check or changes a status.
Scope: payables:read.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
before | string | no | The next_before of the previous page, as it was returned. Opaque. |
limit | integer | no | Page size, 1..200. Default 50. |
status | string | no | One status or several, comma-separated (NEEDS_REVIEW,READY). Absent: every status. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | A filter, the limit or the cursor is not valid. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
next_before | string,null | yes | Pass as before for the next page; null on the last page. |
payables | array of object | yes | — |
curl -X GET https://api.codespar.dev/v1/payables \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/payables HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/payables",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/payables");{
"payables": [
{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
},
"payable_id": "payable_0000000000000000",
"status": "RECEIVED",
"rail_quote": {
"beneficiary": {
"value": "string",
"from": "trilho"
},
"beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"final_beneficiary": {
"value": "string",
"from": "trilho"
},
"final_beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"amount_minor": {
"value": 1000,
"from": "trilho"
},
"discount_minor": {
"value": 1000,
"from": "trilho"
},
"interest_minor": {
"value": 1000,
"from": "trilho"
},
"fine_minor": {
"value": 1000,
"from": "trilho"
},
"due_date": {
"value": "2026-01-15",
"from": "trilho"
},
"quoted_at": "2026-01-15T12:00:00.000Z"
},
"validation": {
"carrier_check_digits": {
"outcome": "ok",
"detail": "string"
},
"document_check_digits": {
"outcome": "ok",
"detail": "string"
},
"nfe_key_consistency": {
"outcome": "ok",
"detail": "string"
},
"arithmetic": {
"outcome": "ok",
"detail": "string"
},
"duplicate_document": {
"outcome": "ok",
"detail": "string"
},
"amount_matches_rail": {
"outcome": "ok",
"detail": "string"
},
"beneficiary_document_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"due_date_state": {
"outcome": "ok",
"detail": "string"
},
"pix_key_owner_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"supplier_registry": {
"outcome": "ok",
"detail": "string"
},
"bank_details_changed": {
"outcome": "ok",
"detail": "string"
}
},
"review": {
"validation_status": "REJECTED",
"review_by": [
{
"code": "string",
"cause": "check_failed"
}
],
"rejected_by": [
"string"
],
"confirmed_by": [
"string"
],
"awaiting": [
"string"
],
"not_covered": [
"string"
],
"review_triggers": [
"string"
],
"resolutions": [
{
"code": "string",
"resolution": "human_correction",
"at": "string",
"by": "string",
"by_source": "verified_user_token",
"fields": [
"string"
],
"confirmed_beneficiary_document": "string"
}
]
},
"payment": {
"state": "awaiting_approval",
"approval_id": "approval_0000000000000000",
"settled_against": "document_line",
"document_amount_minor": 1000,
"original_document_settled": false,
"settlement_note": "string",
"mandate_id": "mandate_0000000000000000",
"attempt_id": "attempt_0000000000000000",
"tries": 0,
"amount_minor": 1000,
"quote_hash": "string",
"claimed_at": "string",
"key_id": "key_0000000000000000",
"via": "string",
"executed_at": "string",
"receipt_id": "receipt_0000000000000000",
"transaction_id": "transaction_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"money_moved": true,
"uncertain_code": "string",
"hold_entry_id": "holdentry_0000000000000000",
"last_refusal": {
"code": "string",
"attempt_id": "attempt_0000000000000000",
"at": "string"
}
},
"created_at": "string",
"updated_at": "string",
"due_date_effective": "2026-01-15"
}
],
"next_before": "string"
}POST /v1/payables
https://api.codespar.dev/v1/payablesCreate a payable from a partner's record or a person's form
A payable is an obligation to pay somebody. This endpoint takes one as structured data: attribution.channel is structured for a partner system's record and manual for a form a person filled in. Both go through the same schema and the same deterministic checks, so the same payable sent through either channel ends in the same status with the same validation. A file (PDF or image) goes to POST /v1/payables/documents, which reads the payable out of it and enters the same pipeline; a multipart/form-data body sent HERE answers 415 payable_document_endpoint, and a JSON body claiming attribution.channel: "upload" is refused.
Every field carries its provenance in from. Amounts are integer centavos (*_minor). rail_quote is refused by name: the amount and the payee of a payment come from the rail, never from a request. attribution.credential_id is set to the API key the request authenticated with; a different value is refused.
The amount and the payee come from the rail, never from what was sent. amount_matches_rail, beneficiary_document_matches_supplier and due_date_state run against a consult of the boleto on the payable, and they block READY whether or not they answered. A consult that could not be made — no connected payment provider, a provider that did not answer — leaves them indisponivel and the payable NEEDS_REVIEW, named under review.review_by. pix_key_owner_matches_supplier is not consulted in this release and blocks a payable carrying a Pix BR Code with cause required_not_implemented. VALIDATING is never returned: nothing re-runs a validation yet, so a check with no answer goes to a person rather than waiting. due_date_state calls a payable overdue only after its EFFECTIVE due date: a due date on a weekend, a national bank holiday (Carnival Monday and Tuesday and Corpus Christi included) or, for a boleto, the last business day of the year is payable without charges on the next business day. State and municipal holidays are not considered.
Status. Two fields answer two questions. review.validation_status is what the checks alone say: REJECTED (a check that rejects failed), NEEDS_REVIEW (a check failed, or a required check has no answer it can have), VALIDATING (a required, implemented check did not answer and will be retried) or VALIDATED (every required check passed). status is what the payable may do: READY only from VALIDATED with no review trigger open, and NEEDS_REVIEW when a trigger is open. The trigger this API evaluates is new_supplier: this project has no executed payable for the supplier's document, or the payable names no supplier document. It is evaluated per project, never across projects.
Why things are in review. review.review_by lists each check that sends the payable to a person and its cause: check_failed, retries_exhausted or required_not_implemented. review.review_triggers lists the open triggers. review.not_covered lists checks that did not run and do not block READY in this release (supplier_registry, bank_details_changed).
What fails a check. A carrier that fails its own check digits (carrier_check_digits) or a duplicate (duplicate_document) is REJECTED and not stored. A document number with wrong check digits, an NF-e key that disagrees with the printed fields, or totals that do not add up (arithmetic, one centavo of tolerance per comparison) is check_failed. The total is composed the way an NF-e composes vNF: subtotal_minor minus discount_minor and tax_relief_deducted_minor, plus taxes_added_minor, freight_minor, insurance_minor and other_charges_minor. taxes_included_minor is informational and in no sum.
Duplicates. A payable is a duplicate when another payable of the SAME project that is open or already paid shares a duplicity axis with it: document_hash, the boleto (a 47- or 48-digit linha digitável and the 44-digit barcode it expands from are the same boleto), the Pix txid, the NF-e access key, or the supplier document together with invoice number and série. The answer is 409 payable_duplicate and nothing is stored; details.existing_payable_id names the payable that holds the axis. A payable in another project never collides and is never named.
Idempotency. Send an Idempotency-Key header to make a retry safe. The same key with the same body answers 200 with idempotent_replay: true and the payable as it is now, never a duplicate. The same key with a different body answers 409 idempotency_key_conflict. Without a key, a retry of a create that succeeded answers payable_duplicate naming the payable it created.
Scope: payables:write.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
attribution | object | yes | — |
carriers | object | no | — |
doc_type | "invoice" | "danfe_nfe" | "boleto" | "unknown" | yes | — |
document_hash | string | no | — |
due_date | object | no | — |
extraction | object | no | — |
invoice_number | object | no | — |
issue_date | object | no | — |
items | array of object | no | — |
rail_quote | — | no | — |
recipient | object | no | — |
schema_version | "payable.v1" | yes | — |
serie | object | no | — |
supplier | object | yes | — |
totals | object | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
201 | object | OK |
400 | object | The body did not match the schema, carried rail_quote, or the Idempotency-Key header is empty or longer than 255 characters. details.issues names paths and codes, never the values sent. |
409 | object | payable_duplicate: the payable is REJECTED as a duplicate and was not stored. details.axes names the axis, details.existing_payable_id the payable of this project that holds it, and details.validation the verdict. idempotency_key_conflict: the key was used with a different body. |
415 | object | A multipart/form-data body was sent to the JSON endpoint. Send the file to POST /v1/payables/documents (details.endpoint). |
422 | object | payable_carrier_malformed: a carrier failed its own check digits, so the payable is REJECTED and was not stored; details.carriers names the carrier and the failure. payable_no_duplicate_key: the payable carries no duplicity axis. payable_channel_unsupported: a JSON body claimed upload. payable_attribution_mismatch: attribution.credential_id names another API key. |
503 | object | The key that protects supplier documents could not be read. Nothing was stored; retry later with the same Idempotency-Key. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
attribution | object | yes | — |
carriers | object | no | — |
created_at | string | yes | ISO 8601 instant the payable was created. |
doc_type | "invoice" | "danfe_nfe" | "boleto" | "unknown" | yes | — |
document_hash | string | no | — |
due_date | object | no | — |
extraction | object | no | — |
idempotent_replay | boolean | yes | True when this answer replays an earlier call made with the same Idempotency-Key. |
invoice_number | object | no | — |
issue_date | object | no | — |
items | array of object | no | — |
payable_id | string | yes | — |
payment | object,null | yes | What paying it did (ent#1587); null until a payment was attempted. |
rail_quote | object | no | — |
recipient | object | no | — |
review | object | yes | — |
schema_version | "payable.v1" | yes | — |
serie | object | no | — |
status | "RECEIVED" | "EXTRACTING" | "EXTRACTED" | "VALIDATING" | "VALIDATED" | "NEEDS_REVIEW" | "READY" | "APPROVED" | "EXECUTED" | "REJECTED" | "FAILED_EXTRACTION" | "EXPIRED" | yes | — |
supplier | object | yes | — |
totals | object | yes | — |
updated_at | string | yes | ISO 8601 instant of the last change to the payable. |
validation | object | yes | — |
curl -X POST https://api.codespar.dev/v1/payables \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
}
}'POST /v1/payables HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
}
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
}
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
}
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables", {
body: {
schema_version: "payable.v1",
doc_type: "invoice",
attribution: {
channel: "upload",
credential_id: "credential_0000000000000000",
connection_id: "conn_0000000000000000",
actor: "string"
},
document_hash: "string",
supplier: {
document: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
name: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
}
},
recipient: {
document: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
name: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
}
},
invoice_number: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
serie: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
issue_date: {
value: "2026-01-15",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
due_date: {
value: "2026-01-15",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
items: [
{
description: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
quantity: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
unit_amount_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
total_amount_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
}
}
],
totals: {
subtotal_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
discount_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
tax_relief_deducted_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
freight_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
insurance_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
other_charges_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
taxes_added_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
taxes_included_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
grand_total_minor: {
value: 1000,
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
},
currency: {
value: "string",
from: "parser_local",
evidence: {
page: 0,
excerpt: "string",
verification: "verified"
},
confidence: 0
}
},
carriers: {
boleto: "string",
br_code: "string",
nfe: "string"
},
extraction: {
model: "string",
model_version: "string",
input_tokens: 0,
output_tokens: 0,
reasoning_tokens: 0
}
}
});
// r.status is one of the documented statuses (200, 422),
// each with its own body shape in r.data; nothing here throws on 422.
if (r.ok) {
console.log(r.data);
}{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
},
"payable_id": "payable_0000000000000000",
"status": "RECEIVED",
"rail_quote": {
"beneficiary": {
"value": "string",
"from": "trilho"
},
"beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"final_beneficiary": {
"value": "string",
"from": "trilho"
},
"final_beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"amount_minor": {
"value": 1000,
"from": "trilho"
},
"discount_minor": {
"value": 1000,
"from": "trilho"
},
"interest_minor": {
"value": 1000,
"from": "trilho"
},
"fine_minor": {
"value": 1000,
"from": "trilho"
},
"due_date": {
"value": "2026-01-15",
"from": "trilho"
},
"quoted_at": "2026-01-15T12:00:00.000Z"
},
"validation": {
"carrier_check_digits": {
"outcome": "ok",
"detail": "string"
},
"document_check_digits": {
"outcome": "ok",
"detail": "string"
},
"nfe_key_consistency": {
"outcome": "ok",
"detail": "string"
},
"arithmetic": {
"outcome": "ok",
"detail": "string"
},
"duplicate_document": {
"outcome": "ok",
"detail": "string"
},
"amount_matches_rail": {
"outcome": "ok",
"detail": "string"
},
"beneficiary_document_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"due_date_state": {
"outcome": "ok",
"detail": "string"
},
"pix_key_owner_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"supplier_registry": {
"outcome": "ok",
"detail": "string"
},
"bank_details_changed": {
"outcome": "ok",
"detail": "string"
}
},
"review": {
"validation_status": "REJECTED",
"review_by": [
{
"code": "string",
"cause": "check_failed"
}
],
"rejected_by": [
"string"
],
"confirmed_by": [
"string"
],
"awaiting": [
"string"
],
"not_covered": [
"string"
],
"review_triggers": [
"string"
],
"resolutions": [
{
"code": "string",
"resolution": "human_correction",
"at": "string",
"by": "string",
"by_source": "verified_user_token",
"fields": [
"string"
],
"confirmed_beneficiary_document": "string"
}
]
},
"payment": {
"state": "awaiting_approval",
"approval_id": "approval_0000000000000000",
"settled_against": "document_line",
"document_amount_minor": 1000,
"original_document_settled": false,
"settlement_note": "string",
"mandate_id": "mandate_0000000000000000",
"attempt_id": "attempt_0000000000000000",
"tries": 0,
"amount_minor": 1000,
"quote_hash": "string",
"claimed_at": "string",
"key_id": "key_0000000000000000",
"via": "string",
"executed_at": "string",
"receipt_id": "receipt_0000000000000000",
"transaction_id": "transaction_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"money_moved": true,
"uncertain_code": "string",
"hold_entry_id": "holdentry_0000000000000000",
"last_refusal": {
"code": "string",
"attempt_id": "attempt_0000000000000000",
"at": "string"
}
},
"created_at": "string",
"updated_at": "string",
"idempotent_replay": true
}POST /v1/payables/batches
https://api.codespar.dev/v1/payables/batchesCreate a batch of payables from files and items
At least one of file_ids (uploaded with POST /v1/payables/files) and items (stated in text). At most 20 files and 500 items. Every item is resolved on the rail before it can be confirmed.
What an item shows comes from the rail, never from the file. A boleto is quoted (amount, beneficiary, masked document, due date); a Pix key is resolved in the DICT (owner name, masked document); a BR Code is decoded with its CRC, and a dynamic one through the payment provider's own decode call (its location URL is never fetched by us). A bare Pix key never sets the amount: the person, the integration or the CSV column states it, and amount_source says which. Nothing a document says about a beneficiary or an amount changes those values. No model reads any file.
Identity. An item's item_id is the payable's id, derived from the batch and the item's content (the file hash and the carrier, or a row's content and reference), never from its position: re-uploading the same file into the same batch, reordering, or resuming never yields another item. A carrier repeated in the batch is flagged duplicate_in_batch; one already open or paid in the project fails duplicate_document.
One bill, two ways. A page that prints one boleto and one Pix code for the same charge is ONE item with two ways (Pix first); rail is the chosen way and rail_chosen_by says who chose it (default until a person or an integration sends PATCH { rail }). A way the rail refuses (an expired dynamic code, a boleto past its final date, a provider that did not answer) carries unavailable and the item switches to the other way (rail_switched); two ways whose own amounts differ wait for a choice (missing: ["rail"], ways_amount_mismatch). document carries what the page says about itself (title from a closed catalog, issuer, due date, the carrier's own amount), shown before the rail answers and never confirmed in its place.
Idempotency-Key is required; a replay with the same key answers the same batch.
Scope: payables:write.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
conversation_id | string | no | — |
file_ids | array of string | no | — |
items | array of object | no | — |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
201 | object | OK |
400 | object | The body did not match, the batch would be empty, or the Idempotency-Key is missing. |
409 | object | The Idempotency-Key was used with a different body. |
422 | object | A file_id that is not an unexpired file of this project, or more than 20 files / 500 items. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | pbat_ followed by 20 characters. |
confirmation | object,null | yes | — |
conversation_id | string,null | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
discarded | object,null | yes | Set once the batch was discarded (POST .../discard). With nothing dispatched before, the status is canceled; after a partial execution the status is computed from the items that remain, which kept their payments. |
idempotent_replay | boolean | yes | True when this answers an earlier call with the same Idempotency-Key. |
items | array of object | yes | — |
list_hash | string | yes | SHA-256 over the canonical list: for each non-excluded item in item_id order item_id|rail|destination id|amount_minor|currency, then the total. Send it back on pay. |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | yes | — |
status | "reviewing" | "ready" | "confirmed" | "executing" | "completed" | "partially_completed" | "failed" | "canceled" | yes | — |
totals | object | yes | — |
version | integer | yes | Bumps on every change to the list. A confirmation binds to one version. |
curl -X POST https://api.codespar.dev/v1/payables/batches \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
],
"source": "api",
"conversation_id": "conversation_0000000000000000"
}'POST /v1/payables/batches HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
],
"source": "api",
"conversation_id": "conversation_0000000000000000"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/batches",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
],
"source": "api",
"conversation_id": "conversation_0000000000000000"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/batches", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
],
"source": "api",
"conversation_id": "conversation_0000000000000000"
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/batches", {
header: {
"Idempotency-Key": "string"
},
body: {
file_ids: [
"string"
],
items: [
{
pix_key: "string",
boleto_line: "string",
br_code: "string",
amount_minor: 1000,
description: "string",
reference: "string"
}
],
source: "api",
conversation_id: "conversation_0000000000000000"
}
});
// r.status is one of the documented statuses (200, 422),
// each with its own body shape in r.data; nothing here throws on 422.
if (r.ok) {
console.log(r.data);
}{
"batch_id": "batch_0000000000000000",
"status": "reviewing",
"version": 0,
"list_hash": "string",
"currency": "BRL",
"totals": {
"items": 0,
"payable_items": 0,
"excluded": 0,
"amount_minor": 1000
},
"confirmation": {
"version": 0,
"list_hash": "string",
"confirmed_by": "string",
"confirmed_at": "string",
"mandate_id": "mandate_0000000000000000"
},
"discarded": {
"at": "string",
"items": 0
},
"items": [
{
"item_id": "item_0000000000000000",
"position": 0,
"origin": {
"file_id": "file_0000000000000000",
"page": 0,
"row": 0,
"kind": "boleto_line"
},
"rail": "boleto",
"rail_chosen_by": "default",
"ways": [
{
"rail": "pix",
"origin": {
"page": 0,
"kind": "boleto_line"
},
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"unavailable": {
"code": "br_code_expired",
"message": "string"
}
}
],
"document": {
"title": "Example",
"issuer": "string",
"due_date": "2026-01-15",
"amount_minor": 1000
},
"state": "needs_complement",
"missing": [
"amount"
],
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"amount_minor": 1000,
"amount_source": "rail_quote",
"description": "string",
"reference": "string",
"checks": [
{
"code": "carrier_check_digits",
"status": "pass",
"message": "string",
"simulated": true
}
],
"payment": {
"state": "executed",
"attempt_id": "attempt_0000000000000000",
"receipt_id": "receipt_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"refusal": {
"code": "string",
"message": "string"
},
"test_substitution": {
"settled_against": "sandbox_fixture",
"original_document_settled": false,
"document_amount_minor": 1000,
"note": "string"
}
}
}
],
"created_at": "string",
"source": "api",
"conversation_id": "conversation_0000000000000000",
"idempotent_replay": true
}GET /v1/payables/batches/{batchId}
https://api.codespar.dev/v1/payables/batches/{batchId}Read a batch, its items and their results
The batch as it is now: every item with its resolved destination, amount and source, the missing fields, the checks and, after paying, its own payment result. While a large batch executes, status is executing and this shows progress.
The confirmation binds to the list. version bumps on every change and list_hash is the SHA-256 of the canonical list (each non-excluded item in item_id order as item_id|rail|destination id|amount_minor|currency, then the total). Any change after a confirmation needs a new one.
Scope: payables:read.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | pbat_ followed by 20 characters. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | No batch with this id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | pbat_ followed by 20 characters. |
confirmation | object,null | yes | — |
conversation_id | string,null | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
discarded | object,null | yes | Set once the batch was discarded (POST .../discard). With nothing dispatched before, the status is canceled; after a partial execution the status is computed from the items that remain, which kept their payments. |
items | array of object | yes | — |
list_hash | string | yes | SHA-256 over the canonical list: for each non-excluded item in item_id order item_id|rail|destination id|amount_minor|currency, then the total. Send it back on pay. |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | yes | — |
status | "reviewing" | "ready" | "confirmed" | "executing" | "completed" | "partially_completed" | "failed" | "canceled" | yes | — |
totals | object | yes | — |
version | integer | yes | Bumps on every change to the list. A confirmation binds to one version. |
curl -X GET https://api.codespar.dev/v1/payables/batches/{batchId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/payables/batches/{batchId} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/payables/batches/{batchId}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/batches/{batchId}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/payables/batches/{batchId}", {
path: {
batchId: "batch_0000000000000000"
}
});{
"batch_id": "batch_0000000000000000",
"status": "reviewing",
"version": 0,
"list_hash": "string",
"currency": "BRL",
"totals": {
"items": 0,
"payable_items": 0,
"excluded": 0,
"amount_minor": 1000
},
"confirmation": {
"version": 0,
"list_hash": "string",
"confirmed_by": "string",
"confirmed_at": "string",
"mandate_id": "mandate_0000000000000000"
},
"discarded": {
"at": "string",
"items": 0
},
"items": [
{
"item_id": "item_0000000000000000",
"position": 0,
"origin": {
"file_id": "file_0000000000000000",
"page": 0,
"row": 0,
"kind": "boleto_line"
},
"rail": "boleto",
"rail_chosen_by": "default",
"ways": [
{
"rail": "pix",
"origin": {
"page": 0,
"kind": "boleto_line"
},
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"unavailable": {
"code": "br_code_expired",
"message": "string"
}
}
],
"document": {
"title": "Example",
"issuer": "string",
"due_date": "2026-01-15",
"amount_minor": 1000
},
"state": "needs_complement",
"missing": [
"amount"
],
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"amount_minor": 1000,
"amount_source": "rail_quote",
"description": "string",
"reference": "string",
"checks": [
{
"code": "carrier_check_digits",
"status": "pass",
"message": "string",
"simulated": true
}
],
"payment": {
"state": "executed",
"attempt_id": "attempt_0000000000000000",
"receipt_id": "receipt_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"refusal": {
"code": "string",
"message": "string"
},
"test_substitution": {
"settled_against": "sandbox_fixture",
"original_document_settled": false,
"document_amount_minor": 1000,
"note": "string"
}
}
}
],
"created_at": "string",
"source": "api",
"conversation_id": "conversation_0000000000000000"
}POST /v1/payables/batches/{batchId}/discard
https://api.codespar.dev/v1/payables/batches/{batchId}/discardDiscard what was never dispatched
Takes out of the list every item never dispatched (needs_complement, resolving, ready, rejected, including items a large batch has not reached yet): each becomes excluded, and the charge it held is free for a new batch. Items already dispatched, awaiting an approval, uncertain, executed or refused stay exactly as they are; a discard never stops or reverses money already in motion, and a later pay call with the same confirmation dispatches nothing new.
With nothing dispatched before, the batch becomes canceled: no change, add or pay is taken after (payable_batch_discarded). After a partial execution, status is the one the remaining items give. discarded records when and how many. Discarding again answers the batch as it is.
Scope: payables:write.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | — |
Request body
Type: object
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The body is not an empty object. |
404 | object | No batch with this id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | pbat_ followed by 20 characters. |
confirmation | object,null | yes | — |
conversation_id | string,null | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
discarded | object,null | yes | Set once the batch was discarded (POST .../discard). With nothing dispatched before, the status is canceled; after a partial execution the status is computed from the items that remain, which kept their payments. |
items | array of object | yes | — |
list_hash | string | yes | SHA-256 over the canonical list: for each non-excluded item in item_id order item_id|rail|destination id|amount_minor|currency, then the total. Send it back on pay. |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | yes | — |
status | "reviewing" | "ready" | "confirmed" | "executing" | "completed" | "partially_completed" | "failed" | "canceled" | yes | — |
totals | object | yes | — |
version | integer | yes | Bumps on every change to the list. A confirmation binds to one version. |
curl -X POST https://api.codespar.dev/v1/payables/batches/{batchId}/discard \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'POST /v1/payables/batches/{batchId}/discard HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/batches/{batchId}/discard",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/batches/{batchId}/discard", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await res.json();const result = await cs.api.post("/v1/payables/batches/{batchId}/discard", {
path: {
batchId: "batch_0000000000000000"
},
body: {}
});{
"batch_id": "batch_0000000000000000",
"status": "reviewing",
"version": 0,
"list_hash": "string",
"currency": "BRL",
"totals": {
"items": 0,
"payable_items": 0,
"excluded": 0,
"amount_minor": 1000
},
"confirmation": {
"version": 0,
"list_hash": "string",
"confirmed_by": "string",
"confirmed_at": "string",
"mandate_id": "mandate_0000000000000000"
},
"discarded": {
"at": "string",
"items": 0
},
"items": [
{
"item_id": "item_0000000000000000",
"position": 0,
"origin": {
"file_id": "file_0000000000000000",
"page": 0,
"row": 0,
"kind": "boleto_line"
},
"rail": "boleto",
"rail_chosen_by": "default",
"ways": [
{
"rail": "pix",
"origin": {
"page": 0,
"kind": "boleto_line"
},
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"unavailable": {
"code": "br_code_expired",
"message": "string"
}
}
],
"document": {
"title": "Example",
"issuer": "string",
"due_date": "2026-01-15",
"amount_minor": 1000
},
"state": "needs_complement",
"missing": [
"amount"
],
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"amount_minor": 1000,
"amount_source": "rail_quote",
"description": "string",
"reference": "string",
"checks": [
{
"code": "carrier_check_digits",
"status": "pass",
"message": "string",
"simulated": true
}
],
"payment": {
"state": "executed",
"attempt_id": "attempt_0000000000000000",
"receipt_id": "receipt_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"refusal": {
"code": "string",
"message": "string"
},
"test_substitution": {
"settled_against": "sandbox_fixture",
"original_document_settled": false,
"document_amount_minor": 1000,
"note": "string"
}
}
}
],
"created_at": "string",
"source": "api",
"conversation_id": "conversation_0000000000000000"
}POST /v1/payables/batches/{batchId}/items
https://api.codespar.dev/v1/payables/batches/{batchId}/itemsAdd files or items to an open batch
Same rules as creating: every new item is resolved on the rail; version bumps.
Scope: payables:write.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
expected_version | integer | yes | — |
file_ids | array of string | no | — |
items | array of object | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The body did not match, or nothing to add. |
404 | object | No batch with this id in this project. |
409 | object | Stale expected_version (current batch in details.batch), or the batch was confirmed or discarded. |
422 | object | A file_id that is not an unexpired file of this project, or the batch would pass 20 files / 500 items. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | pbat_ followed by 20 characters. |
confirmation | object,null | yes | — |
conversation_id | string,null | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
discarded | object,null | yes | Set once the batch was discarded (POST .../discard). With nothing dispatched before, the status is canceled; after a partial execution the status is computed from the items that remain, which kept their payments. |
items | array of object | yes | — |
list_hash | string | yes | SHA-256 over the canonical list: for each non-excluded item in item_id order item_id|rail|destination id|amount_minor|currency, then the total. Send it back on pay. |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | yes | — |
status | "reviewing" | "ready" | "confirmed" | "executing" | "completed" | "partially_completed" | "failed" | "canceled" | yes | — |
totals | object | yes | — |
version | integer | yes | Bumps on every change to the list. A confirmation binds to one version. |
curl -X POST https://api.codespar.dev/v1/payables/batches/{batchId}/items \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expected_version": 0,
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
]
}'POST /v1/payables/batches/{batchId}/items HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"expected_version": 0,
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
]
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/batches/{batchId}/items",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"expected_version": 0,
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
]
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/batches/{batchId}/items", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"expected_version": 0,
"file_ids": [
"string"
],
"items": [
{
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"amount_minor": 1000,
"description": "string",
"reference": "string"
}
]
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/batches/{batchId}/items", {
path: {
batchId: "batch_0000000000000000"
},
body: {
expected_version: 0,
file_ids: [
"string"
],
items: [
{
pix_key: "string",
boleto_line: "string",
br_code: "string",
amount_minor: 1000,
description: "string",
reference: "string"
}
]
}
});
// r.status is one of the documented statuses (200, 422),
// each with its own body shape in r.data; nothing here throws on 422.
if (r.ok) {
console.log(r.data);
}{
"batch_id": "batch_0000000000000000",
"status": "reviewing",
"version": 0,
"list_hash": "string",
"currency": "BRL",
"totals": {
"items": 0,
"payable_items": 0,
"excluded": 0,
"amount_minor": 1000
},
"confirmation": {
"version": 0,
"list_hash": "string",
"confirmed_by": "string",
"confirmed_at": "string",
"mandate_id": "mandate_0000000000000000"
},
"discarded": {
"at": "string",
"items": 0
},
"items": [
{
"item_id": "item_0000000000000000",
"position": 0,
"origin": {
"file_id": "file_0000000000000000",
"page": 0,
"row": 0,
"kind": "boleto_line"
},
"rail": "boleto",
"rail_chosen_by": "default",
"ways": [
{
"rail": "pix",
"origin": {
"page": 0,
"kind": "boleto_line"
},
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"unavailable": {
"code": "br_code_expired",
"message": "string"
}
}
],
"document": {
"title": "Example",
"issuer": "string",
"due_date": "2026-01-15",
"amount_minor": 1000
},
"state": "needs_complement",
"missing": [
"amount"
],
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"amount_minor": 1000,
"amount_source": "rail_quote",
"description": "string",
"reference": "string",
"checks": [
{
"code": "carrier_check_digits",
"status": "pass",
"message": "string",
"simulated": true
}
],
"payment": {
"state": "executed",
"attempt_id": "attempt_0000000000000000",
"receipt_id": "receipt_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"refusal": {
"code": "string",
"message": "string"
},
"test_substitution": {
"settled_against": "sandbox_fixture",
"original_document_settled": false,
"document_amount_minor": 1000,
"note": "string"
}
}
}
],
"created_at": "string",
"source": "api",
"conversation_id": "conversation_0000000000000000"
}PATCH /v1/payables/batches/{batchId}/items/{itemId}
https://api.codespar.dev/v1/payables/batches/{batchId}/items/{itemId}Complement, correct, choose the way or exclude one item
Re-resolves the item's destination on the rail, re-runs its checks, keeps every other item as it is and bumps version. A correction of the carrier is a new carrier for the same item. rail chooses the way of an item that has two (ways): the item re-resolves on it, rail_chosen_by becomes person (dashboard, chat) or integration (API key), and a switch after a confirmation needs a new one, since list_hash covers rail.
The confirmation binds to the list. version bumps on every change and list_hash is the SHA-256 of the canonical list (each non-excluded item in item_id order as item_id|rail|destination id|amount_minor|currency, then the total). Any change after a confirmation needs a new one.
Scope: payables:write.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | — |
itemId | string | yes | pbl_ followed by 16 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer | no | — |
boleto_line | string | no | — |
br_code | string | no | — |
description | string | no | — |
excluded | boolean | no | — |
expected_version | integer | yes | — |
pix_key | string | no | — |
rail | "pix" | "boleto" | no | The way to pay an item that has two (ways): the item re-resolves on it. Refused 409 payable_item_rail_unavailable when the item has no such way or it is unavailable. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The body did not match the schema. |
404 | object | No such batch or item in this project. |
409 | object | payable_batch_version_conflict: expected_version is stale; details.batch is the current batch. payable_batch_locked: the batch was confirmed. payable_item_rail_unavailable: rail names a way the item does not have, or one that is unavailable (details.unavailable says why, details.ways lists the item's ways). payable_batch_discarded: the batch was discarded. |
422 | object | The correction does not fit this item. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | pbat_ followed by 20 characters. |
confirmation | object,null | yes | — |
conversation_id | string,null | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
discarded | object,null | yes | Set once the batch was discarded (POST .../discard). With nothing dispatched before, the status is canceled; after a partial execution the status is computed from the items that remain, which kept their payments. |
items | array of object | yes | — |
list_hash | string | yes | SHA-256 over the canonical list: for each non-excluded item in item_id order item_id|rail|destination id|amount_minor|currency, then the total. Send it back on pay. |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | yes | — |
status | "reviewing" | "ready" | "confirmed" | "executing" | "completed" | "partially_completed" | "failed" | "canceled" | yes | — |
totals | object | yes | — |
version | integer | yes | Bumps on every change to the list. A confirmation binds to one version. |
curl -X PATCH https://api.codespar.dev/v1/payables/batches/{batchId}/items/{itemId} \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expected_version": 0,
"amount_minor": 1000,
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"description": "string",
"excluded": true,
"rail": "pix"
}'PATCH /v1/payables/batches/{batchId}/items/{itemId} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"expected_version": 0,
"amount_minor": 1000,
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"description": "string",
"excluded": true,
"rail": "pix"
}import os
import requests
res = requests.patch(
"https://api.codespar.dev/v1/payables/batches/{batchId}/items/{itemId}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"expected_version": 0,
"amount_minor": 1000,
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"description": "string",
"excluded": True,
"rail": "pix"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/batches/{batchId}/items/{itemId}", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"expected_version": 0,
"amount_minor": 1000,
"pix_key": "string",
"boleto_line": "string",
"br_code": "string",
"description": "string",
"excluded": true,
"rail": "pix"
}),
});
const data = await res.json();const r = await cs.api.response("patch", "/v1/payables/batches/{batchId}/items/{itemId}", {
path: {
batchId: "batch_0000000000000000",
itemId: "item_0000000000000000"
},
body: {
expected_version: 0,
amount_minor: 1000,
pix_key: "string",
boleto_line: "string",
br_code: "string",
description: "string",
excluded: true,
rail: "pix"
}
});
// r.status is one of the documented statuses (200, 422),
// each with its own body shape in r.data; nothing here throws on 422.
if (r.ok) {
console.log(r.data);
}{
"batch_id": "batch_0000000000000000",
"status": "reviewing",
"version": 0,
"list_hash": "string",
"currency": "BRL",
"totals": {
"items": 0,
"payable_items": 0,
"excluded": 0,
"amount_minor": 1000
},
"confirmation": {
"version": 0,
"list_hash": "string",
"confirmed_by": "string",
"confirmed_at": "string",
"mandate_id": "mandate_0000000000000000"
},
"discarded": {
"at": "string",
"items": 0
},
"items": [
{
"item_id": "item_0000000000000000",
"position": 0,
"origin": {
"file_id": "file_0000000000000000",
"page": 0,
"row": 0,
"kind": "boleto_line"
},
"rail": "boleto",
"rail_chosen_by": "default",
"ways": [
{
"rail": "pix",
"origin": {
"page": 0,
"kind": "boleto_line"
},
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"unavailable": {
"code": "br_code_expired",
"message": "string"
}
}
],
"document": {
"title": "Example",
"issuer": "string",
"due_date": "2026-01-15",
"amount_minor": 1000
},
"state": "needs_complement",
"missing": [
"amount"
],
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"amount_minor": 1000,
"amount_source": "rail_quote",
"description": "string",
"reference": "string",
"checks": [
{
"code": "carrier_check_digits",
"status": "pass",
"message": "string",
"simulated": true
}
],
"payment": {
"state": "executed",
"attempt_id": "attempt_0000000000000000",
"receipt_id": "receipt_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"refusal": {
"code": "string",
"message": "string"
},
"test_substitution": {
"settled_against": "sandbox_fixture",
"original_document_settled": false,
"document_amount_minor": 1000,
"note": "string"
}
}
}
],
"created_at": "string",
"source": "api",
"conversation_id": "conversation_0000000000000000"
}POST /v1/payables/batches/{batchId}/pay
https://api.codespar.dev/v1/payables/batches/{batchId}/paypays every payable item through the governed consumer-payment cycle
Confirm the exact list and pay it
Records the confirmation (version, hash, who, mandate) and pays every payable item through the governed consumer-payment cycle: mandate verification, policy, caps under lock, hold, dispatch with uncertainty classification and a sealed receipt. Each item gets its own payment: an item refused by the mandate, a policy or funds is refused with the code and does not stop the others.
Who confirms. A verified person through the dashboard (the decide gate, enforcement on), or an API key with payables:pay and a mandate_id of this organization.
Resume, never pay twice. A later call with the same version and list_hash resumes: executed items are returned as they are, an uncertain item is looked up on the payment's own record first and never resent, and only items never dispatched are dispatched. Two concurrent calls claim items with a conditional update, so an item is dispatched at most once. The attempt id is derived from the item and its quote, so the same item at the same quote is the same attempt, hold and receipt.
The rail is consulted again before each dispatch; an answer that is not the confirmed one is refused with payable_quote_diverged and nothing is sent. A large batch may answer with status: executing after about 20 seconds: repeat the same call (same version and list_hash) to continue with the items never dispatched; GET shows progress.
Idempotency-Key is required.
Scope: payables:pay.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
batchId | string | yes | — |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
list_hash | string | yes | — |
mandate_id | string | no | Required for an API key. A chat batch paid by its person uses the conversation's Shop mandate when absent. |
version | integer | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The body did not match, or the Idempotency-Key is missing. |
403 | object | The dashboard path confirms only as a verified person: the person's session token is missing, invalid or names someone else. An API key never takes this path; it pays with payables:pay and a mandate_id. mandate_paused, mandate_revoked, mandate_expired: the mandate_id a first confirmation names cannot pay; nothing is recorded, and the batch can be confirmed under an active mandate. |
404 | object | No batch with this id in this project, or no such mandate in this organization. |
409 | object | payable_batch_version_mismatch: version/list_hash are not the current list's; details.batch is the current batch. payable_batch_discarded: the batch was discarded. |
422 | object | payable_batch_not_ready: a non-excluded item is not ready (details.items). payable_batch_mandate_required: an API key named no mandate_id. payable_batch_mandate_unavailable: the conversation has no Shop mandate to pay under. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
batch_id | string | yes | pbat_ followed by 20 characters. |
confirmation | object,null | yes | — |
conversation_id | string,null | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
discarded | object,null | yes | Set once the batch was discarded (POST .../discard). With nothing dispatched before, the status is canceled; after a partial execution the status is computed from the items that remain, which kept their payments. |
items | array of object | yes | — |
list_hash | string | yes | SHA-256 over the canonical list: for each non-excluded item in item_id order item_id|rail|destination id|amount_minor|currency, then the total. Send it back on pay. |
source | "api" | "chat" | "dashboard" | "cli" | "mcp" | yes | — |
status | "reviewing" | "ready" | "confirmed" | "executing" | "completed" | "partially_completed" | "failed" | "canceled" | yes | — |
totals | object | yes | — |
version | integer | yes | Bumps on every change to the list. A confirmation binds to one version. |
curl -X POST https://api.codespar.dev/v1/payables/batches/{batchId}/pay \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"version": 0,
"list_hash": "string",
"mandate_id": "mandate_0000000000000000"
}'POST /v1/payables/batches/{batchId}/pay HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"version": 0,
"list_hash": "string",
"mandate_id": "mandate_0000000000000000"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/batches/{batchId}/pay",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"version": 0,
"list_hash": "string",
"mandate_id": "mandate_0000000000000000"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/batches/{batchId}/pay", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"version": 0,
"list_hash": "string",
"mandate_id": "mandate_0000000000000000"
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/batches/{batchId}/pay", {
path: {
batchId: "batch_0000000000000000"
},
header: {
"Idempotency-Key": "string"
},
body: {
version: 0,
list_hash: "string",
mandate_id: "mandate_0000000000000000"
}
});
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"batch_id": "batch_0000000000000000",
"status": "reviewing",
"version": 0,
"list_hash": "string",
"currency": "BRL",
"totals": {
"items": 0,
"payable_items": 0,
"excluded": 0,
"amount_minor": 1000
},
"confirmation": {
"version": 0,
"list_hash": "string",
"confirmed_by": "string",
"confirmed_at": "string",
"mandate_id": "mandate_0000000000000000"
},
"discarded": {
"at": "string",
"items": 0
},
"items": [
{
"item_id": "item_0000000000000000",
"position": 0,
"origin": {
"file_id": "file_0000000000000000",
"page": 0,
"row": 0,
"kind": "boleto_line"
},
"rail": "boleto",
"rail_chosen_by": "default",
"ways": [
{
"rail": "pix",
"origin": {
"page": 0,
"kind": "boleto_line"
},
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"unavailable": {
"code": "br_code_expired",
"message": "string"
}
}
],
"document": {
"title": "Example",
"issuer": "string",
"due_date": "2026-01-15",
"amount_minor": 1000
},
"state": "needs_complement",
"missing": [
"amount"
],
"destination": {
"kind": "boleto",
"display": "string",
"beneficiary_name": "Example",
"beneficiary_document_masked": "string",
"due_date": "2026-01-15",
"beneficiary_institution": "string",
"key": "string"
},
"amount_minor": 1000,
"amount_source": "rail_quote",
"description": "string",
"reference": "string",
"checks": [
{
"code": "carrier_check_digits",
"status": "pass",
"message": "string",
"simulated": true
}
],
"payment": {
"state": "executed",
"attempt_id": "attempt_0000000000000000",
"receipt_id": "receipt_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"refusal": {
"code": "string",
"message": "string"
},
"test_substitution": {
"settled_against": "sandbox_fixture",
"original_document_settled": false,
"document_amount_minor": 1000,
"note": "string"
}
}
}
],
"created_at": "string",
"source": "api",
"conversation_id": "conversation_0000000000000000"
}GET /v1/payables/counts
https://api.codespar.dev/v1/payables/countsCount the payables waiting for a person
How many of this project's payables are NEEDS_REVIEW and how many are READY, and how many unpaid open ones fall due in the rest of this week. due_this_week counts READY and APPROVED payables (an APPROVED payment is claimed or awaiting an approval and has not paid yet) whose EFFECTIVE due date is from today to Sunday of the current São Paulo week, inclusive (the day taken in America/Sao_Paulo; weeks run Monday to Sunday). The effective due date is the printed one rolled forward to the next bank business day, the day until which the bill is payable without charges: a boleto due last Saturday is due this Monday. Due today is in it; an effective due date before today is overdue and is not. NEEDS_REVIEW is not in due_this_week, and a payable with no due date never is.
overdue counts the same READY and APPROVED set whose effective due date is before today.
Scope: payables:read.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
due_this_week | integer | yes | Unpaid open payables (READY and APPROVED) whose EFFECTIVE due date (due_date_effective) is from today to Sunday of the current São Paulo week, inclusive. Due today is in it; an effective due date before today is overdue and is not; NEEDS_REVIEW is not in it. |
needs_review | integer | yes | Payables in NEEDS_REVIEW. |
overdue | integer | yes | The same unpaid open payables (READY and APPROVED) whose EFFECTIVE due date is before today in São Paulo. A printed date before today that rolls forward to today is not overdue; NEEDS_REVIEW is not in it. |
ready | integer | yes | Payables in READY. |
curl -X GET https://api.codespar.dev/v1/payables/counts \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/payables/counts HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/payables/counts",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/counts", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/payables/counts");{
"needs_review": 0,
"ready": 0,
"due_this_week": 0,
"overdue": 0
}POST /v1/payables/documents
https://api.codespar.dev/v1/payables/documentsCreate a payable from an uploaded document (PDF or image)
The file goes in the multipart field document (PDF, PNG, JPEG or WEBP by its bytes; the declared type is not consulted), an optional text field actor names the person who uploaded it, and the payable is READ from the document: attribution.channel is upload, every field carries from: "modelo", a page and a literal excerpt, and extraction names the model and its token counts. From there it enters the same pipeline as POST /v1/payables — the same schema, the same deterministic checks, the same duplicate axes, the same statuses — and answers the same 200/201 shape. A JSON body sent here answers 415 payable_document_required.
Upload (multipart). The door is behind a deployment switch: while it is off every upload answers 503 payable_extraction_disabled and nothing is read or stored. The file is checked before any reader sees it — real type by magic bytes (415 payable_document_type_unsupported; HEIC is detected and named but not accepted), at most 10 MB (413 payable_document_too_large), at most 10 pages (422 payable_document_too_many_pages), no JavaScript, embedded files, forms or launch actions (422 payable_document_active_content; refused, not stripped, so the hash on the payable is the hash of the bytes sent), and it must open (422 payable_document_unreadable, details.reason encrypted or malformed). The SHA-256 of the file is document_hash, computed before anything else: a file an open or paid payable of this project already holds answers 409 payable_duplicate on the document_hash axis without being read again. The original file is NOT kept; re-extraction is re-upload.
The reader's citations are checked against the PDF's text layer (§7): each field's evidence.verification is verified, moved (the excerpt is on another page) or unverifiable (an image, or a PDF with no text layer — a scan). moved and unverifiable on a critical field open the critical_field_without_evidence review trigger; an excerpt that is in NO page of a critical field refuses the whole document, 422 payable_evidence_fabricated, and nothing is stored. A reader that did not find enough for the schema answers 422 payable_extraction_incomplete naming the missing paths. A reader that did not answer after a bounded retry answers 502 payable_extraction_unavailable (retriable, nothing stored; the original is not kept, so there is no failed row to resume); a reader that declined the document answers 422 payable_extraction_refused. Nothing of the document's content reaches a log, an audit event or an error body: the trail carries the hash, the model, the token counts and the verdict counts.
The amount and the payee come from the rail, never from what was sent. amount_matches_rail, beneficiary_document_matches_supplier and due_date_state run against a consult of the boleto on the payable, and they block READY whether or not they answered. A consult that could not be made — no connected payment provider, a provider that did not answer — leaves them indisponivel and the payable NEEDS_REVIEW, named under review.review_by. pix_key_owner_matches_supplier is not consulted in this release and blocks a payable carrying a Pix BR Code with cause required_not_implemented. VALIDATING is never returned: nothing re-runs a validation yet, so a check with no answer goes to a person rather than waiting. due_date_state calls a payable overdue only after its EFFECTIVE due date: a due date on a weekend, a national bank holiday (Carnival Monday and Tuesday and Corpus Christi included) or, for a boleto, the last business day of the year is payable without charges on the next business day. State and municipal holidays are not considered.
Status. Two fields answer two questions. review.validation_status is what the checks alone say: REJECTED (a check that rejects failed), NEEDS_REVIEW (a check failed, or a required check has no answer it can have), VALIDATING (a required, implemented check did not answer and will be retried) or VALIDATED (every required check passed). status is what the payable may do: READY only from VALIDATED with no review trigger open, and NEEDS_REVIEW when a trigger is open. The trigger this API evaluates is new_supplier: this project has no executed payable for the supplier's document, or the payable names no supplier document. It is evaluated per project, never across projects.
Why things are in review. review.review_by lists each check that sends the payable to a person and its cause: check_failed, retries_exhausted or required_not_implemented. review.review_triggers lists the open triggers. review.not_covered lists checks that did not run and do not block READY in this release (supplier_registry, bank_details_changed).
Idempotency. Send an Idempotency-Key header to make a retry safe. The same key with the same file answers 200 with idempotent_replay: true; the same key with a different file answers 409 idempotency_key_conflict. Both are checked before the reader runs, so a retry never spends a model call.
Scope: payables:write.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
201 | object | OK |
400 | object | The multipart field document is absent or empty (payable_document_required), the actor field is out of shape or the multipart body could not be read (invalid_body), or the Idempotency-Key header is empty or longer than 255 characters. |
409 | object | payable_duplicate: an open or paid payable of this project already holds this file's document_hash (refused before the reader runs; no details.validation) or shares a carrier with what was read. idempotency_key_conflict: the key was used with a different file. |
413 | object | The uploaded file exceeds 10 MB. Nothing was read or stored. |
415 | object | payable_document_type_unsupported: the file is not a PDF, PNG, JPEG or WEBP by its bytes; details.detected names what it is when known (image/heic is detected and not accepted). payable_document_required: the body was not multipart/form-data at all. |
422 | object | The PDF has more than 10 pages (payable_document_too_many_pages), carries active content (payable_document_active_content, details.found), or cannot be opened (payable_document_unreadable, details.reason); the reader cited, for a critical field, a passage in no page (payable_evidence_fabricated, details.fields); the reader found too little (payable_extraction_incomplete, details.issues); the reader declined the document (payable_extraction_refused); or a carrier it read fails its check digits (payable_carrier_malformed). None of these stores anything. |
502 | object | payable_extraction_unavailable: the reader did not answer usably after a bounded retry (details.attempts); nothing was stored; retry later with the same Idempotency-Key. payable_extraction_failed: the extraction request was refused for a reason on our side; not fixable by retrying. |
503 | object | payable_extraction_disabled: the document door is switched off on this deployment; send the payable as JSON (structured or manual). payable_extractor_unconfigured: the door is on with no reader behind it; a deployment fault, not retriable. vault_unavailable: the key that protects supplier documents could not be read; retry later with the same Idempotency-Key. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
attribution | object | yes | — |
carriers | object | no | — |
created_at | string | yes | ISO 8601 instant the payable was created. |
doc_type | "invoice" | "danfe_nfe" | "boleto" | "unknown" | yes | — |
document_hash | string | no | — |
due_date | object | no | — |
extraction | object | no | — |
idempotent_replay | boolean | yes | True when this answer replays an earlier call made with the same Idempotency-Key. |
invoice_number | object | no | — |
issue_date | object | no | — |
items | array of object | no | — |
payable_id | string | yes | — |
payment | object,null | yes | What paying it did (ent#1587); null until a payment was attempted. |
rail_quote | object | no | — |
recipient | object | no | — |
review | object | yes | — |
schema_version | "payable.v1" | yes | — |
serie | object | no | — |
status | "RECEIVED" | "EXTRACTING" | "EXTRACTED" | "VALIDATING" | "VALIDATED" | "NEEDS_REVIEW" | "READY" | "APPROVED" | "EXECUTED" | "REJECTED" | "FAILED_EXTRACTION" | "EXPIRED" | yes | — |
supplier | object | yes | — |
totals | object | yes | — |
updated_at | string | yes | ISO 8601 instant of the last change to the payable. |
validation | object | yes | — |
curl -X POST https://api.codespar.dev/v1/payables/documents \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/payables/documents HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/documents",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/documents", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/documents", {
body: {
document: "string",
actor: "string"
}
});
// r.status is one of the documented statuses (200, 422),
// each with its own body shape in r.data; nothing here throws on 422.
if (r.ok) {
console.log(r.data);
}{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
},
"payable_id": "payable_0000000000000000",
"status": "RECEIVED",
"rail_quote": {
"beneficiary": {
"value": "string",
"from": "trilho"
},
"beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"final_beneficiary": {
"value": "string",
"from": "trilho"
},
"final_beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"amount_minor": {
"value": 1000,
"from": "trilho"
},
"discount_minor": {
"value": 1000,
"from": "trilho"
},
"interest_minor": {
"value": 1000,
"from": "trilho"
},
"fine_minor": {
"value": 1000,
"from": "trilho"
},
"due_date": {
"value": "2026-01-15",
"from": "trilho"
},
"quoted_at": "2026-01-15T12:00:00.000Z"
},
"validation": {
"carrier_check_digits": {
"outcome": "ok",
"detail": "string"
},
"document_check_digits": {
"outcome": "ok",
"detail": "string"
},
"nfe_key_consistency": {
"outcome": "ok",
"detail": "string"
},
"arithmetic": {
"outcome": "ok",
"detail": "string"
},
"duplicate_document": {
"outcome": "ok",
"detail": "string"
},
"amount_matches_rail": {
"outcome": "ok",
"detail": "string"
},
"beneficiary_document_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"due_date_state": {
"outcome": "ok",
"detail": "string"
},
"pix_key_owner_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"supplier_registry": {
"outcome": "ok",
"detail": "string"
},
"bank_details_changed": {
"outcome": "ok",
"detail": "string"
}
},
"review": {
"validation_status": "REJECTED",
"review_by": [
{
"code": "string",
"cause": "check_failed"
}
],
"rejected_by": [
"string"
],
"confirmed_by": [
"string"
],
"awaiting": [
"string"
],
"not_covered": [
"string"
],
"review_triggers": [
"string"
],
"resolutions": [
{
"code": "string",
"resolution": "human_correction",
"at": "string",
"by": "string",
"by_source": "verified_user_token",
"fields": [
"string"
],
"confirmed_beneficiary_document": "string"
}
]
},
"payment": {
"state": "awaiting_approval",
"approval_id": "approval_0000000000000000",
"settled_against": "document_line",
"document_amount_minor": 1000,
"original_document_settled": false,
"settlement_note": "string",
"mandate_id": "mandate_0000000000000000",
"attempt_id": "attempt_0000000000000000",
"tries": 0,
"amount_minor": 1000,
"quote_hash": "string",
"claimed_at": "string",
"key_id": "key_0000000000000000",
"via": "string",
"executed_at": "string",
"receipt_id": "receipt_0000000000000000",
"transaction_id": "transaction_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"money_moved": true,
"uncertain_code": "string",
"hold_entry_id": "holdentry_0000000000000000",
"last_refusal": {
"code": "string",
"attempt_id": "attempt_0000000000000000",
"at": "string"
}
},
"created_at": "string",
"updated_at": "string",
"idempotent_replay": true
}POST /v1/payables/files
https://api.codespar.dev/v1/payables/filesUpload a document or a spreadsheet of payables
One file per request, in the multipart field file: a PDF, a PNG, JPEG or WEBP image, a CSV or an XLSX, accepted by its bytes (the declared type is not consulted). At most 10 MB, 10 pages and 500 rows. A local reader runs in the request — PDF text layer (boleto lines and barcodes, BR Codes, labelled Pix keys), QR codes and boleto barcodes in images, rows of a sheet (pix_key | boleto_line | br_code, amount, description, reference; Portuguese or English headers) — and what it cannot resolve still becomes an item that asks for the missing field. The original file is not kept: only its SHA-256, kind, size and the items found. The file_id is project-scoped and expires in 24 hours if no batch uses it.
Scope: payables:write.
Responses
| Status | Body | Description |
|---|---|---|
201 | object | OK |
400 | object | No multipart part named file, or an empty one; or the body could not be read. |
413 | object | Over 10 MB. Nothing was read or kept. |
415 | object | Not a PDF, PNG, JPEG, WEBP, CSV or XLSX by its bytes. |
422 | object | payable_file_unreadable: details.reason is too_many_pages, active_content, encrypted, malformed, resource_limit, webp_unsupported or sheet_unreadable. payable_file_rows_exceeded: more than 500 data rows. |
503 | object | Every document inspector is busy; retry in a few seconds. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
expires_at | string | yes | ISO 8601. A file no batch used by then is forgotten. |
file_id | string | yes | pfile_ followed by 20 characters. Project-scoped; used by a batch, by the chat and by the remote MCP. |
items_found | integer | yes | Charges found by the local reader (boleto lines, BR Codes, Pix keys, QR codes, rows). |
kind | "pdf" | "image" | "csv" | "xlsx" | yes | — |
sha256 | string | yes | SHA-256 of the bytes as sent. The file itself is not kept. |
size_bytes | integer | yes | — |
unresolved | integer | yes | Of those, how many still miss something the person has to complete (an amount, a destination). |
curl -X POST https://api.codespar.dev/v1/payables/files \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/payables/files HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/files",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/files", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/files", {
body: {
file: "string"
}
});
// r.status is one of the documented statuses (200, 422),
// each with its own body shape in r.data; nothing here throws on 422.
if (r.ok) {
console.log(r.data);
}{
"file_id": "file_0000000000000000",
"kind": "pdf",
"sha256": "string",
"size_bytes": 0,
"items_found": 0,
"unresolved": 0,
"expires_at": "string"
}POST /v1/payables/files/authorizations
https://api.codespar.dev/v1/payables/files/authorizationsAuthorize a browser to upload files straight to the API
For a browser that cannot send the file through its own server (a proxy that refuses large bodies). Returns a short-lived token and the URL that accepts it: POST upload_url with Authorization: Bearer <token> and the multipart field file, exactly as POST /v1/payables/files. The token uploads at most max_files files of at most max_bytes each into this caller's org, project and environment until expires_at (10 minutes), attributed to this caller; it reads nothing and is accepted by no other route. Only its SHA-256 is kept. Use upload_url as given: it is authenticated by the token alone (send it in the header, never in the URL), answers the dashboard's browser origins only and never with credentials, spends one use per request whatever the outcome, and refuses an absent, unknown, expired or used-up token with 401 payable_upload_token_invalid. Its other answers are those of POST /v1/payables/files.
Scope: payables:write.
Request body
Type: object
Responses
| Status | Body | Description |
|---|---|---|
201 | object | OK |
400 | object | The body is not an empty JSON object. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
expires_at | string | yes | ISO 8601, ten minutes from now. |
max_bytes | integer | yes | The size limit of each file: 10485760. |
max_files | integer | yes | How many uploads the token allows: 20. |
token | string | yes | pfup_ followed by 32 characters. Shown once; it uploads files into this project and authorizes nothing else. |
upload_url | string | yes | Where the browser posts each file (multipart field file) with Authorization: Bearer <token>. Use it as given. |
curl -X POST https://api.codespar.dev/v1/payables/files/authorizations \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'POST /v1/payables/files/authorizations HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/files/authorizations",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/files/authorizations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await res.json();const result = await cs.api.post("/v1/payables/files/authorizations", {
body: {}
});{
"upload_url": "https://example.com/hook",
"token": "string",
"expires_at": "string",
"max_bytes": 1,
"max_files": 1
}GET /v1/payables/{payableId}
https://api.codespar.dev/v1/payables/{payableId}Read a payable
The payable, its status, the outcome of every check in validation, and why it has that status in review. Reading never re-runs a check or changes the status. The amount and the payee come from the rail, never from what was sent. amount_matches_rail, beneficiary_document_matches_supplier and due_date_state run against a consult of the boleto on the payable, and they block READY whether or not they answered. A consult that could not be made — no connected payment provider, a provider that did not answer — leaves them indisponivel and the payable NEEDS_REVIEW, named under review.review_by. pix_key_owner_matches_supplier is not consulted in this release and blocks a payable carrying a Pix BR Code with cause required_not_implemented. VALIDATING is never returned: nothing re-runs a validation yet, so a check with no answer goes to a person rather than waiting. due_date_state calls a payable overdue only after its EFFECTIVE due date: a due date on a weekend, a national bank holiday (Carnival Monday and Tuesday and Corpus Christi included) or, for a boleto, the last business day of the year is payable without charges on the next business day. State and municipal holidays are not considered.
Status. Two fields answer two questions. review.validation_status is what the checks alone say: REJECTED (a check that rejects failed), NEEDS_REVIEW (a check failed, or a required check has no answer it can have), VALIDATING (a required, implemented check did not answer and will be retried) or VALIDATED (every required check passed). status is what the payable may do: READY only from VALIDATED with no review trigger open, and NEEDS_REVIEW when a trigger is open. The trigger this API evaluates is new_supplier: this project has no executed payable for the supplier's document, or the payable names no supplier document. It is evaluated per project, never across projects.
Why things are in review. review.review_by lists each check that sends the payable to a person and its cause: check_failed, retries_exhausted or required_not_implemented. review.review_triggers lists the open triggers. review.not_covered lists checks that did not run and do not block READY in this release (supplier_registry, bank_details_changed).
A payable of another project, of another organization and an id that is not a payable id all answer the same 404.
Scope: payables:read.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
payableId | string | yes | pbl_ followed by 16 characters. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | No payable with this id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
attribution | object | yes | — |
carriers | object | no | — |
created_at | string | yes | ISO 8601 instant the payable was created. |
doc_type | "invoice" | "danfe_nfe" | "boleto" | "unknown" | yes | — |
document_hash | string | no | — |
due_date | object | no | — |
extraction | object | no | — |
invoice_number | object | no | — |
issue_date | object | no | — |
items | array of object | no | — |
payable_id | string | yes | — |
payment | object,null | yes | What paying it did (ent#1587); null until a payment was attempted. |
rail_quote | object | no | — |
recipient | object | no | — |
review | object | yes | — |
schema_version | "payable.v1" | yes | — |
serie | object | no | — |
status | "RECEIVED" | "EXTRACTING" | "EXTRACTED" | "VALIDATING" | "VALIDATED" | "NEEDS_REVIEW" | "READY" | "APPROVED" | "EXECUTED" | "REJECTED" | "FAILED_EXTRACTION" | "EXPIRED" | yes | — |
supplier | object | yes | — |
totals | object | yes | — |
updated_at | string | yes | ISO 8601 instant of the last change to the payable. |
validation | object | yes | — |
curl -X GET https://api.codespar.dev/v1/payables/{payableId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/payables/{payableId} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/payables/{payableId}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/{payableId}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/payables/{payableId}", {
path: {
payableId: "payable_0000000000000000"
}
});{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
},
"payable_id": "payable_0000000000000000",
"status": "RECEIVED",
"rail_quote": {
"beneficiary": {
"value": "string",
"from": "trilho"
},
"beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"final_beneficiary": {
"value": "string",
"from": "trilho"
},
"final_beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"amount_minor": {
"value": 1000,
"from": "trilho"
},
"discount_minor": {
"value": 1000,
"from": "trilho"
},
"interest_minor": {
"value": 1000,
"from": "trilho"
},
"fine_minor": {
"value": 1000,
"from": "trilho"
},
"due_date": {
"value": "2026-01-15",
"from": "trilho"
},
"quoted_at": "2026-01-15T12:00:00.000Z"
},
"validation": {
"carrier_check_digits": {
"outcome": "ok",
"detail": "string"
},
"document_check_digits": {
"outcome": "ok",
"detail": "string"
},
"nfe_key_consistency": {
"outcome": "ok",
"detail": "string"
},
"arithmetic": {
"outcome": "ok",
"detail": "string"
},
"duplicate_document": {
"outcome": "ok",
"detail": "string"
},
"amount_matches_rail": {
"outcome": "ok",
"detail": "string"
},
"beneficiary_document_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"due_date_state": {
"outcome": "ok",
"detail": "string"
},
"pix_key_owner_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"supplier_registry": {
"outcome": "ok",
"detail": "string"
},
"bank_details_changed": {
"outcome": "ok",
"detail": "string"
}
},
"review": {
"validation_status": "REJECTED",
"review_by": [
{
"code": "string",
"cause": "check_failed"
}
],
"rejected_by": [
"string"
],
"confirmed_by": [
"string"
],
"awaiting": [
"string"
],
"not_covered": [
"string"
],
"review_triggers": [
"string"
],
"resolutions": [
{
"code": "string",
"resolution": "human_correction",
"at": "string",
"by": "string",
"by_source": "verified_user_token",
"fields": [
"string"
],
"confirmed_beneficiary_document": "string"
}
]
},
"payment": {
"state": "awaiting_approval",
"approval_id": "approval_0000000000000000",
"settled_against": "document_line",
"document_amount_minor": 1000,
"original_document_settled": false,
"settlement_note": "string",
"mandate_id": "mandate_0000000000000000",
"attempt_id": "attempt_0000000000000000",
"tries": 0,
"amount_minor": 1000,
"quote_hash": "string",
"claimed_at": "string",
"key_id": "key_0000000000000000",
"via": "string",
"executed_at": "string",
"receipt_id": "receipt_0000000000000000",
"transaction_id": "transaction_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"money_moved": true,
"uncertain_code": "string",
"hold_entry_id": "holdentry_0000000000000000",
"last_refusal": {
"code": "string",
"attempt_id": "attempt_0000000000000000",
"at": "string"
}
},
"created_at": "string",
"updated_at": "string"
}POST /v1/payables/{payableId}/pay
https://api.codespar.dev/v1/payables/{payableId}/payPays a `READY` payable through the governed consumer-payment cycle
Pay a payable under a consumer mandate
Pays a READY payable through the governed consumer-payment cycle — mandate HMAC verification, policy, cap under lock, hold, dispatch with uncertainty classification and a sealed receipt — and moves it to EXECUTED. Only a boleto is paid in this wave.
The rail is consulted again first. The stored quote may have aged, so the eleven checks re-run over a fresh consult, with the review's confirmations standing. If that verdict no longer allows paying, nothing is dispatched: the payable goes back to NEEDS_REVIEW with the new verdict and the call answers payable_quote_diverged. READY is derived from a verdict, never assumed from the row.
The attempt id is derived, from the payable id and the fresh quote (payment.attempt_id). The same quote is the same attempt — a retry lands on the same hold, the same provider anchor and the same receipt — and a different quote is a different attempt, which is why the payable leaves READY before money moves.
What the outcome does to the payable. Success: EXECUTED, payment.state = executed, with receipt_id, transaction_id and end_to_end_id; the response also carries receipt. A refusal that provably moved no money (policy, cap, allowlist, funds, boleto_amount_mismatch): the payable is READY again with payment.last_refusal. A dispatch whose outcome is unknown (psp_dispatch_uncertain, psp_attempt_uncertain, payable_dispatch_unknown): the payable stays APPROVED with payment.state = uncertain, pinned for reconciliation — do not resubmit it, under this payable or another; a later call answers payable_payment_uncertain.
The receipt carries the document hash. Its delivery proof is a merchant_document_ref of kind payable whose reference is payable:<id>;sha256:<document_hash> (the hash part only when the payable has one), covered by the receipt's chain: document → mandate → payment → receipt verifies offline.
A policy approval holds the payable. When the durable policy engine routes the spend to a person (403 policy_denied with approval_id), the payable leaves READY under that approval (payment.state = awaiting_approval, payment.approval_id); /pay answers payable_payment_pending_approval until it is decided. The approved execution pays the attempt and records the outcome on the payable; a denied or expired approval is released by the next /pay, which asks the gate again.
Test projects: the document's own line first, a substituted test payment otherwise. A boleto in a test project is settled as its own line when the sandbox knows it. When it does not, a test line (the sandbox fixture) is paid in its place, and only at the confirmed amount: a fixture of another amount is refused sandbox_fixture_amount_mismatch (409) before anything is held. A substituted payment says so: payment.settled_against = sandbox_fixture, payment.original_document_settled = false, payment.settlement_note in the person's words, payment.document_amount_minor keeps what the document quoted, and the receipt's delivery reference ends in ;settled_against:sandbox_fixture. It is never the settlement of the original document, even at an equal amount. Live projects are untouched.
Scope: payables:pay. The mandate must be one of this organization's consumers' and its merchant_allowlist must name the boleto's digitable line.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
payableId | string | yes | pbl_ followed by 16 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
mandate_id | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The body did not match the pay schema. |
403 | object | policy_denied: the durable policy engine refused (reason, ruleId; approval_id when it routed the spend to a person — once approved, the same attempt is paid and a later call to this route records it). payee_not_allowed: the mandate's allowlist does not name the boleto. |
404 | object | not_found: no payable with this id in this project. mandate_not_found: no consumer mandate with this id in this organization. |
409 | object | payable_not_payable: not READY; details.status says what it is. payable_already_paid: EXECUTED; details.receipt_id. payable_payment_in_flight: a dispatch is running. payable_payment_uncertain: a dispatch outcome is unknown and the payable is pinned — do not resubmit. payable_payment_pending_approval: a person is deciding the policy approval (details.approval_id). payable_quote_diverged: the fresh verdict no longer allows paying; the payable is back in NEEDS_REVIEW and details.review says why. payable_payment_conflict: the row moved while this payment was planned; read it again. boleto_amount_mismatch: the provider's true amount moved between the fresh quote and the confirm; nothing paid, the payable is READY. |
422 | object | payable_rail_unsupported: no boleto carrier to pay by. insufficient_funds and the mandate cap codes carry their usual consumer-payment meanings; the payable is READY again. per_tx_cap_exceeded: the payable's amount is above the mandate's per-transaction cap; a boleto is refused, never routed to approval. details carries amount_minor and per_tx_cap_minor (integer strings in the mandate's own unit: minor for a v1 mandate, currency-native for unit_version: 2) and currency, beside payable_status and the message "<amount> > <cap>". |
502 | object | The dispatch did not answer cleanly. psp_dispatch_uncertain / payable_dispatch_unknown: the outcome is unknown, the payable is APPROVED with payment.state = uncertain and held for reconciliation — do not resubmit. psp_dispatch_failed: the provider refused and nothing moved; the payable is READY. payable_cycle_threw: the cycle failed before any provider debit; nothing moved, the payable is READY, retry. |
503 | object | vault_unavailable: the key that protects supplier documents could not be read. policy_engine_error: the policy engine could not evaluate; refused, nothing dispatched. payable_sandbox_fixture_unavailable: test project, the sandbox settlement consult did not answer; nothing dispatched, retry. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
attribution | object | yes | — |
carriers | object | no | — |
created_at | string | yes | ISO 8601 instant the payable was created. |
doc_type | "invoice" | "danfe_nfe" | "boleto" | "unknown" | yes | — |
document_hash | string | no | — |
due_date | object | no | — |
extraction | object | no | — |
invoice_number | object | no | — |
issue_date | object | no | — |
items | array of object | no | — |
payable_id | string | yes | — |
payment | object,null | yes | What paying it did (ent#1587); null until a payment was attempted. |
rail_quote | object | no | — |
receipt | object,null | yes | The sealed Control Record receipt; null when no consumer secret could sign one. |
recipient | object | no | — |
review | object | yes | — |
schema_version | "payable.v1" | yes | — |
serie | object | no | — |
status | "RECEIVED" | "EXTRACTING" | "EXTRACTED" | "VALIDATING" | "VALIDATED" | "NEEDS_REVIEW" | "READY" | "APPROVED" | "EXECUTED" | "REJECTED" | "FAILED_EXTRACTION" | "EXPIRED" | yes | — |
supplier | object | yes | — |
totals | object | yes | — |
updated_at | string | yes | ISO 8601 instant of the last change to the payable. |
validation | object | yes | — |
curl -X POST https://api.codespar.dev/v1/payables/{payableId}/pay \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mandate_id": "mandate_0000000000000000"
}'POST /v1/payables/{payableId}/pay HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"mandate_id": "mandate_0000000000000000"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/{payableId}/pay",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"mandate_id": "mandate_0000000000000000"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/{payableId}/pay", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"mandate_id": "mandate_0000000000000000"
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/{payableId}/pay", {
path: {
payableId: "payable_0000000000000000"
},
body: {
mandate_id: "mandate_0000000000000000"
}
});
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
},
"payable_id": "payable_0000000000000000",
"status": "RECEIVED",
"rail_quote": {
"beneficiary": {
"value": "string",
"from": "trilho"
},
"beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"final_beneficiary": {
"value": "string",
"from": "trilho"
},
"final_beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"amount_minor": {
"value": 1000,
"from": "trilho"
},
"discount_minor": {
"value": 1000,
"from": "trilho"
},
"interest_minor": {
"value": 1000,
"from": "trilho"
},
"fine_minor": {
"value": 1000,
"from": "trilho"
},
"due_date": {
"value": "2026-01-15",
"from": "trilho"
},
"quoted_at": "2026-01-15T12:00:00.000Z"
},
"validation": {
"carrier_check_digits": {
"outcome": "ok",
"detail": "string"
},
"document_check_digits": {
"outcome": "ok",
"detail": "string"
},
"nfe_key_consistency": {
"outcome": "ok",
"detail": "string"
},
"arithmetic": {
"outcome": "ok",
"detail": "string"
},
"duplicate_document": {
"outcome": "ok",
"detail": "string"
},
"amount_matches_rail": {
"outcome": "ok",
"detail": "string"
},
"beneficiary_document_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"due_date_state": {
"outcome": "ok",
"detail": "string"
},
"pix_key_owner_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"supplier_registry": {
"outcome": "ok",
"detail": "string"
},
"bank_details_changed": {
"outcome": "ok",
"detail": "string"
}
},
"review": {
"validation_status": "REJECTED",
"review_by": [
{
"code": "string",
"cause": "check_failed"
}
],
"rejected_by": [
"string"
],
"confirmed_by": [
"string"
],
"awaiting": [
"string"
],
"not_covered": [
"string"
],
"review_triggers": [
"string"
],
"resolutions": [
{
"code": "string",
"resolution": "human_correction",
"at": "string",
"by": "string",
"by_source": "verified_user_token",
"fields": [
"string"
],
"confirmed_beneficiary_document": "string"
}
]
},
"payment": {
"state": "awaiting_approval",
"approval_id": "approval_0000000000000000",
"settled_against": "document_line",
"document_amount_minor": 1000,
"original_document_settled": false,
"settlement_note": "string",
"mandate_id": "mandate_0000000000000000",
"attempt_id": "attempt_0000000000000000",
"tries": 0,
"amount_minor": 1000,
"quote_hash": "string",
"claimed_at": "string",
"key_id": "key_0000000000000000",
"via": "string",
"executed_at": "string",
"receipt_id": "receipt_0000000000000000",
"transaction_id": "transaction_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"money_moved": true,
"uncertain_code": "string",
"hold_entry_id": "holdentry_0000000000000000",
"last_refusal": {
"code": "string",
"attempt_id": "attempt_0000000000000000",
"at": "string"
}
},
"created_at": "string",
"updated_at": "string",
"receipt": {
"id": "obj_0000000000000000",
"state": "paid",
"chain": "string",
"exceptions": [
{
"code": "string",
"detail": "string",
"at": "string"
}
]
}
}POST /v1/payables/{payableId}/review
https://api.codespar.dev/v1/payables/{payableId}/reviewComplete the review of a payable
A person records what they decided about a payable that is NEEDS_REVIEW, and the payable comes out READY or REJECTED. There is no third outcome and no partial review: a call that leaves anything blocking is refused and writes nothing, so resolve every item of review in one call.
Who may review. A verified human. Forward the reviewer's Clerk session token in x-codespar-user-token on a service-auth request; that user must be an admin of the organization. Any other credential, a project API key or an OAuth token, is refused (bearer_token_cannot_decide) even with a user token: a key or token proves which service is calling, never which person confirmed, and the audit event names the person.
The ordinary review is an empty one. resolutions: [] releases a payable that no check failed and that only a review trigger holds — typically new_supplier, a supplier this project has never paid. The person is still recorded as the author.
One resolution per failed check. Each names a code that is currently needs_review and one of the resolutions that code admits: the two rejecting codes (carrier_check_digits, duplicate_document) admit none, the three payee codes (beneficiary_document_matches_supplier, pix_key_owner_matches_supplier, bank_details_changed) admit reject or confirm_rail_beneficiary, and every other code admits human_correction or reject. A refused resolution lists what its code admits in details.allowed. A check that produced no answer (indisponivel) has no finding to resolve and is refused (payable_check_did_not_run) — a check that never ran is not a check that passed.
human_correctioncarriescorrections, each naming a field and its corrected value. The value is stored with provenancepessoa; evidence and confidence of the previous value are dropped. The correction is then RE-CHECKED: the payable is validated again, the rail is consulted again, and the resolution holds only if the check now passes. It is refused otherwise (payable_correction_did_not_resolve).confirm_rail_beneficiaryis the only resolution for a payee that differs from the rail's, because editing the extracted field would turn a forged beneficiary into corrected data. It carriesconfirmed_beneficiary_document: the rail's beneficiary, masked. If it is absent or is not who the rail names now, the call is refused withpayable_rail_beneficiary_unconfirmedanddetails.rail_beneficiary_document_maskedtells you who that is — send it back verbatim to confirm.rejectends the payable, and is sent alone. It is a decision about the payable, not the resolution of a check, so it is accepted whatever state the checks are in — a check that produced no answer included; the code it names only has to be one that admitsreject.
What a correction may touch. issue_date, due_date, recipient.document, recipient.name and the totals.* fields, and only when the payable already carries them. Everything that is a duplicity axis (document_hash, the carriers, supplier.document, invoice_number, serie) is outside, because it is a canonical column the duplicate check runs on, and supplier.* is outside for the payee reason above. A misread carrier or supplier document is re-entered as a new payable, not patched.
Scope: payables:write.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
payableId | string | yes | pbl_ followed by 16 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
resolutions | array of object | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | The body did not match the review schema. |
403 | object | The reviewer was not established. bearer_token_cannot_decide: the credential is not service auth (a project API key or an OAuth token), which cannot review. user_token_required: no token was forwarded. user_token_invalid: the token did not verify — mint a fresh one. user_token_identity_mismatch: x-codespar-user names somebody else. insufficient_role: that user is not an admin of the organization. |
404 | object | No payable with this id in this project. |
409 | object | Nothing was written. payable_not_reviewable: the payable is not NEEDS_REVIEW. payable_correction_did_not_resolve: the corrections were applied and details.code still does not pass. payable_rail_beneficiary_unconfirmed: the confirmation named nobody, or not who the rail names now; details.rail_beneficiary_document_masked names who. payable_review_incomplete: something still blocks the payable; details.review says what. payable_review_conflict: the payable changed while this review was being decided. |
422 | object | The review itself is malformed. payable_check_not_in_review: that check did not fail. payable_check_did_not_run: that check produced no answer. payable_resolution_not_allowed: that check does not admit that resolution; details.allowed lists what it does. payable_resolution_repeated: one code, one resolution. payable_resolution_conflict: a rejection resolves nothing else. payable_field_not_correctable: that field is not corrected in a review, or the payable does not carry it. payable_correction_invalid: the corrected payable no longer matches the schema. |
503 | object | vault_unavailable: the key that protects supplier documents could not be read. user_token_verification_unconfigured: this deployment cannot verify a reviewer's token. Nothing was written either way. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
attribution | object | yes | — |
carriers | object | no | — |
created_at | string | yes | ISO 8601 instant the payable was created. |
doc_type | "invoice" | "danfe_nfe" | "boleto" | "unknown" | yes | — |
document_hash | string | no | — |
due_date | object | no | — |
extraction | object | no | — |
invoice_number | object | no | — |
issue_date | object | no | — |
items | array of object | no | — |
payable_id | string | yes | — |
payment | object,null | yes | What paying it did (ent#1587); null until a payment was attempted. |
rail_quote | object | no | — |
recipient | object | no | — |
review | object | yes | — |
schema_version | "payable.v1" | yes | — |
serie | object | no | — |
status | "RECEIVED" | "EXTRACTING" | "EXTRACTED" | "VALIDATING" | "VALIDATED" | "NEEDS_REVIEW" | "READY" | "APPROVED" | "EXECUTED" | "REJECTED" | "FAILED_EXTRACTION" | "EXPIRED" | yes | — |
supplier | object | yes | — |
totals | object | yes | — |
updated_at | string | yes | ISO 8601 instant of the last change to the payable. |
validation | object | yes | — |
curl -X POST https://api.codespar.dev/v1/payables/{payableId}/review \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resolutions": []
}'POST /v1/payables/{payableId}/review HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"resolutions": []
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/payables/{payableId}/review",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"resolutions": []
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/payables/{payableId}/review", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"resolutions": []
}),
});
const data = await res.json();const r = await cs.api.response("post", "/v1/payables/{payableId}/review", {
path: {
payableId: "payable_0000000000000000"
},
body: {
resolutions: []
}
});
// r.status is one of the documented statuses (200, 403, 422),
// each with its own body shape in r.data; nothing here throws on 403.
if (r.ok) {
console.log(r.data);
}{
"schema_version": "payable.v1",
"doc_type": "invoice",
"attribution": {
"channel": "upload",
"credential_id": "credential_0000000000000000",
"connection_id": "conn_0000000000000000",
"actor": "string"
},
"document_hash": "string",
"supplier": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"recipient": {
"document": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"name": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"invoice_number": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"serie": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"issue_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"due_date": {
"value": "2026-01-15",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"items": [
{
"description": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"quantity": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"unit_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"total_amount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
}
],
"totals": {
"subtotal_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"discount_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"tax_relief_deducted_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"freight_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"insurance_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"other_charges_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_added_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"taxes_included_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"grand_total_minor": {
"value": 1000,
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
},
"currency": {
"value": "string",
"from": "parser_local",
"evidence": {
"page": 0,
"excerpt": "string",
"verification": "verified"
},
"confidence": 0
}
},
"carriers": {
"boleto": "string",
"br_code": "string",
"nfe": "string"
},
"extraction": {
"model": "string",
"model_version": "string",
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": 0
},
"payable_id": "payable_0000000000000000",
"status": "RECEIVED",
"rail_quote": {
"beneficiary": {
"value": "string",
"from": "trilho"
},
"beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"final_beneficiary": {
"value": "string",
"from": "trilho"
},
"final_beneficiary_document_masked": {
"value": "string",
"from": "trilho"
},
"amount_minor": {
"value": 1000,
"from": "trilho"
},
"discount_minor": {
"value": 1000,
"from": "trilho"
},
"interest_minor": {
"value": 1000,
"from": "trilho"
},
"fine_minor": {
"value": 1000,
"from": "trilho"
},
"due_date": {
"value": "2026-01-15",
"from": "trilho"
},
"quoted_at": "2026-01-15T12:00:00.000Z"
},
"validation": {
"carrier_check_digits": {
"outcome": "ok",
"detail": "string"
},
"document_check_digits": {
"outcome": "ok",
"detail": "string"
},
"nfe_key_consistency": {
"outcome": "ok",
"detail": "string"
},
"arithmetic": {
"outcome": "ok",
"detail": "string"
},
"duplicate_document": {
"outcome": "ok",
"detail": "string"
},
"amount_matches_rail": {
"outcome": "ok",
"detail": "string"
},
"beneficiary_document_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"due_date_state": {
"outcome": "ok",
"detail": "string"
},
"pix_key_owner_matches_supplier": {
"outcome": "ok",
"detail": "string"
},
"supplier_registry": {
"outcome": "ok",
"detail": "string"
},
"bank_details_changed": {
"outcome": "ok",
"detail": "string"
}
},
"review": {
"validation_status": "REJECTED",
"review_by": [
{
"code": "string",
"cause": "check_failed"
}
],
"rejected_by": [
"string"
],
"confirmed_by": [
"string"
],
"awaiting": [
"string"
],
"not_covered": [
"string"
],
"review_triggers": [
"string"
],
"resolutions": [
{
"code": "string",
"resolution": "human_correction",
"at": "string",
"by": "string",
"by_source": "verified_user_token",
"fields": [
"string"
],
"confirmed_beneficiary_document": "string"
}
]
},
"payment": {
"state": "awaiting_approval",
"approval_id": "approval_0000000000000000",
"settled_against": "document_line",
"document_amount_minor": 1000,
"original_document_settled": false,
"settlement_note": "string",
"mandate_id": "mandate_0000000000000000",
"attempt_id": "attempt_0000000000000000",
"tries": 0,
"amount_minor": 1000,
"quote_hash": "string",
"claimed_at": "string",
"key_id": "key_0000000000000000",
"via": "string",
"executed_at": "string",
"receipt_id": "receipt_0000000000000000",
"transaction_id": "transaction_0000000000000000",
"end_to_end_id": "endtoend_0000000000000000",
"money_moved": true,
"uncertain_code": "string",
"hold_entry_id": "holdentry_0000000000000000",
"last_refusal": {
"code": "string",
"attempt_id": "attempt_0000000000000000",
"at": "string"
}
},
"created_at": "string",
"updated_at": "string"
}Account
12 operations under /v1/account (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Consumer Payments
2 operations under /v1/consumer-payments (POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.