Compliance

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.

post/v1/invoice/check/batch

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

X-API-Keystring

Request body

signboolean nullable

return results[i].signed_record — the single route's Ed25519 envelope (+ compact jws) over each checked document's report, so one document is verifiable without the batch (Growth/Reseller keys only; otherwise signed_record: null and a note)

bundleboolean nullable

return bundle: an RFC 6962 §2.1 Merkle tree over the checked reports (leaf = SHA-256(0x00 ‖ canonical JSON of {"invoice_check": report})), one §2.1.1 audit path per checked document and a signed record over the root (Growth/Reseller keys only; otherwise bundle: null and a note)

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.

unknown required

Changes

No recorded changes to this operation across all 1 revision of this API.