---
title: "Flow of funds"
url: https://services.dev/docs/money/flow-of-funds
captured: [money-flow.txt, money-test.txt]
---
# Flow of funds

URL: https://services.dev/docs/money/flow-of-funds

> Every ledger line as Stripe holds it. One charge model, separate charges and transfers on behalf of each seller; the accounts and who controls them; the held money that waits under its job; money that moves outside a charge; the order money is drawn back in; the event map by how a charge is held; and the sandbox values that test each path.

services.dev runs Stripe Connect, and every seller is a connected account on it: your brand, each member who sells on their own rail, each door operator and each insurer. This page is the one that names Stripe's objects; every other Money page and every example cites it.

Every charge your brand sells is a separate charge and transfer. The PaymentIntent sits on services.dev's account, `on_behalf_of` your account, so you are the merchant of record and the buyer's statement names you. It carries its job as its `transfer_group` and no `transfer_data`, so nothing moves until the fact. At the fact, one transfer per claimant moves the money, each naming the charge as its `source_transaction`. Only a fee a member sells on their own rail is a destination charge. services.dev's take is what stays on its own account after every transfer, written as its own ledger entry, never an application fee.

## Every line, at Stripe

| Line | The Stripe object | Seller of record | Lands on | The way back |
|---|---|---|---|---|
| Your charge | a PaymentIntent on services.dev's account, `on_behalf_of` your account, `transfer_group` the job, no `transfer_data` | you | services.dev's account, untransferred, until its fact | refunded from the hold before the fact; after it, by [the draw order](#the-way-back) |
| Your share | a `transfer` to your account at the fact, `source_transaction` the charge: the charge less every other line | — | your account | a reversal of that transfer |
| services.dev's take | what stays on services.dev's account after every transfer, its own `le_` entry | — | services.dev | returned pro rata with each refund |
| A member's own charge | a destination charge: `on_behalf_of` the member, `transfer_data` to the member, whole | the member | the member's account | the member's own refund; a lost dispute is recovered from you |
| Pay on your rail | a `transfer` to the member, `source_transaction` the job's charge | you | the member's account | never reversed |
| Money outside a charge | an account debit, `py_`, from your account to services.dev, then a `transfer` onward | you | the member's, the operator's or the insurer's account | never reversed |
| Placement, coverage | a `transfer` to the operator's or the insurer's account, from the charge or by a debit | you | their account | stands |
| Pass-through | a line of your charge, paid on to the outside system | you | the outside system | its own refund, when it gives one |
| Processing | Stripe's fee on each charge, netted from your share; on a member's charge, recovered from you by a debit | — | Stripe | stays spent on a refund |
| Tax | Stripe Tax on your charge with `Brand.tax: 'stripe'`, `automatic_tax.liability` your account; a member-rail fee is taxed on the member's own account | you | your account, to remit | refunded with its line |
| A subscription cycle | a Stripe `invoice` on services.dev's account, `on_behalf_of` your account, its PaymentIntent held like any charge | you | services.dev's account until the cycle releases | as your charge |
| A payout | a Stripe `payout` from each account to its bank, on Stripe's timing | — | the bank | a returned payout comes back to that account's balance |
| Your services.dev plan | a Stripe subscription on services.dev's account, billed to you | — | services.dev | — |

```bash
npx services.dev jobs flow job_7Hq3   # api.lawyer: a receipt releases two charges
```

```txt title="magic values: none"
job_7Hq3 · transfer_group job_7Hq3 · services.dev's account holds each charge, and each seller is a connected account
  chg_7Hq3     $174.00   pi_…Hq3 on services.dev's account · on_behalf_of acct_…L4w1, api.lawyer · separate charge, no transfer_data
               authorized 2 Nov, captured 9 Nov on the receipt
               → tr_…Hq3 $157.17 less the take on $170.00 to acct_…L4w1, source_transaction ch_…Hq3
                 $174.00 less coverage $4.00, AI $2.10 and processing $10.73 on both charges
               → tr_…Hq3c $4.00 to the attorneys Pool's insurer, source_transaction ch_…Hq3
  chg_7Hq4     $175.00   pi_…Hq4 · destination charge on_behalf_of acct_…7Hq3, the attorney · authorized and captured with chg_7Hq3
               → transfer_data to acct_…7Hq3, whole · no take
  take         its own entry, le_7Hq3t · what stays on services.dev's account after both transfers
  payouts      api.lawyer to bank …6789 daily · the attorney on their own schedule, a line per brand
```

## The accounts

| Account | Controlled by | Capabilities | Connected at |
|---|---|---|---|
| Your brand | services.dev: `losses.payments: 'application'`, `requirement_collection: 'application'`, `stripe_dashboard.type: 'none'` | `transfers`, and `card_payments` and `us_bank_account_ach_payments` for the charges made on its behalf | `services.dev payouts connect`, in the owner console |
| A member | the same | `transfers`; `card_payments` where a Rules row puts their fee on their rail | the last step of `/join` |
| A door operator, an insurer | the same | `transfers` | when the door or the cover is listed |
| A buyer's own Stripe account, for a payment proof | the buyer: services.dev's Stripe app, never a connected account | reads customers, invoices, charges, refunds and disputes; writes only the invoice the end step drafts | the order, in the customer portal |

services.dev's account carries every loss first and recovers it from your brand by the draw order, so no loss reaches a member: a member's account is paid by transfers and never debited.

## Held money

services.dev holds only held dollars waiting for their fact, each tagged to its job. A captured hold and outcome money sit on services.dev's account, untransferred, under the job's transfer group, in no one's available balance, so a refund elsewhere, a dispute elsewhere or a negative balance can't reach them:

```bash
npx services.dev jobs flow job_7Hq5   # api.forsale: a won deal, held to its window
```

```txt title="magic values: none"
job_7Hq5 · transfer_group job_7Hq5
  placement      $50.00   4 and 9 Nov, a gig at each fill · py_…Hq5p and py_…Hq5q debited from acct_…F0r5, api.forsale · → tr_…Hq5p to closers.deals
  setter        $250.00   10 Nov, at meet.held · py_…Hq5s debited from acct_…F0r5 · → tr_…Hq5s to acct_…7Hs1, the setter · no charge exists yet
  chg_7Hq5   $11,100.00   pi_…Hq5 on services.dev's account · on_behalf_of acct_…F0r5 · us_bank_account on mandate_…Hq5, off-session
               processing 4 Jan, succeeded 8 Jan · untransferred under job_7Hq5, in no one's available balance
  held        $3,700.00 the 5% · $7,400.00 the closer's share · until 9 Mar 2027
  9 Mar        → tr_…Hq5a $3,683.66 less the take on $3,400.00 to acct_…F0r5: the 5% less AI $11.34 and processing $5.00 · source_transaction ch_…Hq5
  12 Mar       → tr_…Hq5b $7,400.00 to acct_…7Hq4, the closer, on the Pool's Friday run · source_transaction ch_…Hq5
```

## Money outside a charge

Some money moves when no charge exists to move it from: flat pay before any charge, such as a setter's at the held meeting; placement and AI on a job that ends lost; a lost dispute on a member's charge. Each is an account debit from your account to services.dev, then a transfer onward, so a member is always paid from money already collected. A Task with flat pay posts only once its pay can be drawn; until then the order answers 402 `pay_unfunded` with the cure:

```bash
npx services.dev jobs flow job_7Hs3 --debits   # api.forsale: a lost deal, with no charge to draw from
```

```txt title="magic values: none"
job_7Hs3 · lost 10 Nov · competitor · the mandate is never charged
  placement      $25.00   3 Nov · py_…Hs3p debited from acct_…F0r5 · → tr_…Hs3p to closers.deals
  setter        $250.00   3 Nov, at meet.held · py_…Hs3s debited from acct_…F0r5 · → tr_…Hs3s to acct_…7Hs1
  AI             $40.00   10 Nov, at the end · py_…Hs3a debited from acct_…F0r5 · stays on services.dev's account
  drawn from   api.forsale's available balance · its bank account …6789 had it been short · never a member's transfer
```

## The way back

When money goes back, services.dev draws it in this order and stops at the first that covers it:

1. the job's untransferred hold;
2. a reversal of your transfer on that job;
3. your account's available balance;
4. a debit to your saved bank account.

It never draws on a member's transfer, another job's held share or a member-rail charge, and `check` fails `held_pay_exposed` on any definition that would. A balance still negative after the fourth step answers your new orders 402 `balance_negative` until it is paid:

```bash
npx services.dev jobs flow job_7Hr1 --refunds   # a goodwill refund of api.lawyer's line
```

```txt title="magic values: none"
  rfd_7Hr1      $174.00   re_…Hr1 on ch_…Hr1, from services.dev's account
               1 · the hold: none, the charge had released
               2 · tr_…Hr1 reversed in full from acct_…L4w1, $157.17 less the take · the take returned with it
               3 · py_…Hr1 $16.83 debited from acct_…L4w1: coverage $4.00, AI $2.10 and processing $10.73, which stay spent
  chg_7Hr2      $175.00   the attorney's sale · untouched
```

```bash
npx services.dev jobs flow job_7Hn9 --disputes   # lost, on an attorney's own charge
```

```txt title="magic values: none"
  dsp_7Hn9      $175.00   du_…Hn9 on ch_…Hn9 · the amount and the $15.00 fee debited from services.dev's account, which carries it
               recovered from acct_…L4w1, api.lawyer, as le_7Hn9m make_whole, by py_…Hn9 · tr_…Hn9 to the attorney stands
```

## The event map

Each Stripe event has one meaning, set by how the charge is held:

| services.dev event | An authorization | A captured hold | A mandate |
|---|---|---|---|
| `charge.held` | `payment_intent.amount_capturable_updated` | `payment_intent.succeeded`, at the order | — |
| `charge.mandated` | — | — | `setup_intent.succeeded` |
| `charge.authorization_expiring` | services.dev's own clock, a day before `capture_before` | — | — |
| `charge.pending` | — | — | `payment_intent.processing`: a bank debit in flight |
| `charge.collected` | `payment_intent.succeeded`, at capture | the fact itself: the money already sits on services.dev's account | `payment_intent.succeeded` |
| `charge.failed` | — | — | `payment_intent.payment_failed`, or `requires_action` on an off-session charge |
| `charge.voided` | `payment_intent.canceled` | a full refund from the hold | `payment_intent.canceled` |

| services.dev event | The Stripe event behind it |
|---|---|
| `charge.refunded` | `charge.refunded`, with `transfer.reversed` once your share has moved |
| `dispute.prevented` | `radar.early_fraud_warning.created`, then the refund |
| `dispute.opened`, `dispute.closed` | `charge.dispute.created`, `charge.dispute.closed` |
| `pay.paid` | `transfer.created`, to the member's account |
| `payout.paid`, `payout.failed` | `payout.paid`, `payout.failed`, on the account that paid out |
| `pay.waiting` | `account.updated`, with `requirements.currently_due` |
| `take.credited` | `transfer.created`: the month's lost-cost netting, returned to your account |
| `subscription.renewed`, `subscription.unpaid` | `invoice.paid`, `invoice.payment_failed` |

Each event on the feed carries the Stripe ids behind it:

```bash
npx services.dev events --source stripe --job job_7Hq5   # each services.dev event, and the Stripe event behind it
```

```txt title="magic values: none"
  10 Nov 11:34   pay.accrued          ← the meet.held guard · then py_…Hq5s, and transfer.created for tr_…Hq5s
   4 Jan 09:21   charge.pending       ← payment_intent.processing · us_bank_account on mandate_…Hq5
   8 Jan 09:00   charge.collected     ← payment_intent.succeeded
   9 Mar 09:00   charge.earned        ← the window's own clock · transfer.created for tr_…Hq5a
  12 Mar 16:02   pay.paid             ← transfer.created for tr_…Hq5b, on the Pool's run · payout.paid follows on acct_…7Hq4, on Stripe's timing
```

## Every form

| Key | Written | Left out | Does |
|---|---|---|---|
| `Brand.tax` | `'stripe' \| false` | `false` | Calculates tax on your charges with Stripe Tax and collects it as its own line |
| `Brand.payouts` | `{ rail: 'stripe'; schedule? }` | `rail: 'stripe'`, daily | Your connected account, and how often it pays you: [payouts](https://services.dev/docs/money/payouts.md#every-form) |

| Code | When |
|---|---|
| `pay_unfunded` | 402 on an order: flat pay before any charge can't be drawn from your balance or your bank account |
| `balance_negative` | 402 on an order: your balance is negative after the draw order |
| `held_pay_exposed` | `check`: a definition would draw on held share pay, a member's transfer or a member-rail charge |

## Testing the money

Every path runs in test mode, set by magic values on the order's `metadata.sandbox`:

| Value | What it makes happen | At Stripe |
|---|---|---|
| `payment: 'decline'` | the charge declines, and dunning runs | `pm_card_chargeDeclined` |
| `payment: 'auth_expired'` | the authorization nears its lapse, and the hold is captured | the test clock past `capture_before` |
| `payment: 'dispute'` | the buyer's bank opens a dispute after capture | `pm_card_createDispute` |
| `payment: 'early_warning'` | an early fraud warning arrives first | a test card that raises `radar.early_fraud_warning.created` |
| `payment: 'bank'` | the charge is a bank debit, pending four days, then collected | `us_bank_account`, test routing `110000000` |
| `payout: 'fail'` | the member's next payout fails | test bank account `000111111116` |
| `proof: 'paid'` | the outcome's payment lands at once | `invoice.paid` on the buyer's test account |
| `proof: 'refunded_day_20'` | the payment behind the outcome is refunded on day 20 | the test clock at day 20, then `charge.refunded` |

services.dev's test clock advances a Stripe test clock with it, so every window and schedule moves together:

```txt
→ POST /deals {"account":{"name":"Test Freight"},"value":80000,"offer":"https://seller.example/offer","max_usd":12000,"payment_method":"pm_card_visa","key":"test-1","metadata":{"sandbox":{"proof":"paid"}}}
```

```txt title="201 · job_9Tq1 · deal · working · mandated · up to $12,000.00 · on test clock clk_9Tq1 · magic values: metadata.sandbox.proof = paid"

→ GET /owner/deals/job_9Tq1   # the AI and a test closer work it; the proof pays at once
← 200 · job_9Tq1 · deal · won · $11,100.00 charged, held to 2027-01-12 · test clock at 13 Nov 2026
  ledger    +$11,100.00 · closer $7,400.00 · setter $250.00 · placement $50.00 · AI $11.34 · you $3,388.66 before the take

→ POST /test_clocks/default/advance {"by":"60d"}   # backed by a Stripe test clock
  2027-01-12  job_9Tq1  outcome.final · charge.earned $11,100.00
  2027-01-12  tsk_9Tq3  pay.accrued $7,400.00 · the closer
  2027-01-14  tsk_9Tq3  pay.payable
  2027-01-15  pyo_9Tq3  pay.paid $7,400.00 · Friday's run · test bank …0000
```

---

- Up: [Money](https://services.dev/docs/money.md)
- Previous: [Lines and rails](https://services.dev/docs/money/lines-and-rails.md)
- Next: [Refunds and disputes](https://services.dev/docs/money/refunds-and-disputes.md)
