Flow of funds

The running jobjob_7Hq2 · accepted · document.fr-CA.pdf · $49.00 final

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.

LineThe Stripe objectSeller of recordLands onThe way back
Your chargea PaymentIntent on services.dev's account, on_behalf_of your account, transfer_group the job, no transfer_datayouservices.dev's account, untransferred, until its factrefunded from the hold before the fact; after it, by the draw order
Your sharea transfer to your account at the fact, source_transaction the charge: the charge less every other line—your accounta reversal of that transfer
services.dev's takewhat stays on services.dev's account after every transfer, its own le_ entry—services.devreturned pro rata with each refund
A member's own chargea destination charge: on_behalf_of the member, transfer_data to the member, wholethe memberthe member's accountthe member's own refund; a lost dispute is recovered from you
Pay on your raila transfer to the member, source_transaction the job's chargeyouthe member's accountnever reversed
Money outside a chargean account debit, py_, from your account to services.dev, then a transfer onwardyouthe member's, the operator's or the insurer's accountnever reversed
Placement, coveragea transfer to the operator's or the insurer's account, from the charge or by a debityoutheir accountstands
Pass-througha line of your charge, paid on to the outside systemyouthe outside systemits own refund, when it gives one
ProcessingStripe's fee on each charge, netted from your share; on a member's charge, recovered from you by a debit—Stripestays spent on a refund
TaxStripe Tax on your charge with Brand.tax: 'stripe', automatic_tax.liability your account; a member-rail fee is taxed on the member's own accountyouyour account, to remitrefunded with its line
A subscription cyclea Stripe invoice on services.dev's account, on_behalf_of your account, its PaymentIntent held like any chargeyouservices.dev's account until the cycle releasesas your charge
A payouta Stripe payout from each account to its bank, on Stripe's timing—the banka returned payout comes back to that account's balance
Your services.dev plana Stripe subscription on services.dev's account, billed to you—services.dev—
Run
npx services.dev jobs flow job_7Hq3   # api.lawyer: a receipt releases two charges
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
AccountControlled byCapabilitiesConnected at
Your brandservices.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 behalfservices.dev payouts connect, in the owner console
A memberthe sametransfers; card_payments where a Rules row puts their fee on their railthe last step of /join
A door operator, an insurerthe sametransferswhen the door or the cover is listed
A buyer's own Stripe account, for a payment proofthe buyer: services.dev's Stripe app, never a connected accountreads customers, invoices, charges, refunds and disputes; writes only the invoice the end step draftsthe 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.

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:

Run
npx services.dev jobs flow job_7Hq5   # api.forsale: a won deal, held to its window
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

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:

Run
npx services.dev jobs flow job_7Hs3 --debits   # api.forsale: a lost deal, with no charge to draw from
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

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:

Run
npx services.dev jobs flow job_7Hr1 --refunds   # a goodwill refund of api.lawyer's line
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
Run
npx services.dev jobs flow job_7Hn9 --disputes   # lost, on an attorney's own charge
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

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

services.dev eventAn authorizationA captured holdA mandate
charge.heldpayment_intent.amount_capturable_updatedpayment_intent.succeeded, at the order—
charge.mandated——setup_intent.succeeded
charge.authorization_expiringservices.dev's own clock, a day before capture_before——
charge.pending——payment_intent.processing: a bank debit in flight
charge.collectedpayment_intent.succeeded, at capturethe fact itself: the money already sits on services.dev's accountpayment_intent.succeeded
charge.failed——payment_intent.payment_failed, or requires_action on an off-session charge
charge.voidedpayment_intent.canceleda full refund from the holdpayment_intent.canceled
services.dev eventThe Stripe event behind it
charge.refundedcharge.refunded, with transfer.reversed once your share has moved
dispute.preventedradar.early_fraud_warning.created, then the refund
dispute.opened, dispute.closedcharge.dispute.created, charge.dispute.closed
pay.paidtransfer.created, to the member's account
payout.paid, payout.failedpayout.paid, payout.failed, on the account that paid out
pay.waitingaccount.updated, with requirements.currently_due
take.creditedtransfer.created: the month's lost-cost netting, returned to your account
subscription.renewed, subscription.unpaidinvoice.paid, invoice.payment_failed

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

Run
npx services.dev events --source stripe --job job_7Hq5   # each services.dev event, and the Stripe event behind it
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
KeyWrittenLeft outDoes
Brand.tax'stripe' | falsefalseCalculates tax on your charges with Stripe Tax and collects it as its own line
Brand.payouts{ rail: 'stripe'; schedule? }rail: 'stripe', dailyYour connected account, and how often it pays you: payouts
CodeWhen
pay_unfunded402 on an order: flat pay before any charge can't be drawn from your balance or your bank account
balance_negative402 on an order: your balance is negative after the draw order
held_pay_exposedcheck: a definition would draw on held share pay, a member's transfer or a member-rail charge

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

ValueWhat it makes happenAt Stripe
payment: 'decline'the charge declines, and dunning runspm_card_chargeDeclined
payment: 'auth_expired'the authorization nears its lapse, and the hold is capturedthe test clock past capture_before
payment: 'dispute'the buyer's bank opens a dispute after capturepm_card_createDispute
payment: 'early_warning'an early fraud warning arrives firsta test card that raises radar.early_fraud_warning.created
payment: 'bank'the charge is a bank debit, pending four days, then collectedus_bank_account, test routing 110000000
payout: 'fail'the member's next payout failstest bank account 000111111116
proof: 'paid'the outcome's payment lands at onceinvoice.paid on the buyer's test account
proof: 'refunded_day_20'the payment behind the outcome is refunded on day 20the 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:

Call
→ 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"}}}
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