Audience: consultant. Prerequisite: 01 — Engagement lifecycle.
Getting paid for the work. Mostly Engagement ▸ Operate, with the firm-wide settings at Firm scope.
Set up once, at Firm scope
| Surface | Sets |
|---|---|
Rates (/rates) | your standard billing rates |
Payment Settings (/payment-config) | how clients pay you |
Branding (/operator-branding) | what your invoices look like — logo, business identity |
Billing & Costs (/billing) | your own platform spend, and a monthly budget |
How money moves
Four things become billable, and they all converge on the same invoice. What an invoice can do next is a one-way ratchet:
flowchart TD
RATE["<b>Rates</b><br/>firm or engagement rate card"] --> TIME
SCOPE["<b>Scope items</b><br/>agreed at the start"] --> CONTRACT["<b>Contract</b><br/>built from the scope"]
CR["<b>Change request</b><br/>raised by the client<br/>in the portal"] --> QUOTE["<b>Quote</b><br/>priced"]
QUOTE -->|"client approves<br/>in the portal"| CO["<b>Change order</b><br/>becomes a scope item —<br/>and is billable"]
TIME["<b>Time entries</b><br/>logged against the engagement,<br/>each carrying its rate"] --> INV
CONTRACT --> INV
CO --> INV
INV["<b>INVOICE · draft</b><br/>not a claim on the client yet;<br/>excluded from the aging figures"]
INV -->|issue| SENT["<b>sent</b>"]
SENT -->|due date passes| OVERDUE["<b>overdue</b>"]
SENT -->|payment| PAID["<b>paid</b> 🔒"]
OVERDUE -->|payment| PAID
INV -->|cancel| VOID["<b>void</b> 🔒"]
SENT -->|cancel| VOID
DISPUTE["<b>Dispute</b><br/>recorded against a line"] -.->|"keeps the state honest;<br/>does not change it"| SENT
⚠️ paid and void are terminal, and the platform enforces it. A later write trying to
move a paid invoice backwards is refused, not applied — because reverting a paid invoice
would put the client back into the late-payment cadence and re-arm the portal’s Pay button for
money you already hold. Invoices also cannot move backwards generally: sent → draft is
refused the same way.
⚙️ The refusal is reported, never silent. It comes back as a verdict with a reason rather than an exception, because the caller is often a payment webhook — and a webhook that throws is retried forever while the money has already moved.
⚠️ A draft is not a claim. Drafts are deliberately excluded from the aging tiles, so your receivables stay honest. An invoice you have not issued is not money anyone owes you.
Why a draft is not a claim
An invoice starts as a draft, and drafts are excluded from the aging tiles. That exclusion is the point, not a rounding choice.
Aging answers one question: how much does the outside world owe me, and how late is it? A draft is a number you have written down for yourself. Counting it would inflate receivables with money nobody has been asked for — and receivables are what you plan cash against, quote a lender, and decide whether to chase.
⚙️ So the ratchet has a floor as well as a ceiling: a draft is not a claim, and once an invoice
is paid or void it cannot move backwards. Both ends exist so the number in the middle
means something.
★ The practical habit: issue promptly or delete. A draft left sitting is invisible to your aging and invisible to the client — the worst of both, because it feels like work in progress while being neither billed nor withdrawn.
⚖️ Judgement, not mechanism. The exclusion of drafts from aging and the terminal-status refusal are both enforced and anchored. “Issue promptly or delete” is advice.
Contracts
Engagement ▸ Contracts (/engagements/:id/contracts). Build the agreement from
the engagement’s scope items so the contract and the delivery plan cannot drift
apart.
Generating is private; sending is what publishes. A generated contract is yours alone — regenerate it as often as you like while you settle the wording. Nothing reaches the client, nothing appears in their portal, and no email goes out until you press Send to client. Only then can they see it or sign it.
⚠️ Sending refuses an invoice nobody can pay. If Stripe is not collecting payments for your firm AND Settings ▸ Branding has no bank wire details, the PDF would carry no payment instructions at all — while your client’s portal tells them to pay using the details on the invoice. Add wire details or connect Stripe, then send.
This is the same distinction the invoice section below draws between a draft and an issued invoice, and for the same reason: work in progress should not look like a demand on the client.
⚙️ Who may act on a contract. Generating, sending, recording the client’s signed copy,
counter-signing, deleting and turning an approved change order into a document are the
engagement’s commercial acts, so they need the same modify_scope capability as its scope — the
admin and build roles, narrowed by the seat’s role on that engagement. A billing seat reads every
contract and its PDFs, and can preview one, but is offered none of the steps.
⚠️ A contract that has been sent cannot be deleted — by anyone. Once it has gone to the client it records what they were offered and agreed to, the same standing as an issued invoice. A draft or a generated contract the client never received can still be deleted. Uploading a firm-wide contract template — the boilerplate every future contract starts from — is an administrator’s action.
Time
Engagement ▸ Invoices & Time (/engagements/:id/invoices). Log time against the
engagement. Time entries carry a rate (from the engagement’s or the firm’s rate
card) and flow into invoices.
Invoicing
From the same surface, raise an invoice. An invoice starts as a draft — it is not a claim on the client until you issue it, and drafts are excluded from the aging figures so your receivables are honest.
The aging tiles and the invoice list are scope-consistent: what the tiles count is what the list shows. If they ever disagree, that is a bug worth reporting rather than a rounding artefact.
Invoices carry your firm’s branding, support multiple currencies, and can apply tax configuration.
Recurring charges
Some money is not a one-off: a monthly hosting fee, a support retainer. Raising those by hand means your revenue depends on you remembering, every month, forever.
A recurring invoice is a schedule. You give it an engagement, a label, the lines it bills, and a day of the month; from then on it raises an ordinary invoice on that day with nobody pressing anything. What comes out is a normal invoice — its own number in your sequence, its own PDF, and (when your Stripe account is connected) its own hosted payment link, exactly as if you had raised it by hand. It appears in the same lists, aging tiles and reminders as everything else. There is no separate “subscriptions” ledger to reconcile.
⚙️ It bills the month that just ended. An invoice raised on 1 March covers 1–28 February. That is the reading a client expects for a service already delivered, and those dates are printed on the invoice they receive.
⚠️ You can only anchor on days 1–28. This is a real limitation rather than a tidy-up: the schedule is a cron expression, and cron does not clamp — a 31st anchor fires in January and March and simply does not fire in February, April, June, September or November. Rather than silently skip five months a year, or quietly rewrite your 31 into a 28 and bill on a date you did not choose, it refuses. If you need “the last day of the month”, say so and it becomes a feature; today it is not one.
Pausing does not lose money, and resuming does not create it. A paused schedule raises nothing. When you resume it, you get one invoice — not one for every month it was paused. The same is true after downtime: if the server is off over a billing day, the next sweep raises a single catch-up invoice rather than a backlog. A client never receives four bills at once because of something that happened on our side.
⚠️ Stage 1 holds no card. The schedule issues an invoice; it does not charge a saved payment method, because there is nowhere in the product to save one yet. So this closes “my revenue depends on me remembering”. It does not close “my revenue depends on my client remembering” — the client still has to pay the invoice, exactly as they do today. Those are different risks and it is worth being clear which one you have solved.
If a schedule fails to bill — the engagement was archived, say — it records why on the schedule itself and releases the period so the next sweep retries it. A schedule that has stopped producing invoices will tell you; it does not go quiet.
When scope grows: change orders
Engagement ▸ Change Orders (/engagements/:id/change-orders).
The honest path when a client asks for something outside the agreed scope:
- The client raises a change request from the portal (or you raise it here).
- It becomes a change order with the extra scope and its price.
- The client approves it in the portal.
- Approved scope becomes a scope item on the engagement, and is billable.
This is the mechanism that keeps scope creep visible and paid for rather than absorbed. Use it early; a change order raised late reads as a surprise bill.
Quotes
Where a piece of work needs pricing before commitment, a quote can be produced from the change request and sent for approval. Approval converts it into scope.
Disputes
Engagement ▸ Disputes (/engagements/:id/disputes). When a client disputes a
line, record it here rather than in email. It keeps the invoice’s state honest and
gives you a record of the resolution.
Costs and budgets
Firm ▸ Billing & Costs (/billing) shows what the platform is costing you,
broken down by engagement. Set a workspace monthly budget in the form on that
page: enter the amount and save. Once set, you get consumption bars and daily breach
alerts. Clearing the field and saving removes the budget.
AI usage is metered per engagement against its AI capability budget — see
Engagement ▸ AI Capabilities (/engagements/:id/ai-capabilities).
What “done” looks like for commercials
- Rates are set before you log time, not after.
- Every hour worked is logged against the engagement.
- Scope that grew became a change order the client approved — not absorbed.
- Draft invoices are drafts; issued invoices are issued; the aging reflects that.
- You know what this engagement costs you to run, not just what you charge.