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
| Property | Value |
|---|---|
| Capability | bill.charge |
| Risk class | Money - autonomy L3 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | Connector - an outbound edge integration (Xero / DocuSign / Onfido / M365) |
| MFA step-up | Required - a fresh MFA challenge before the handler runs |
| High-risk oversight | Human four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14) |
| Legal basis | CC-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
| Property | Value |
|---|---|
| Capability | bill.connection |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.invoice |
| Risk class | Money - autonomy L3 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - 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
| Property | Value |
|---|---|
| Capability | bill.config |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.invoice |
| Risk class | Money - autonomy L3 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - 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
| Property | Value |
|---|---|
| Capability | bill.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - 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
| Property | Value |
|---|---|
| Capability | bill.invoice |
| Risk class | Money - autonomy L3 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.charge |
| Risk class | Money - autonomy L3 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
| MFA step-up | Required - a fresh MFA challenge before the handler runs |
| High-risk oversight | Human four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14) |
| Legal basis | CC-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
| Property | Value |
|---|---|
| Capability | bill.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | Connector - an outbound edge integration (Xero / DocuSign / Onfido / M365) |
| High-risk oversight | Human four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14) |
| Budget-gated | Refused when the workspace AI budget is exceeded (Owner break-glass audited) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
| Legal basis | CC-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
| Property | Value |
|---|---|
| Capability | bill.refund |
| Risk class | Money - autonomy L3 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
| MFA step-up | Required - a fresh MFA challenge before the handler runs |
| High-risk oversight | Human four-eyes approval in HIGH-risk orgs (EU AI Act Art. 14) |
| Legal basis | CC-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
| Property | Value |
|---|---|
| Capability | matter.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - 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
| Field | Type | Required | Description |
|---|---|---|---|
matterId | string | yes | |
feeMinor | integer | yes | the fee the remission is computed from, minor-units |
currency | string | no | |
sourceInvoiceId | string | no | |
sourcePaymentId | string | no |
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
| Property | Value |
|---|---|
| Capability | matter.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Member |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
A matter’s accrued partner remittances + the accrued total.
Input
| Field | Type | Required | Description |
|---|---|---|---|
matterId | string | yes |
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
| Property | Value |
|---|---|
| Capability | matter.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - 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
| Field | Type | Required | Description |
|---|---|---|---|
matterId | string | yes | |
partnerPartyId | string | yes | the introducer party id |
mode | string | yes | |
bps | integer | no | basis points of the fee |
amountMinor | integer | no | OR a fixed amount, minor-units |
currency | string | no |
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
| Property | Value |
|---|---|
| Capability | bill.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | On demand (MCP tier 2) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | On demand (MCP tier 2) |
| Idempotent | Yes |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.sync |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | Connector - an outbound edge integration (Xero / DocuSign / Onfido / M365) |
| High-risk oversight | Human 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
| Property | Value |
|---|---|
| Capability | bill.config |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.config |
| Risk class | Destructive - autonomy L3 |
| Min. permission | Owner |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - 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
| Property | Value |
|---|---|
| Capability | bill.config |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - 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
| Property | Value |
|---|---|
| Capability | bill.config |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | Vertical (MCP tier 3) |
| Idempotent | Yes |
| Egress | None - 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.