# Billing

URL: /workflows/processes/billing · Id: `process:billing`

> Billing charges each Invoice when it is owed, recovers failed payments, and refunds within Limits.

Billing charges each [Invoice](/business/nouns/invoice) when it is owed, recovers failed payments, and refunds within [Limits](/business/nouns/limit).

## Outcome
| Part | This Process |
| --- | --- |
| Outcome | Each Invoice ends paid, refunded, or written off, and its Customer heard why at every step. |
| Reads | [Invoice](/business/nouns/invoice), [Order](/business/nouns/order) |
| Leaves | [Transaction](/business/nouns/transaction), [Invoice](/business/nouns/invoice), [Message](/business/nouns/message), [Approval](/business/nouns/approval), [Evidence](/business/nouns/evidence), [Dispute](/business/nouns/dispute) |
| On time | Within 1 day (business hours); the clock stops while an Action waits on `awaitingInput`. Late Actions go to the [Finance Lead](/agents/finance/finance-lead). |
| Runs in | Each Startup |

## Accountable
[Billing Specialist](/agents/finance/billing-specialist), [Finance Team](/agents/finance).

Responsible: [Billing Specialist](/agents/finance/billing-specialist).

Consulted: [Finance Lead](/agents/finance/finance-lead).

Informed: [Customer Lead](/agents/customer/customer-lead), [Success Manager](/agents/customer/success-manager).

## Workflows
| Workflow | Runs | Team |
| --- | --- | --- |
| [Dunning](/workflows/processes/billing/dunning) | every day | [Finance Team](/agents/finance) |
| [Issue a refund](/workflows/processes/billing/issue-refund) | on every [Order.refund](/business/nouns/order/refund) call | [Finance Team](/agents/finance) |
| [Invoice reminders](/workflows/processes/billing/invoice-reminders) | every day | [Finance Team](/agents/finance) |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | every day | [Finance Team](/agents/finance) |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) | on [`Dispute.opened`](/business/nouns/dispute#disputeopened) | [Finance Team](/agents/finance) |

## Who does each step

| Workflow | Step | Role | Calls | Approval |
| --- | --- | --- | --- | --- |
| [Dunning](/workflows/processes/billing/dunning) | 1. Retry each unpaid Invoice on the dunning schedule. | [Billing Specialist](/agents/finance/billing-specialist) | [Invoice.collect](/business/nouns/invoice/collect) | Within a Mandate; waits 1 business hour |
| [Dunning](/workflows/processes/billing/dunning) | 2. Tell the Customer once per retry, with a link to update payment. | [Billing Specialist](/agents/finance/billing-specialist) | [Message.create](/business/nouns/message/create) | None |
| [Dunning](/workflows/processes/billing/dunning) | 3. Pause service only after the last attempt fails. | [Billing Specialist](/agents/finance/billing-specialist) | [Subscription.pause](/business/nouns/subscription/pause) | None |
| [Dunning](/workflows/processes/billing/dunning) | 4. Void the Invoice and write it off 30 days after service pauses unpaid. | [Billing Specialist](/agents/finance/billing-specialist) | [Invoice.void](/business/nouns/invoice/void) | Asks first; waits 1 business hour |
| [Issue a refund](/workflows/processes/billing/issue-refund) | 1. Check the amount against the Billing Specialist’s Limit. | [Billing Specialist](/agents/finance/billing-specialist) |  |  |
| [Issue a refund](/workflows/processes/billing/issue-refund) | 2. Refund the Order through Stripe once its delay window passes. | [Billing Specialist](/agents/finance/billing-specialist) |  |  |
| [Issue a refund](/workflows/processes/billing/issue-refund) | 3. Tell the Customer the refund is sent. | [Billing Specialist](/agents/finance/billing-specialist) | [Message.create](/business/nouns/message/create) | None |
| [Invoice reminders](/workflows/processes/billing/invoice-reminders) | 1. Remind the Customer on each `send` Invoice a week before its due date, with bank details and its purchase order number. | [Billing Specialist](/agents/finance/billing-specialist) | [Message.create](/business/nouns/message/create) | None |
| [Invoice reminders](/workflows/processes/billing/invoice-reminders) | 2. Remind again the day after it falls past due. | [Billing Specialist](/agents/finance/billing-specialist) | [Message.schedule](/business/nouns/message/schedule) | None |
| [Invoice reminders](/workflows/processes/billing/invoice-reminders) | 3. Pause service only after the last reminder past due goes unpaid. | [Billing Specialist](/agents/finance/billing-specialist) | [Subscription.pause](/business/nouns/subscription/pause) | None |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | 1. Find Subscriptions and Agreements of a year or longer that renew inside the notice window, 15 to 45 days out by default. | [Billing Specialist](/agents/finance/billing-specialist) | [Subscription.list](/business/nouns/subscription/list) | None |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | 2. Find free trials longer than 31 days that convert to paid inside 3 to 21 days. | [Billing Specialist](/agents/finance/billing-specialist) |  |  |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | 3. Send one Message with the renewal date, the Price, usage against each Entitlement, and a one-step cancel link. | [Billing Specialist](/agents/finance/billing-specialist) | [Message.create](/business/nouns/message/create) | None |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | 4. Send it inside the Customer’s open Thread when a complaint is open, never after the window’s last day. | [Billing Specialist](/agents/finance/billing-specialist) |  |  |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | 5. Record the notice on the Subscription or Agreement with the date sent. | [Billing Specialist](/agents/finance/billing-specialist) | [Subscription.update](/business/nouns/subscription/update) | None |
| [Renewal notice](/workflows/processes/billing/renewal-notice) | 6. Hand Customers whose usage fell this term to the Success Manager before the renewal date. | [Success Manager](/agents/customer/success-manager) | [Renewal.open](/business/nouns/renewal/open) | None |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) | 1. Gather the Order, its delivery record, the Messages and the terms the Customer accepted as Evidence. | [Billing Specialist](/agents/finance/billing-specialist) | [Evidence.create](/business/nouns/evidence/create) | None |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) | 2. Accept the Dispute only when the Evidence cannot win it; otherwise answer it. | [Billing Specialist](/agents/finance/billing-specialist) | [Dispute.accept](/business/nouns/dispute/accept) | Asks first; waits 1 business hour |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) | 3. Answer the Dispute through Stripe before its deadline. | [Billing Specialist](/agents/finance/billing-specialist) | [Dispute.answer](/business/nouns/dispute/answer) | Asks first; waits 1 business hour |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) | 4. Send a Customer with a second Dispute to fraud screening. | [Risk Analyst](/agents/operations/risk-analyst) | [Party.screen](/business/nouns/party/screen) | None |

