post_v1_books_scan_book
BookScan posts a reviewed scanned bill to the ledger.
BookScan posts a reviewed scanned bill to the ledger. It is the scanner's ONLY write: the voucher goes through the same post() choke point every other source uses, so it is checked to balance (Σdebit == Σcredit) and is idempotent by (scan, scanId) — re-booking the same scan answers posted=false and writes nothing. A bill whose economic identity (vendor, total, issue date) already posted under a DIFFERENT scan is refused 409 unless override is set, which is what stops the same receipt re-scanned into a new file hash from double-booking. An unbalanced voucher is refused 400.
| Tool | post_v1_books_scan_book |
| Door | https://api.hanzo.ai/v1/mcp |
| Method | tools/call (JSON-RPC 2.0) |
| Arguments | 3 |
| Operation | POST /v1/books/scan/book |
| Product | books |
Arguments
| Field | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
override | boolean | — | — | — | Override books this bill even when one of the SAME economic identity (vendor, total, issue date) already posted — the explicit human confirmation that a same-looking bill is a genuine second spend, not the same receipt re-scanned. |
scanId | string | — | — | — | ScanID is the scanned document's file hash, as GET /v1/books/inbox and the scan draft report it. It is the idempotency key: re-booking the same scan writes nothing. |
voucher | Voucher | — | — | — | Voucher is the reviewed voucher to post. Its source is FORCED to (scan, scanId) server-side, so it can never be booked under another source's key. |
tools/list declares a type and a description for each field and nothing further. A — means neither the door nor that operation constrains the field.
Object types
Leg and Voucher are objects this tool's fields are made of. Each is declared inside the tool's own schema, and each is enumerated in full below.
Leg
| Field | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
account | string | — | — | — | Account is the chart-of-accounts number this side posts to, e.g. "5300". |
credit | integer | — | — | — | Credit is the leg's credit in exact cents. Set this or Debit, not both. |
debit | integer | — | — | — | Debit is the leg's debit in exact cents. Set this or Credit, not both. |
Voucher
| Field | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
description | string | — | — | — | Description is the human line for the event, e.g. the vendor a bill came from. |
legs | Leg[] | — | — | — | Legs are the sides of the posting. They must balance: Σdebit == Σcredit, give or take the 2¢ round-off allowance. |
postingAt | string | — | — | — | PostingAt is the RFC3339 instant the event posts at — the time every statement window filters on. |
sourceId | string | — | — | — | SourceID is the source event's own id within that namespace. Together with SourceKind it is the key that makes a repeat posting a no-op. |
sourceKind | string | — | — | — | SourceKind is the idempotency namespace naming what booked this, e.g. "scan". |
Call it
A tools/call carries every argument in one flat object — nothing binds to a path or a query string. Nothing above is required, so every declared argument is shown rather than a guess at which matter.
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "post_v1_books_scan_book",
"arguments": {
"override": false,
"scanId": "<scanId>",
"voucher": {
"description": "<description>",
"legs": [
"<legs>"
],
"postingAt": "<postingAt>",
"sourceId": "<sourceId>",
"sourceKind": "<sourceKind>"
}
}
}
}'Values are the operation's own defaults and enumerated values where it declares them, and a <placeholder> where neither source declares one. override holds a stand-in that cannot be spelled that way — JSON gives a number, a boolean and a timestamp no placeholder form — so that value is this page's, not the API's. Neither the door nor the operation declares one. tools/list needs no credential; tools/call does — called without one the door answers HTTP 200 with a JSON-RPC result whose isError is set and whose text says what was missing. How to get a key →
The operation behind it
| Operation | Route | Product | Summary |
|---|---|---|---|
post_v1_books_scan_book | POST /v1/books/scan/book | books | Posts a reviewed scanned bill to the ledger. |
The same capability over plain HTTP is in the books API reference, on https://api.hanzo.ai.
All 755 tools · The door · API reference
Generated from tools/list on https://api.hanzo.ai/v1/mcp — 833 tools captured 2026-08-01, of which 755 are documented here (the operator surface is not published) (this build read the vendored copy; the door was unreachable).
How is this guide?
post_v1_books_rules
UpsertRule creates or updates one auto-categorization rule, keyed by its pattern — writing a pattern that already exists REPLACES that row's category and priority.
post_v1_books_sync
Sync ingests the caller's OWN org from commerce into BOTH ledgers (live and sandbox) and reports how many new vouchers posted to each.