Books
The books family (43 methods) is the agent lane of the finance desk: the same domain services the app calls, so floors, holds, and the signature law behave identically whether a human clicks or an agent calls. Scopes: books.read / books.write — and scope alone cannot move money.
The signature law
Six acts complete only on proof of a human decision, no matter who calls or what scope they hold:
| Signature act | What it releases |
| --- | --- |
| payrollSign | A drafted pay run |
| taxSignLodge | A prepared tax lodgement |
| billScheduleRun | A scheduled payment run |
| invoiceChaseStep (step ≥ 2) | Escalating dunning beyond the first nudge |
| onboardingAdvance (contract step) | An employment contract |
| matchHoldResolve (above-floor) | A held bank-feed match above the materiality floor |
Called from the agent lane without proof, these do not error — they return a hold frame and append one books.signature.hold event to the ledger:
{
"ok": true,
"result": {
"status": "approval_required",
"approval": {
"actionClass": "books.payroll_sign",
"payloadHash": "b3a9…",
"subject": { "kind": "books.signature", "subjectId": "run_…", "summary": "Sign the August pay run" },
"expiresInMs": 259200000
},
"subjectId": "run_…",
"message": "Sign the August pay run — held for a human signature. File approval.request with this actionClass, payloadHash and subject; once a human approves it, retry with the approvalId (or the minted signatureToken) and a fresh idempotency key."
}
}
The frame tells you exactly what to file. Request the approval, wait for the human, then retry the same call with approvalId (or the minted signatureToken) and a fresh x-idempotency-key — the full loop is worked through in Governance. The proof is cryptographically bound to exactly this act (org, action class, subject row, payload hash) and is single-use: a second act on the same approval refuses.
Related floor releases behave the same way by refusal: billApprove on a bill held for ABOVE FLOOR or NEW PAYEE (PB-B1) refuses the bare agent lane with GOVERNANCE_NOT_READY until a human signature backs it.
Work the desk
| Area | Methods |
| --- | --- |
| Reconciliation | feedLines, matchAccept, matchHoldResolve, receiptsRegister, lineRecode, journalPost |
| Receivables | invoiceDraft, invoiceSend, invoiceChaseStep, invoiceRecordPayment, invoiceCreditNote, invoiceVoid, invoicePromise, statementRun |
| Payables | billCapture, billApprove, billHold, billReturn, billScheduleRun |
| Expenses | expenseClaim, expenseApprove |
| Payroll and people | payrollDraft, payrollSign, payrollRecall, onboardingStart, onboardingAdvance, leaveRequest, leaveApprove, stpResubmit |
| Tax | taxPrepare, taxHold, taxReturn, taxSignLodge |
| Assets | assetDepreciationRun, assetDispose |
| Reads | accounts, journals, invoices, bills, taxObligations, reports, assets, ask |
A typical receivables flow:
# Draft, then send — each returns { ledgerId, … }
curl -X POST https://os.cohortapp.com/api/v1/books.invoiceDraft \
-H "Authorization: Bearer $COHORT_API_KEY" -H "Content-Type: application/json" \
-H "x-idempotency-key: inv-acme-aug" \
-d '{"params": {"payeeName": "Acme Pty Ltd", "description": "August retainer", "totalCents": 450000, "dueDays": 14}}'
curl -X POST https://os.cohortapp.com/api/v1/books.invoiceSend \
-H "Authorization: Bearer $COHORT_API_KEY" -H "Content-Type: application/json" \
-H "x-idempotency-key: inv-acme-aug-send" \
-d '{"params": {"id": "inv_812"}}'
Results quote a ledgerId — the chain reference for the act (see The event ledger). Relay held and refused outcomes verbatim; a held lodgement reported as "lodged" is the one failure mode the desk cannot forgive.
Retries
Books writes are idempotent by state on top of the idempotency header: a replayed transition against an already-transitioned row no-ops rather than double-posting. The exceptions, where each run is deliberately a new act: lineRecode, invoicePromise, statementRun, taxReturn, stpResubmit.
Ask the books
books.ask answers a grounded question over the ledger and desks ("what did we spend on hosting last quarter?") on the read scope, with its model call self-audited through the AI gateway. Use it for analysis; use the typed reads for anything you will act on.