## Events
| Direction | When | What happens |
| --- | --- | --- |
| In | Every day | Starts [Dunning](/workflows/processes/billing/dunning) |
| In | On every [Order.refund](/business/nouns/order/refund) call | Starts [Issue a refund](/workflows/processes/billing/issue-refund) |
| In | Every day | Starts [Invoice reminders](/workflows/processes/billing/invoice-reminders) |
| In | Every day | Starts [Renewal notice](/workflows/processes/billing/renewal-notice) |
| In | On [`Dispute.opened`](/business/nouns/dispute#disputeopened) | Starts [Answer a chargeback](/workflows/processes/billing/answer-chargeback) |
| In | On [`Invoice.overdue`](/business/nouns/invoice#invoiceoverdue) | Wakes this Process, the first time only. The first retry of a Customer’s payment has failed. |
| Out | [`Invoice.collected`](/business/events/Invoice.collected) | Raised by step 1 of [Dunning](/workflows/processes/billing/dunning) |
| Out | `Message.created` | Raised by step 2 of [Dunning](/workflows/processes/billing/dunning) |
| Out | [`Subscription.paused`](/business/events/Subscription.paused) | Raised by step 3 of [Dunning](/workflows/processes/billing/dunning) |
| Out | [`Invoice.voided`](/business/events/Invoice.voided) | Raised by step 4 of [Dunning](/workflows/processes/billing/dunning) |
| Out | [`Message.scheduled`](/business/events/Message.scheduled) | Raised by step 2 of [Invoice reminders](/workflows/processes/billing/invoice-reminders) |
| Out | `Subscription.updated` | Raised by step 5 of [Renewal notice](/workflows/processes/billing/renewal-notice) |
| Out | [`Renewal.opened`](/business/events/Renewal.opened) | Raised by step 6 of [Renewal notice](/workflows/processes/billing/renewal-notice) |
| Out | `Evidence.created` | Raised by step 1 of [Answer a chargeback](/workflows/processes/billing/answer-chargeback) |
| Out | [`Dispute.accepted`](/business/events/Dispute.accepted) | Raised by step 2 of [Answer a chargeback](/workflows/processes/billing/answer-chargeback) |
| Out | [`Dispute.answered`](/business/events/Dispute.answered) | Raised by step 3 of [Answer a chargeback](/workflows/processes/billing/answer-chargeback) |
| Out | [`Party.screened`](/business/events/Party.screened) | Raised by step 4 of [Answer a chargeback](/workflows/processes/billing/answer-chargeback) |

## Plays
No Play names this Process.

## Controls
- [One-way Action windows](/systems/trust-and-governance/controls/one-way-windows)

## Settings
| Setting | Type | Default | Say | Set with |
| --- | --- | --- | --- | --- |
| `dunning.retries` | `integer` | `4` | Retry failed payments four times | [Setting.update](/business/nouns/setting/update) |
| `dunning.pauseService` | `boolean` | `true` | Pause service after the last retry fails | [Setting.update](/business/nouns/setting/update) |
| `refunds.limit` | `money` | $100 | Refund up to $100 without asking me | [Setting.update](/business/nouns/setting/update) |

## Failure
A failed step retries; when retries run out, the Action goes to the [Finance Lead](/agents/finance/finance-lead).

| Step | On failure |
| --- | --- |
| [Dunning](/workflows/processes/billing/dunning) 1. Retry each unpaid Invoice on the dunning schedule. | `cardExpired`: The card on file has expired, so no retry can succeed. Continues at `tell`. |
| [Dunning](/workflows/processes/billing/dunning) 3. Pause service only after the last attempt fails. | Remedy: [Subscription.resume](/business/nouns/subscription/resume). |
| [Issue a refund](/workflows/processes/billing/issue-refund) 2. Refund the Order through Stripe once its delay window passes. | Goes to the [Finance Lead](/agents/finance/finance-lead). |
| [Invoice reminders](/workflows/processes/billing/invoice-reminders) 3. Pause service only after the last reminder past due goes unpaid. | Remedy: [Subscription.resume](/business/nouns/subscription/resume). |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) 2. Accept the Dispute only when the Evidence cannot win it; otherwise answer it. | `contest`: The Evidence shows the Order was delivered as sold, so the Dispute is answered. Continues at `answer`. |
| [Answer a chargeback](/workflows/processes/billing/answer-chargeback) 3. Answer the Dispute through Stripe before its deadline. | Goes to the [Finance Lead](/agents/finance/finance-lead). |

## Loop
| Step | Value |
| --- | --- |
| Hypothesis | Retrying a failed payment on the Customer’s usual pay day recovers more Invoices and keeps more Customers than retrying daily. |
| Measure | [Month-3 retention](/agents/customer#month-3-retention) over 90d, at least 100 observations |
| Learn | The learn step reads the Readout, records Evidence and a Decision, and drafts the next Variant. |

## Code

**Ask**

```text
Turn on billing
```

**API**

```bash
curl -X POST https://api.sb/leaseline.app/processes/process_1/enable \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

**SDK**

```ts
import { $ } from 'startup-builder'

await $.Process.enable({ process: 'process_1' })
```

**CLI**

```bash
npx startup-builder do Process.enable --json '{"process":"process_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Process.enable({ process: 'process_1' })"
  }
}
```

## Enable
On by default for [SaaS](/products/templates/saas), [Headless SaaS](/products/templates/headless-saas), [Dataset API](/products/templates/dataset-api), [Agent as a Service](/products/templates/agent-as-a-service). [Process.pause](/business/nouns/process/pause) stops new Actions; Actions in progress finish.

**Ask**

```text
Retry failed payments four times
```

**API**

```bash
curl -X PATCH https://api.sb/leaseline.app/settings/setting_1 \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value":4,"key":"dunning.retries"}'

