opbox

The bill.* family (26 verbs). Every verb enters through the one front door: it is capability-checked, permission-checked, scope-checked and audited before it runs (see Security & permissions). Each entry below lists its capability, risk class (which fixes the minimum autonomy level), the minimum caller permission, its availability tier, whether it is idempotent, and any outbound egress.

Chargebill.charge

PropertyValue
Capabilitybill.charge
Risk classMoney - autonomy L3
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressConnector - an outbound edge integration (Xero / DocuSign / Onfido / M365)
MFA step-upRequired - a fresh MFA challenge before the handler runs
High-risk oversightHuman four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14)
Legal basisCC-OPBOX 19

, OUTBOUND egress (CONNECTOR rail). Mint a hosted pay-now link via a configured PaymentRail (US-BILL-03). Resolves the rail from connectionId, calls the rail protocol’s create_payment_request (a stable pr_<invoiceId>_<attempt> idempotency key), and persists a PaymentRequest. The egress chokepoint (dispatch) has ALREADY allow-listed the rail’s endpoint by the time this runs (fail-closed deny otherwise). Adding a rail is config (a registry lookup), never substrate code.

Call POST /v/bill.charge with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.charge.

Createbill.connection.create

PropertyValue
Capabilitybill.connection
Risk classSensitive - autonomy L2
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

Register a rail/ledger connection with @encrypted credentials (INV-7). Reuse-shaped on the (unbuilt) connection.* family - billing CONSUMES it. The credentials are encrypted before write; the secret is NEVER returned by any read.

Call POST /v/bill.connection.create with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.connection.create.

Creditnotebill.creditnote

PropertyValue
Capabilitybill.invoice
Risk classMoney - autonomy L3
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

Issue a credit note (standalone or invoice-linked), numbered by the SAME per-workspace integer sequence scheme as invoices (US-BILL-09). Integer minor-units (C14).

Call POST /v/bill.creditnote with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.creditnote.

Getbill.defaults.get

PropertyValue
Capabilitybill.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

Read the firm billing defaults (base-currency / number-prefix / due-days). {}. RLS-scoped + an explicit workspace_id = $ predicate. Returns the substrate fallbacks (USD / INV- / 30) when no row has been authored yet (so the config panel always renders a value); NEVER returns a secret (this config row carries none - payment-rail creds live @encrypted on bill_connection, never read by a config verb). Computes nothing authoritative (INV-14).

Call POST /v/bill.defaults.get with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.defaults.get.

Setbill.defaults.set

PropertyValue
Capabilitybill.config
Risk classSensitive - autonomy L2
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Set the firm billing defaults (base-currency drives the FX freeze → a mid-life change is consequential). The KNOWN_PEGS table is substrate, never config.

Call POST /v/bill.defaults.set with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.defaults.set.

Recordbill.event.record

PropertyValue
Capabilitybill.write
Risk classWrite - autonomy L1
Min. permissionMember
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

(DEFERRED). Append a billing DOMAIN event. org_id == ctx.workspace_id.

Call POST /v/bill.event.record with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.event.record.

Invoicebill.invoice

PropertyValue
Capabilitybill.invoice
Risk classMoney - autonomy L3
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Create a DRAFT invoice from a matter’s DELIVERED line items (US-BILL-01, zero re-keying). Snapshots each line with its provenance frozen; server-computes subtotal/tax/total as integer minor-units (C14). Idempotent double-mint prevention: if the matter already has an un-voided invoice consuming these lines, returns it (the GENERATE→PAYMENT adoption).

Call POST /v/bill.invoice with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.invoice.

Getbill.invoice.get

PropertyValue
Capabilitybill.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

, IDOR-scoped. One invoice: status + frozen line-item snapshot + integer-minor-unit totals + FX snapshot + pdfFileId + the paid/outstanding standing (computed-on- read via the ONE status authority’s aggregation, never a stored standing, INV-6).

Call POST /v/bill.invoice.get with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.invoice.get.

Listbill.invoice.list

PropertyValue
Capabilitybill.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

, paginated, IDOR-scoped. List a workspace’s invoices, filter by matter/status/contact. Standing reuses the one authority’s integer aggregation (no second calc).

Call POST /v/bill.invoice.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.invoice.list.

Transitionbill.invoice.transition

PropertyValue
Capabilitybill.invoice
Risk classMoney - autonomy L3
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

Drive the code-enforced 7-state machine. Validates against ALLOWED_TRANSITIONS; hard-rejects PARTIALLY_PAID/PAID as manual targets (INV-1); freezes the FX snapshot + issue/due dates on the first issue out of DRAFT; enqueues a fire-and-forget PDF render on SUBMITTED/AUTHORISED. VOID is terminal-immutable. { invoiceId, to, fxRate? }.

