# Expense approval

URL: /workflows/processes/expense-approval · Id: `process:expense-approval`

> Expense approval checks every card charge against its Mandate and the spender’s Limit, asks the Founder above it, and codes each charge with its receipt.

Expense approval checks every card charge against its [Mandate](/business/nouns/mandate) and the spender’s [Limit](/business/nouns/limit), asks the [Founder](/agents/founding/founder) above it, and codes each charge with its receipt.

## Outcome
| Part | This Process |
| --- | --- |
| Outcome | Every card charge is inside its Mandate or approved by the Founder, coded to an account, and carries its receipt. |
| Reads | [Mandate](/business/nouns/mandate), [Card](/business/nouns/card) |
| Leaves | [Transaction](/business/nouns/transaction), [Approval](/business/nouns/approval) |
| 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
[Treasurer](/agents/finance/treasurer), [Finance Team](/agents/finance).

Responsible: [Treasurer](/agents/finance/treasurer).

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

Informed: [Founder](/agents/founding/founder).

## Workflows
| Workflow | Runs | Team |
| --- | --- | --- |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) | on [`Mandate.spent`](/business/events/Mandate.spent) | [Finance Team](/agents/finance) |

## Who does each step

| Workflow | Step | Role | Calls | Approval |
| --- | --- | --- | --- | --- |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) | 1. Check the charge against the Card’s Mandate and the spender’s Limit. | [Treasurer](/agents/finance/treasurer) |  |  |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) | 2. Ask the Founder for an Approval when the charge passes the Limit. | [Treasurer](/agents/finance/treasurer) | [Approval.create](/business/nouns/approval/create) | None |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) | 3. Code the Transaction to its account. | [Treasurer](/agents/finance/treasurer) | [Transaction.categorize](/business/nouns/transaction/categorize) | None |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) | 4. Attach the receipt from the vendor’s email, and chase the spender after a week without one. | [Treasurer](/agents/finance/treasurer) | [Transaction.attach](/business/nouns/transaction/attach) | None |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) | 5. Pause the Mandate when the Founder refuses a charge. | [Treasurer](/agents/finance/treasurer) | [Mandate.pause](/business/nouns/mandate/pause) | None |

## Events
| Direction | When | What happens |
| --- | --- | --- |
| In | On [`Mandate.spent`](/business/events/Mandate.spent) | Starts [Approve spend](/workflows/processes/expense-approval/approve-spend) |
| In | On [`Card.issued`](/business/events/Card.issued) | Wakes this Process, the first time only. The Startup issues its first Card. |
| Out | `Approval.created` | Raised by step 2 of [Approve spend](/workflows/processes/expense-approval/approve-spend) |
| Out | [`Transaction.categorized`](/business/events/Transaction.categorized) | Raised by step 3 of [Approve spend](/workflows/processes/expense-approval/approve-spend) |
| Out | [`Transaction.attached`](/business/events/Transaction.attached) | Raised by step 4 of [Approve spend](/workflows/processes/expense-approval/approve-spend) |
| Out | [`Mandate.paused`](/business/events/Mandate.paused) | Raised by step 5 of [Approve spend](/workflows/processes/expense-approval/approve-spend) |

## Plays
No Play names this Process.

## Controls
No Control names this Process or a Workflow of it.

## Settings
| Setting | Type | Default | Say | Set with |
| --- | --- | --- | --- | --- |
| `spend.askAbove` | `money` | $500 | Ask me before any charge over $500 | [Setting.update](/business/nouns/setting/update) |
| `spend.receiptWithin` | `string` | `7d` | Chase missing receipts after a week | [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 |
| --- | --- |
| [Approve spend](/workflows/processes/expense-approval/approve-spend) 2. Ask the Founder for an Approval when the charge passes the Limit. | `refused`: The Founder refuses the charge, so the Mandate pauses. Continues at `pause`. |

## Loop
| Step | Value |
| --- | --- |
| Hypothesis | Asking the Founder only above a per-charge threshold keeps spend inside budget with fewer Approvals than asking on every charge. |
| Measure | [Fully loaded Cost](/agents/finance#fully-loaded-cost) over 30d, at least 50 observations |
| Learn | The learn step reads the Readout, records Evidence and a Decision, and drafts the next Variant. |

## Code

**Ask**

```text
Turn on expense approval
```

**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 every Product Template. [Process.pause](/business/nouns/process/pause) stops new Actions; Actions in progress finish.

**Ask**

```text
Ask me before any charge over $500
```

**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":500,"key":"spend.askAbove"}'

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":"7d","key":"spend.receiptWithin"}'
```

**SDK**

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

await $.Setting.update({ id: 'setting_1', value: 500, key: 'spend.askAbove' })
await $.Setting.update({ id: 'setting_2', value: '7d', key: 'spend.receiptWithin' })
```

**CLI**

```bash
npx startup-builder do Setting.update --json '{"id":"setting_1","value":500,"key":"spend.askAbove"}'
npx startup-builder do Setting.update --json '{"id":"setting_2","value":"7d","key":"spend.receiptWithin"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "await $.Setting.update({ id: 'setting_1', value: 500, key: 'spend.askAbove' })\nreturn await $.Setting.update({ id: 'setting_2', value: '7d', key: 'spend.receiptWithin' })"
  }
}
```

## Example
A design-tool [Startup](/business/nouns/startup) gives its [Marketing Lead](/agents/marketing/marketing-lead) a $1,000 monthly [Mandate](/business/nouns/mandate) for ads. A $640 booth charge passes the $500 threshold. The [Founder](/agents/founding/founder) approves it in one [Approval](/business/nouns/approval), and the [Treasurer](/agents/finance/treasurer) codes it to events with the receipt attached.