JP-Verify Invoice Facts — many invoices' facts in one call (JSON), optionally under one signed RFC 6962 Merkle root
Check MANY invoices from their facts — {"documents": [<InvoiceCheckRequest without "sign">, …], "sign": false, "bundle": false} — and get one positional entry per document: results[i] is {"index": i, "ok": true, "report": …} with exactly the report POST /v1/invoice/check returns for that document (byte-identical for the same facts — the same rules, sources, limitations and rule_set_version), or {"index": i, "ok": false, "error": …, "invalid": [...]} for a document that fails request-level validation (the single route's free-400 reasons, free here too). summary counts pass / fail / unknown / not_applicable over the checked reports; count / checked / invalid_count say how many were submitted, checked and refused. One register snapshot serves the whole batch (one data_as_of).
NOT tax advice — the same register facts, document checks and published schedule as the single route, per document; no verdict, no deductibility determination, no buyer-side input.
Ceilings, refused FREE before any document is parsed: the body over 4 MiB (413); more documents than the caller's tier batch ceiling — 100 Sandbox/Starter, 500 Growth, 1,000 Reseller — (400 naming the count); documents missing or not a list (400); nothing checkable (400 with the per-document reasons). Metered checked units, all-or-nothing: limiter.precheck refuses an exhausted caller before the work with a 429 that names the shortfall (N documents need N units; M remaining — nothing was charged), and the atomic consume after the work charges nothing when it denies. A stale or unreadable register is a free 503 for the whole batch.
"sign": true (Growth / Reseller): every checked entry carries signed_record — the single route's Ed25519 envelope over {"invoice_check": report} with a compact jws sibling, so one document is verifiable on its own. "bundle": true (Growth / Reseller): bundle is an RFC 6962 §2.1 Merkle tree over the checked reports in results order — leaf = SHA-256(0x00 ‖ canonical JSON of {"invoice_check": report}) (sorted keys, no whitespace, UTF-8), interior nodes SHA-256(0x01 ‖ left ‖ right), the split at the largest power of two below the size — with one §2.1.1 audit path per leaf (proofs[j]: index = leaf position, document_index = the results index, tree_size, path leaf-level sibling first) and a signed record over {"invoice_check_batch": {root, count, rule_set_version, data_as_of}} (also at the top level as signed_record); verify a document with 15 lines of stdlib: leaf = sha256(0x00 ‖ canonical JSON of {"invoice_check": report} — sorted keys, no spaces), node = sha256(0x01 ‖ left ‖ right) (RFC 6962 §2.1), fold the document's path from its leaf up to root, then verify the signed record over {root, count, rule_set_version, data_as_of} against GET /pubkey — the worked recipe is in the field reference INVOICE_CHECK.md §2c, available on request. Other tiers get null and a note for either flag.
Opt-in Idempotency-Key: a retry with the same key + body replays the first response (Idempotent-Replayed: true) instead of being re-metered; this route caches bodies up to 4 MiB (JPVERIFY_IDEMPOTENCY_BATCH_MAX_CACHED_BYTES) and a larger response is served with X-JPVerify-Idempotency: not-cached — a retry would reprocess and re-meter, so keep a batch to about 200 documents when replay protection matters. Names and amounts are consumed in memory only — never stored, logged or echoed; the bundle hashes the reports, not the documents.
Request
- Base URL: https://jp-verify.obolpay.xyz
- URL: https://jp-verify.obolpay.xyz/v1/invoice/check/batch
- Auth: one of:
- none
- API key in header X-API-Key
Headers
Request body
Example request
{
"documents": [
{
"buyer_name": "Example K.K.",
"currency": "JPY",
"description_present": true,
"document_type": "qualified_invoice",
"issue_date": "2026-10-05",
"reduced_rate_marked": true,
"seller_name": "株式会社サンプル",
"sign": false,
"supplier_t_number": "T8200012010001",
"tax_lines": [
{
"amount": 60000,
"amount_basis": "tax_exclusive",
"category": "standard",
"rate_percent": 10,
"tax_amount": 6000
},
{
"amount": 40000,
"amount_basis": "tax_exclusive",
"category": "reduced",
"rate_percent": 8,
"tax_amount": 3200
}
],
"totals": {
"payable_amount": 109200,
"tax_exclusive_total": 100000,
"tax_inclusive_total": 109200,
"tax_total": 9200
},
"transaction_date": "2026-09-28"
}
]
}Response
One positional entry per submitted document: ok: true with the single route's report (byte-identical for the same document), or ok: false with the request-level reason (free). summary counts rule statuses over the checked reports; bundle / signed_record are null unless requested on a Growth/Reseller key. Metered checked units, all-or-nothing. The example is the real response on the sample register (Growth key, sign + bundle) to the request example's first document plus a second document whose issue_date is malformed — refused free as ok: false.
Changes
No recorded changes to this operation across all 1 revision of this API.