Call POST /v/bill.invoice.transition with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.invoice.transition.

Createbill.lineitem.create

PropertyValue
Capabilitybill.write
Risk classWrite - autonomy L1
Min. permissionMember
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

(DEFERRED). Fence: the subscription’s org == ctx.workspace_id.

Call POST /v/bill.lineitem.create with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.lineitem.create.

Deletebill.lineitem.delete

PropertyValue
Capabilitybill.write
Risk classWrite - autonomy L1
Min. permissionMember
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

(DEFERRED). Refuse if already billed (billed_at set). Idempotent.

Call POST /v/bill.lineitem.delete with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.lineitem.delete.

Recordbill.payment.record

PropertyValue
Capabilitybill.charge
Risk classMoney - autonomy L3
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)
MFA step-upRequired - a fresh MFA challenge before the handler runs
High-risk oversightHuman four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14)
Legal basisCC-OPBOX 19

Append a Payment (existence ⇒ money landed) and recompute the invoice status via the ONE authority. Handles a rail receipt (source=rail:…, providerEventId) AND a manual/offline entry (source defaults to manual:<actor>:<ts> - US-BILL-06, ADMIN-gated, audited). Idempotent on idempotencyKey (no double-credit).

Call POST /v/bill.payment.record with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.payment.record.

Listbill.rail.invoice.list

PropertyValue
Capabilitybill.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressConnector - an outbound edge integration (Xero / DocuSign / Onfido / M365)
High-risk oversightHuman four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14)
Budget-gatedRefused when the workspace AI budget is exceeded (Owner break-glass audited)
Render schemaYes - drives an inline chat artefact and a GUI panel
Legal basisCC-OPBOX 67

([2026] VJS-CC-OPBOX 67 Plane C) - read invoices THROUGH the tenant payment rail, the symmetric READ of the bill.charge write seam. Resolves the connection with the SAME IDOR-safe, enabled-checked, kind-asserted helper bill.charge uses (resolve_connection(.., "rail", ..)), then reads via the kernel rail protocol (PaymentRail::list_invoices) - the agent never imports the vendor SDK (Steering #4/#5). RiskClass::Read / Authz::Admin / EgressClass::Connector (the egress chokepoint gates the target endpoint against the per-workspace allow-list before this runs) / budget_gated (CC-OPBOX 33). SCOPE-BOUND: a tenant rail bill_connection ONLY; the super-org own-account read (Opbox’s OWN Stripe across orgs) is RESERVED to a fresh CC-OPBOX 16 record.

Call POST /v/bill.rail.invoice.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.rail.invoice.list.

Refundbill.refund

PropertyValue
Capabilitybill.refund
Risk classMoney - autonomy L3
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)
MFA step-upRequired - a fresh MFA challenge before the handler runs
High-risk oversightHuman four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14)
Legal basisCC-OPBOX 19

Issue a refund against a Payment. The over-refund guard re-aggregates (COMPLETED + PENDING refunds) INSIDE the txn after SELECT … FOR UPDATE on the payment row - two concurrent refunds serialise here; the locked re-check is authoritative (no optimism). A manual/offline payment (manual: source) refuses the rail-refund path (must use the manual path). On COMPLETED, recomputes the status (PAID→PARTIALLY_PAID/AUTHORISED roll-back).

Call POST /v/bill.refund with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.refund.

Accruebill.remittance.accrue

PropertyValue
Capabilitymatter.write
Risk classWrite - autonomy L1
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

Accrue a partner_remittance row (the firm-owed obligation; moves no money - the payout is RESERVED, CC-OPBOX 30 D7). { matterId, feeMinor, currency?, sourceInvoiceId?, sourcePaymentId? }. Amount = the config’s fixed amount, else feeMinor * bps / 10000. COMMISSION_PAYABLE accrues on invoice issue, PASS_THROUGH on payment collection; CLIENT_DISCOUNT does NOT accrue (it is a discount line item).

Input

FieldTypeRequiredDescription
matterIdstringyes
feeMinorintegeryesthe fee the remission is computed from, minor-units
currencystringno
sourceInvoiceIdstringno
sourcePaymentIdstringno

Call POST /v/bill.remittance.accrue with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.remittance.accrue.

Listbill.remittance.list

PropertyValue
Capabilitymatter.read
Risk classRead - autonomy L0
Min. permissionMember
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

A matter’s accrued partner remittances + the accrued total.

Input

FieldTypeRequiredDescription
matterIdstringyes

Call POST /v/bill.remittance.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.remittance.list.

Setbill.remittance.set

