Overview
Staple Invoice exposes two programmatic surfaces. The REST API provides full read and write access to bills (AP) and invoices (AR) over HTTP — create, update, soft-delete, restore, integrity verification, and Peppol UBL export — plus drafts (AR, pre-send), which support create, update, and delete but not restore. The MCP server lets any MCP-compatible AI agent — Claude Code, Claude Desktop, claude.ai — read and write invoice data directly from your AI workflow.
Both surfaces share the same authentication mechanism: personal access tokens minted in Settings → Developers. One token works for both.
https://invoice.staple.jpAuthentication
Create a personal access token in the Staple Invoice app under Settings → Developers. The token is shown once — copy it immediately. It is stored only as a SHA-256 hash and is bound to your (orgId, userId)pair, so it inherits your organisation’s tenancy boundaries automatically.
Pass the token as a standard Bearer credential:
Authorization: Bearer <your-token>
Example
curl https://invoice.staple.jp/api/bills \ -H "Authorization: Bearer $TOKEN"
Query-parameter fallback
The MCP server URL also accepts ?token=<token>. Clients like Claude Desktop and claude.ai only take a Name and URL with no header field, so this is the only way to attach a static token there. Prefer the Authorization header wherever the client supports it.
https://invoice.staple.jp/api/mcp?token=<your-token>
List bills
/api/billsBearer token or session cookieReturns all AP bills for your organisation, newest first. Soft-deleted bills are excluded.
curl https://invoice.staple.jp/api/bills \ -H "Authorization: Bearer $TOKEN"
Create bill
/api/billsBearer token or session cookieCreate a new AP bill. The route runs vendor linking (find-or-create the canonical vendor row), AI category suggestion, duplicate detection (same vendor + number, or same vendor + amount within 30 days), and JPY-equivalent FX estimation for non-JPY bills.
For OCR-promoted bills the client posts with a triage id (id starts with "ocr-"); the route generates a stable inv-<ulid> id and the call is idempotent on reload. For direct creates, supply a final id and the call is idempotent on that id.
curl -X POST https://invoice.staple.jp/api/bills \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "inv-01jxx...",
"vendor": "Acme Cloud KK",
"amount": 48000,
"currency": "JPY",
"received": "2026-06-01",
"recognizedDate": "2026-05-31"
}'Returns the created bill with status 201. Returns 200 with the existing row if the id was already committed (idempotent).
Get bill
/api/bills/{id}Bearer token or session cookieFetch a single bill by ID. Returns 404 if not found or in a different organisation.
curl https://invoice.staple.jp/api/bills/inv-01jxx... \ -H "Authorization: Bearer $TOKEN"
Update bill
/api/bills/{id}Bearer token or session cookiePatch one or more fields. Only supplied fields are changed. A status change fires a status-changed event in the audit log; each other field change fires a field-edited event (訂正削除履歴 for 電子帳簿保存法). Changing currency or amount automatically re-estimates jpyAmount unless the caller also supplies jpyAmount or jpyConfirmed directly.
vendorIdre-points the bill at a vendor already on file — use it to correct a bill auto-linked to the wrong row, or to a near-duplicate created at import. The bill’s displayed vendor name follows the link, and it adopts that vendor’s registration number when it has one (pass vendor or taxRegIdin the same call to override either). An id that isn’t a vendor in your organisation returns 400 (unknown_vendor), and a customer record returns 400 (vendor_is_customer) — bills link to vendors you pay.
recognizedDate (計上日) is the accounting period the bill belongs to, as YYYY-MM-DD. Leave it unset when it matches received (the issue date); set it when they differ — a bill issued 2026-09-02 for August work is recognized 2026-08-31 and counts toward August spend. It drives every month-bucketed figure (spend reporting, period filters, AI summaries); received stays the 取引年月日 behind the 電子帳簿保存法 index and the retention deadline. Send null to clear it.
Line items and tax
A bill’s tax rate is read from its line items — each line’s taxRate — not from a bill-level field. To correct it, send lines. It replaces every line item, so send the complete list, not only the line you are changing. The bill total stays amount; lines don’t change it. Over REST each line is the full object below. The MCP bill_update tool accepts a shorter form and fills the rest — see MCP tools.
| Field | Type | Notes |
|---|---|---|
name | string | Line description. |
qty | number | Quantity. |
rate | number | Unit price. |
amount | number | Line total, pre-tax, in the bill’s currency. |
taxRate | number, optional | Consumption-tax rate as a percent: 10, 8 or 0. When no line has one, the app assumes 0% for a non-JPY bill and 10% for a JPY one. |
sku, unit, cat | string | Required; send "" if unused. |
catDot | string | Required display colour; send "bg-blue", the app’s default. |
To restore a soft-deleted bill, send { "restore": true }.
# Approve a bill for payment
curl -X PATCH https://invoice.staple.jp/api/bills/inv-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "status": "ready-for-pay" }'
# Correct a bill's tax to 10% (replaces all line items)
curl -X PATCH https://invoice.staple.jp/api/bills/inv-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"lines": [{
"name": "Monthly subscription", "qty": 1, "rate": 15, "amount": 15,
"taxRate": 10, "sku": "", "unit": "", "cat": "", "catDot": "bg-blue"
}]
}'
# Restore a deleted bill
curl -X PATCH https://invoice.staple.jp/api/bills/inv-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "restore": true }'Delete bill
/api/bills/{id}Bearer token or session cookieSoft-delete — the row is hidden from list/get but retained for 電子帳簿保存法 history, and can be un-deleted via PATCH { "restore": true }. An optional { "reason": "..." } body is recorded on the deleted audit event.
curl -X DELETE https://invoice.staple.jp/api/bills/inv-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reason": "duplicate upload" }'Returns 204 No Content. Returns 404 if not found or already deleted.
Bill activity log
/api/bills/{id}/activityBearer token or session cookieFull 訂正削除履歴 audit trail for a bill — imports, status changes, field edits, and deletes/restores — oldest first.
curl https://invoice.staple.jp/api/bills/inv-01jxx.../activity \ -H "Authorization: Bearer $TOKEN"
[
{
"id": "evt_...",
"invoiceId": "inv-01jxx...",
"userId": "usr_...",
"userName": "Dev User",
"userEmail": "dev@staple.local",
"type": "status-changed",
"fromStatus": "ai-review",
"toStatus": "auto-approved",
"data": null,
"createdAt": "2026-06-04T02:11:00.000Z"
}
]Verify bill integrity
/api/bills/{id}/verifyBearer token or session cookie電子帳簿保存法 真実性 check. Re-fetches the preserved source file from Blob storage, recomputes its SHA-256, and compares it to the hash captured at ingest. A mismatch means the stored bytes were altered after the fact. The check itself is logged immutably in the audit trail.
curl -X POST https://invoice.staple.jp/api/bills/inv-01jxx.../verify \ -H "Authorization: Bearer $TOKEN"
{
"ok": true,
"expected": "e3b0c44298fc1c14...",
"actual": "e3b0c44298fc1c14...",
"match": true
}List invoices
/api/invoicesBearer token or session cookieReturns all AR invoices for your organisation, newest first. Soft-deleted invoices are excluded.
curl https://invoice.staple.jp/api/invoices \ -H "Authorization: Bearer $TOKEN"
Create invoice
/api/invoicesBearer token or session cookieCreate a new AR invoice. The route runs customer vendor linking (find-or-create a kind="payer" vendor), payment-terms inference from the issue → due gap, and duplicate detection (same customer + number, or same customer + amount within 30 days). Creation fires a created event in the immutable audit log.
curl -X POST https://invoice.staple.jp/api/invoices \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"number": "INV-2026-001",
"customer": "Acme Corp",
"amount": 100000,
"currency": "JPY",
"sentIso": "2026-06-01"
}'Returns the created invoice with status 201.
Get invoice
/api/invoices/{id}Bearer token or session cookieFetch a single invoice by ID. Returns 404 if not found or in a different organisation.
curl https://invoice.staple.jp/api/invoices/inv-01jxx... \ -H "Authorization: Bearer $TOKEN"
Update invoice
/api/invoices/{id}Bearer token or session cookiePatch one or more fields. A status change fires a status-changed event; each other field change fires a field-edited event in the immutable audit log. Pass { "restore": true } to un-delete a soft-deleted invoice.
# Mark an invoice paid
curl -X PATCH https://invoice.staple.jp/api/invoices/inv-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "status": "paid" }'Delete invoice
/api/invoices/{id}Bearer token or session cookieSoft-delete — the row is hidden from list/get but retained for 電子帳簿保存法 history, and can be un-deleted via PATCH { "restore": true }. An optional { "reason": "..." } body is recorded on the deleted audit event.
curl -X DELETE https://invoice.staple.jp/api/invoices/inv-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reason": "sent to wrong customer" }'Returns 204 No Content. Returns 404 if not found or already deleted.
Peppol UBL export
/api/invoices/{id}/peppolBearer token or session cookieGenerate a JP PINT-compliant UBL 2.1 XML document for the invoice. The generated XML is persisted to Blob storage as the authoritative transmitted artifact, its SHA-256 upgrades the stored integrity fingerprint, and the export is logged immutably.
Returns application/xml as a file download.
| Status | Meaning |
|---|---|
| 200 | UBL XML returned as attachment. |
| 400 | invalid_invoice — generated XML failed structural validation. Response includes issues array. |
| 422 | not_exportable — invoice lacks captured line items (e.g. seeded demo records). |
curl https://invoice.staple.jp/api/invoices/inv-01jxx.../peppol \ -H "Authorization: Bearer $TOKEN" \ -o invoice.xml
Verify invoice integrity
/api/invoices/{id}/verifyBearer token or session cookie電子帳簿保存法 真実性 check for issued invoices. If a transmitted artifact (UBL/PDF) was persisted via the Peppol export, re-fetches and re-hashes it; otherwise recomputes the canonical snapshot hash. Compares to the stored fingerprint and logs the run immutably.
curl -X POST https://invoice.staple.jp/api/invoices/inv-01jxx.../verify \ -H "Authorization: Bearer $TOKEN"
{
"ok": true,
"expected": "e3b0c44298fc1c14...",
"actual": "e3b0c44298fc1c14...",
"match": true
}List drafts
/api/draftsBearer token or session cookieReturns drafts (invoice, estimate, receipt, or purchase order — not yet sent), newest first. On a Solo-plan org this is only the caller’s own drafts; on a Team-plan org, every draft in the organisation.
curl https://invoice.staple.jp/api/drafts \ -H "Authorization: Bearer $TOKEN"
Create or update a draft
/api/draftsBearer token or session cookieIdempotent upsert keyed on the supplied id — POSTing the same id again updates that draft rather than creating a second one, so this is safe to call repeatedly (e.g. on every autosave tick, or every time an upstream event recurs). id, number, and design ("A" | "B" | "C" | "D" — the invoice template) are required; everything else defaults to empty. docType defaults to "invoice"and is gated by your token’s AR/AP scope (drafts are an AR concern, so an AP-scoped token is rejected).
subject (件名, a short descriptor printed above the line items) and memo(free-form, printed below totals) are both optional and independent of each other and of the line items’ own description fields.
adjustments (invoices only) are settlement lines printed after the tax total, such as withholding tax (源泉徴収) or an advance payment already received. Each is { id, type, amount, label?, ref? } with a signed yen amount: negative reduces the amount payable, positive adds to it. type is withholding_tax, advance_payment or offset (all three must be negative), or deposit / other (either sign). They never change the subtotal, consumption tax or invoice total, so discounts belong in the line items instead. Send the whole list; an empty array clears it.
curl -X POST https://invoice.staple.jp/api/drafts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "draft-01jxx...",
"number": "INV-2026-042",
"design": "A",
"customer": "Acme Corp",
"issueIso": "2026-06-01",
"dueIso": "2026-07-01",
"subject": "June consulting retainer",
"lines": [
{ "id": "1", "description": "Consulting", "qty": 1, "rate": 100000, "taxPct": 10 }
],
"memo": "Auto-drafted from a captured payment",
"adjustments": [
{ "id": "wh", "type": "withholding_tax", "amount": -10210 }
]
}'Returns the created/updated draft. Returns 410 (draft_deleted) if that id was already soft-deleted — ids don’t get resurrected, mint a new one.
Get draft
/api/drafts/{id}Bearer token or session cookieFetch a single draft by ID. Returns 404 if not found (or, on a Solo-plan org, owned by someone else).
curl https://invoice.staple.jp/api/drafts/draft-01jxx... \ -H "Authorization: Bearer $TOKEN"
Update draft
/api/drafts/{id}Bearer token or session cookiePatch one or more fields. Only supplied fields change.
curl -X PATCH https://invoice.staple.jp/api/drafts/draft-01jxx... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "memo": "Confirmed against payment reference pay_..." }'Delete draft
/api/drafts/{id}Bearer token or session cookieSoft-delete. The id is retired — a later upsert with the same id returns 410 instead of resurrecting it.
curl -X DELETE https://invoice.staple.jp/api/drafts/draft-01jxx... \ -H "Authorization: Bearer $TOKEN"
Returns 204 No Content. Returns 404 if not found.
Next draft number
/api/drafts/next-numberBearer token or session cookieGet the next suggested document number for a new draft, following your org’s numbering pattern. Optional ?docType= query param (invoice, estimate, receipt, or purchase_order; defaults to invoice).
curl "https://invoice.staple.jp/api/drafts/next-number?docType=invoice" \ -H "Authorization: Bearer $TOKEN"
{ "number": "INV-2026-043" }Forward a bill by email
Every organisation has a dedicated inbound address — forward or BCC a vendor bill to it and it lands in your Inbox, no API call or login required. Find your address on the Inbox screen under “Forward bills by email.”
<org-id>@bill.staple.jp (lowercased)Each PDF, JPEG, or PNG attachment — plus Peppol / JP PINT UBL XML — runs through the same OCR pipeline as a browser upload and lands as a real bill with status: "ai-review". Nothing is auto-approved: every email-ingested bill still needs a human to confirm it, exactly like a dragged-in file. An email with multiple attachments creates one bill per attachment.
Duplicate protection
Byte-identical re-deliveries (a webhook retry, an accidental double-forward) are skipped automatically via a content-hash check, before OCR ever runs. Bills sharing a vendor and document number are flagged as a possible duplicate for review, same as any other ingestion path.
Limits
| Constraint | Detail |
|---|---|
| File size | 4.5MB per attachment. Larger images are compressed automatically before OCR; oversized PDFs are skipped. |
| File types | PDF, JPEG, PNG, and Peppol/JP PINT UBL XML. Other attachment types are silently skipped, not a hard failure for the whole email. |
| Sender | Not restricted to a pre-registered address — a bill’s real sender is the vendor’s own billing system. A sender domain that doesn’t match the resolved vendor’s known domain gets an informational flag in review, not a block. |
Errors
All error responses return JSON with an error field. HTTP status codes follow standard conventions.
| Status | Error | Cause |
|---|---|---|
| 401 | unauthorized:no-session · unauthorized:no-org · unauthorized:invalid-token | Missing, invalid, or revoked Bearer token — or no session cookie. |
| 400 | invalid_body | Request body failed schema validation. The response includes an issues field with the full error detail. |
| 403 | unauthorized:scope-forbidden | Your token’s AR/AP scope doesn’t cover this resource (e.g. an AP-scoped token calling an invoices or drafts endpoint). |
| 404 | not_found | Document does not exist or belongs to a different organisation. |
| 410 | draft_deleted | Draft upsert: that id was already soft-deleted and can’t be resurrected — mint a new id. |
| 400 | invalid_invoice | Peppol export: generated UBL failed structural validation. Response includes an issues array. |
| 422 | not_exportable | Peppol export: invoice cannot be exported (missing line items). |
MCP server
The Staple Invoice MCP server runs in-process as a stateless Streamable-HTTP server. Connect any MCP-compatible AI client to read and write invoice data from within your AI workflow.
https://invoice.staple.jp/api/mcpConnect from Claude Code
claude mcp add staple-invoice --transport http \ https://invoice.staple.jp/api/mcp \ --header "Authorization: Bearer $TOKEN"
Connect from Claude Desktop or claude.ai
These clients only accept a Name and URL — no custom header field. Pass the token as a query parameter instead:
Name: Staple Invoice URL: https://invoice.staple.jp/api/mcp?token=<your-token>
Test with MCP Inspector
npx @modelcontextprotocol/inspector # → Transport: Streamable HTTP # → URL: http://localhost:3000/api/mcp # → Headers: Authorization: Bearer <token>
Available tools
19 tools across reads and safe writes. No deletes exposed via MCP, no email sending.
Read tools
| Tool | Description |
|---|---|
bill_list | List received (AP) bills, newest first. Optionally filtered by status. |
bill_get | Fetch a single AP bill by ID, including all extracted fields and line items. |
bill_activity | Get the full audit-trail activity log for a received bill. |
invoice_list | List all sent (AR) invoices for the org, newest first. |
invoice_get | Fetch a single invoice by ID. |
invoice_peppol | Get the Peppol / JP PINT UBL XML export of an invoice. |
vendor_search | Search or list vendors (payee and payer counterparties) in the organisation's directory. |
vendor_get | Fetch a single vendor by ID. |
vendor_rollup | Aggregate AP totals and bill counts for a vendor. |
space_list | List the org's spaces — cost centers / departments used to allocate invoices. |
draft_list | List AR invoice drafts. |
draft_get | Fetch a single draft by ID. |
draft_next_number | Get the next suggested invoice number for a new draft. |
report_summary | Financial summary — AP/AR counts and totals by status, plus outstanding and paid amounts on each side. |
Write tools
| Tool | Description |
|---|---|
bill_create | Create a new AP bill record with vendor linking and AI categorisation. |
bill_update | Patch a received bill — status, amount, currency, category, notes, due date, or line items. To correct a bill's tax, pass lines (each with name, amount and taxRate): the app shows a bill's tax rate from its line items. Every changed field is recorded in the bill's audit log. |
vendor_create | Create a new vendor in the payee directory. |
vendor_update | Patch fields on an existing vendor. Only supplied fields change. |
draft_upsert | Create or update an AR invoice draft. |
Correcting a bill’s tax
A bill’s tax rate is read from its line items, so pass lines to bill_update. Each line needs name, amount and taxRate (0–100); qty defaults to 1 and rate to amount ÷ qty, and the tool fills the internal fields itself. linesreplaces every line item and works on paid bills too — each correction is recorded in the bill’s audit log.
{
"id": "inv-01jxx...",
"lines": [{ "name": "Monthly subscription", "amount": 15, "taxRate": 10 }]
}