curl -X PATCH https://api.sb/leaseline.app/settings/setting_2 \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value":true,"key":"dunning.pauseService"}'

curl -X PATCH https://api.sb/leaseline.app/settings/setting_3 \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value":100,"key":"refunds.limit"}'
```

**SDK**

```ts
import { $ } from 'startup-builder'

await $.Setting.update({ id: 'setting_1', value: 4, key: 'dunning.retries' })
await $.Setting.update({ id: 'setting_2', value: true, key: 'dunning.pauseService' })
await $.Setting.update({ id: 'setting_3', value: 100, key: 'refunds.limit' })
```

**CLI**

```bash
npx startup-builder do Setting.update --json '{"id":"setting_1","value":4,"key":"dunning.retries"}'
npx startup-builder do Setting.update --json '{"id":"setting_2","value":true,"key":"dunning.pauseService"}'
npx startup-builder do Setting.update --json '{"id":"setting_3","value":100,"key":"refunds.limit"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "await $.Setting.update({ id: 'setting_1', value: 4, key: 'dunning.retries' })\nawait $.Setting.update({ id: 'setting_2', value: true, key: 'dunning.pauseService' })\nreturn await $.Setting.update({ id: 'setting_3', value: 100, key: 'refunds.limit' })"
  }
}
```

## Example
A dental-reminder [SaaS](/products/templates/saas) bills 140 offices on the 1st. [Dunning](/workflows/processes/billing/dunning) recovers 9 of the 11 failed cards by the 14th. The [Billing Specialist](/agents/finance/billing-specialist) refunds one double charge of $49, inside its $100 Limit, the same hour.