PropertyValue
Capabilitymatter.write
Risk classWrite - autonomy L1
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Author a matter’s remission config (the matter_billing_profile config-row pattern, CC-OPBOX 30 D4). { matterId, partnerPartyId, mode(COMMISSION_PAYABLE|CLIENT_DISCOUNT| PASS_THROUGH), bps?, amountMinor?, currency? }. Upserts a FIXED_FEE profile carrying the remission config if the matter has none (remission is orthogonal to the billing mode).

Input

FieldTypeRequiredDescription
matterIdstringyes
partnerPartyIdstringyesthe introducer party id
modestringyes
bpsintegernobasis points of the fee
amountMinorintegernoOR a fixed amount, minor-units
currencystringno

Call POST /v/bill.remittance.set with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.remittance.set.

Updatebill.subscription.update

PropertyValue
Capabilitybill.write
Risk classWrite - autonomy L1
Min. permissionMember
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

(DEFERRED). The row MUST exist (BadInput if not). Optional OCC guard on expectedUpdatedAt (Conflict on a concurrent modification). Partial column update.

Call POST /v/bill.subscription.update with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.subscription.update.

Upsertbill.subscription.upsert

PropertyValue
Capabilitybill.write
Risk classWrite - autonomy L1
Min. permissionMember
AvailabilityOn demand (MCP tier 2)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

(DEFERRED). Keyed on org_id (UNIQUE). Only the PROVIDED columns are written (absent optionals keep the existing/default value via COALESCE on the raw bind param).

Call POST /v/bill.subscription.upsert with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.subscription.upsert.

Syncbill.sync

PropertyValue
Capabilitybill.sync
Risk classWrite - autonomy L1
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressConnector - an outbound edge integration (Xero / DocuSign / Onfido / M365)
High-risk oversightHuman four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14)

, OUTBOUND egress (CONNECTOR rail). Push the invoice/ledger to an external ledger (Xero first) FIRE-AND-FORGET (INV-2, US-BILL-08): the LOCAL write is authoritative; a sync failure leaves local data FRESH + external STALE, never local data WRONG. Self-gates on the persisted external_id (idempotent). Owns NO money logic. The egress chokepoint has allow-listed the ledger endpoint by the time this runs (fail-closed otherwise).

Call POST /v/bill.sync with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.sync.

Archivebill.theme.archive

PropertyValue
Capabilitybill.config
Risk classWrite - autonomy L1
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Withdraw a branding theme from the active list, REVERSIBLE (ACTIVE->ARCHIVED over invoice_branding_theme.status, migration 0034). A retired theme stays a row (never a hard delete) so any invoice that pinned it still renders; a future un-archive flips it back. IDOR-scoped (workspace_id = $); a cross-workspace id reads back not-found. Idempotent: an already-ARCHIVED theme is a no-op-shaped success (alreadyArchived: true). { themeId }.

Call POST /v/bill.theme.archive with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.theme.archive.

Deletebill.theme.delete

PropertyValue
Capabilitybill.config
Risk classDestructive - autonomy L3
Min. permissionOwner
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

Hard-delete a branding theme. REFUSES (Conflict) if ANY invoice references it (the invoice.branding_theme_id pointer, migration 0009): a regulated invoice must always render its frozen branding, so a pinned theme is never removed out from under it (archive it instead, bill.theme.archive). IDOR-scoped (workspace_id = $); a cross-workspace id reads back not-found. NOT idempotent (a second delete of the same id is a not-found). { themeId }.

Call POST /v/bill.theme.delete with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.theme.delete.

Listbill.theme.list

PropertyValue
Capabilitybill.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

Inventory the firm’s invoice branding themes. { limit? }. RLS-scoped + an explicit workspace_id = $ predicate (a cross-workspace theme is unreachable). Returns the branding config (name / colours / layout / default flag) - NEVER a secret (this table carries none; the optional logo_file_id is an encrypted-StoredFile REFERENCE, not the bytes). Paginated (NFR-PERF-3). Computes nothing authoritative (INV-14).

Call POST /v/bill.theme.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.theme.list.

Set Defaultbill.theme.setDefault

PropertyValue
Capabilitybill.config
Risk classWrite - autonomy L1
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Flip exactly ONE theme to the firm default (single-default invariant enforced at the verb AND by the partial unique index). { themeId }.

Call POST /v/bill.theme.setDefault with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.theme.setDefault.

Upsertbill.theme.upsert

PropertyValue
Capabilitybill.config
Risk classWrite - autonomy L1
Min. permissionAdmin
AvailabilityVertical (MCP tier 3)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Author/update an invoice branding theme (a first-class config row, NOT a generic Table). { name, primaryColour?, accentColour?, footerText?, termsText?, headerLayout?, logoFileId? }.

Call POST /v/bill.theme.upsert with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool bill.theme.upsert.