Outcomes
Pricing on results, as one operating contract. The win rate a price breaks even at, who funds the work before the end, the proof that confirms the amount, how the money is collected, the window that holds it, what reverses it, and the tail that keeps a lost deal watched.
Outcome pricing pays you on results, never on a claim. The outcome names an amount, who proves it, how it's collected and how long it stays held.
Two lines on api.forsale's deal: the outcome and its proof, then a price that charges nothing on effort:
grep -n -A1 "outcome:\|price:" services/deal.ts49: outcome: { amount: 'closed_value', proof: 'payment', match: { customer: 'account', metadata: 'deal' },
50- window: { bank: '60d', card: '120d' }, tail: '90d', due: '90d', collect: job => (job.output.billing === 'monthly' ? 'each' : 'first') },
51: price: { usd: 0, share: '5%', of: 'closed_value', on: 'won', ai: 40,
52- lines: { referral: { share: '1%', of: 'closed_value', to: 'referrer', on: 'won' } } },check reads it back, down to the win rate it breaks even at:
npx services.dev check services/deal.tsservices/deal.ts · 42 lines · nothing on effort; 5% of closed_value at won, proven by the account's payment, held 60 days by bank, 120 by card.
outcome proof payment, read through services.dev's Stripe app on the seller's account · matched on the account's customer and metadata.deal · collect first, or each invoice when billing is monthly · due 90d · window 60d by bank, 120d by card, from each collection · tail 90d
after the window, a reversal of in_… is the seller's · the closer waits with the 5%
hold mandate · the seller's bank account or card, saved at the order up to max_usd · nothing held before won
ai $40.00 a deal, funded by api.forsale, won or lost · then ask
ledger +$11,100.00 at won on $74,000 · closer $7,400.00 (share, from the seller) · setter $250.00 · placement $50.00 · AI $11.34 (example) · you $3,388.66
lost +$0.00 · setter $250.00 when the meeting was held · placement $50.00, the setter's gig and the closer's · AI up to $40.00 · you −$340.00 when a closer loses it
odds a deal a closer loses costs $340.00, a won one leaves $3,388.66 · breaks even at 9.1% won (about 1 in 11)Under the seller's humans_from, AI closes alone, and the whole share is yours:
→ GET /owner/deals/job_7Hq1 # won by AI alone close ai · committed · closed_value $30,000.00 · under humans_from, so no closer
proof payment · in_…5520 on the seller's Stripe account · $30,000.00 paid 20 Oct 2026
ledger +$1,500.00 · AI $9.12 of $40.00 · no person, no placement · you $1,490.88 before the take
take on $1,500.00, the whole shareFrom $50,000, a closer takes it, and their 10% is charged to the seller beside your 5%:
→ GET /owner/deals/job_7Hq5 # won by a closer, and proven close person · closer · mbr_7Hq4 · committed · closed_value $74,000.00
proof payment · in_…8841, the seller's own invoice, matched on the account's customer and metadata.deal · $74,000.00 paid 4 Jan 2027, net 30 from the close on 4 Dec
outcome verified · $74,000.00 · window 60d from the collection · closes 9 Mar 2027
money outcome.verified → chg_7Hq5 $11,100.00 debited off-session on the seller's bank mandate → charge.pending 4 Jan → charge.collected 8 Jan → held under job_7Hq5
ledger +$11,100.00 · closer $7,400.00 (share, from the seller) · setter $250.00 · placement $50.00 · AI $11.34 of $40.00 · you $3,388.66 before the take
held the 5% $3,700.00, the closer's $7,400.00 and the take, until the window closesOn deal.ts you fund the $340 a lost deal costs, which the break-even rests on. If the seller funds the setter instead, the deal breaks even at 2.4%, and check fails effort_charged until price.effort: 'named' puts that charge first in the OFFER. Pay runs both.
outcome.proof names who confirms the amount. Every surface labels a verified outcome apart from a self-reported one:
proof | Confirmed by | Labelled |
|---|---|---|
'payment' | a payment on the buyer's own Stripe account, read at the order | verified |
'signature' | the counterparty's signature on the outcome | verified |
'counterparty' | the counterparty's confirmation through its own ask | verified |
'receipt' | an outside system's receipt that carries the amount | verified |
'self', or left out | your code's job.outcome({ amount, evidence }) | self-reported |
'payment' reads the buyer's Stripe account through services.dev's Stripe app, installed at the order. It watches that payment through the window, so its refund or dispute fires outcome.reversed by itself. A payment outside Stripe proves by 'receipt' or 'counterparty'.
A self-reported amount never sets anyone's money alone. The buyer sees it with its evidence and can contest it inside the window, and a contested amount stays held until a credentialed peer rules. No share pay rests on 'self':
→ GET /owner/jobs/job_7Hq2 # claim-appeal: an outcome your code reports outcome self-reported 9 Nov 2026 · $1,200.00 · job.outcome() with payment-notice.pdf
contest open to the buyer until 9 Dec 2026 · none filed
ledger +$99.00 at delivery · +$201.00 at outcome · AI $0.84 · you $299.16outcome.match ties one payment to one job: it comes from the customer party and carries the job's id under metadata. The end step commits an invoice drafted that way on the buyer's account, api.forsale's pay_link, so no other sale counts.
outcome.due bounds the wait, 90 days unless set; past it, outcome.lapsed ends the outcome with nothing charged. A party that already pays the buyer answers 409 account_is_customer, whose next orders with expansion: true: the share is of what the account adds. The sales money runs it.
At outcome.verified, the share and every pay charged from: 'buyer' are collected on the buyer's mandate, one charge per seller, and held under the job. For an amount paid over time, outcome.collect times the cash; price.on stays the end:
collect | Charged |
|---|---|
'first', the default | the whole share, at the first payment |
'each' | each payment's part, each held for its own window |
'full' | the whole share, once the amount is paid in full |
→ GET /owner/deals/job_7Ht1 # collect 'each': a $74,000.00 contract paid monthly proof payment · in_…T101 to in_…T112 on the seller's Stripe account, matched on the subscription
each $6,166.67 paid → $925.00 charged: the 5% $308.33 and the closer's $616.67, each held 60 days from its own payment
held $2,775.00 · final 11 Jan, 10 Feb and 12 Mar 2027 · 9 invoices to come
ledger each invoice +$925.00 · closer $616.67 · you $308.33 before the take · the first also carries the setter's $250.00, placement $50.00 and AI $11.34 · you $3,388.66 over the 12A declined collection retries under Brand.dunning. If it stays unpaid: the buyer's next orders answer 402, flat pay stands, a share charged to the buyer is still the member's at outcome.final, and the loss is yours, with no take on it:
→ GET /owner/deals/job_7Hs8 # won; the first collection was declined 18 Nov outcome.verified · chg_7Hs8 card declined, insufficient_funds · charge.failed
19 Nov ask_7Hs8 to the seller: update the card · Stripe retries on 19 and 20 Nov
21 Nov collected on the new card · charge.collected $9,300.00 · held under job_7Hs8
unpaid had the retries failed: outcome.unpaid on 2 Dec (Brand.dunning, 14 days) · the closer's $6,200.00 still theirs at outcome.final · the loss yours, with no take on it · the seller's next orders answer 402outcome.window holds what depends on the outcome until it closes: your share, any share pay, and services.dev's take on them. Its default is the horizon of the money behind the proof, and check fails window_too_long on a window past it:
| Proof | Window, unless set | The horizon it follows |
|---|---|---|
| A card payment | '120d' | how long a cardholder can dispute it |
| A bank payment | '60d' | how long a debit can be returned |
| A signature | '30d' | the counterparty's time to rescind |
| A receipt | '7d' | as any charge |
| Self-reported | '30d' | the buyer's time to contest |
window: { bank: '60d', card: '120d' } holds each payment for its own horizon. After the window, a reversal is the buyer's to carry. A member sees the window on the teaser before they claim:
→ GET /me/earnings/tsk_7Hq5 # as mbr_7Hq4, the closer, on closers.deals committed Fri 4 Dec 14:02 won, awaiting the payment deal.ended $0.00
proven Mon 4 Jan 09:20 won, proven by the payment outcome.verified $0.00
held Fri 8 Jan 09:00 the seller's debit collected charge.collected $7,400.00
accrues Tue 9 Mar 2027 when the 60-day window closes pay.accrued
payable Thu 11 Mar 2027 two days after pay.payable
paid Fri 12 Mar 2027 weekly, on the Pool's scheduleAt the window's close, the money is final and held pay accrues:
→ GET /owner/deals/job_7Hq5 # 9 Mar 2027: the window closed money outcome.final → charge.earned $11,100.00 → pay.accrued $7,400.00
ledger +$11,100.00 · closer $7,400.00 · setter $250.00 · placement $50.00 · AI $11.34 · you $3,388.66 before the takeHeld money moves your cash, not your margin. api.forsale's November pays its setters now and its 5% in January:
$ npx services.dev jobs ledger --month 2026-11 --held # api.forsale: paid on the close
api.forsale · November 2026 · 21 deals ended · 3 won, 18 lost
collected +$33,300.00 3 won deals: the 5% $11,100.00 and the closers' $22,200.00, by bank debit
held −$33,300.00 all of it, under each deal's job, final 12 Jan to 28 Jan 2027
processing −$15.00 $5.00 a bank debit
pay −$5,000.00 setters and nurturers, flat, won or lost
placement −$625.00 25 filled gigs on closers.deals
Held share pay reverses for one cause only: the outcome's own money going back inside the window, recorded as outcome.reversed:
→ GET /owner/deals/job_7Hw6 # another won deal: its payment was refunded inside the window proof payment · in_…9310 refunded 3 Dec 2026, day 20 of 60
money outcome.reversed → chg_7Hw6 refunded $11,100.00 from the hold → the closer's $7,400.00 never left it
ledger +$0.00 · setter $250.00, paid at the held meeting · placement $50.00 · AI $12.02 · you −$312.02
take reversed with the share| What happens | Your share | Held share pay | services.dev's take | Flat pay, placement |
|---|---|---|---|---|
| The outcome's money goes back inside the window | refunded to the buyer | reverses: it was held, never paid | reverses | stands |
| Part of it goes back inside the window | adjusts pro rata, outcome.adjusted | adjusts pro rata | adjusts | stands |
| Your goodwill refund of the share | refunded | stands, and accrues at outcome.final | reverses | stands |
| A dispute on another of your charges | nothing moves | nothing moves | nothing moves | stands |
| A reversal after the window | final | the member's | final | stands |
A referral share moves with yours, and flat pay is never clawed back. A partial refund moves every share line by one fraction:
→ GET /owner/deals/job_7Hs2 # won; the seller's customer was refunded $18,500.00 on day 31 proof payment · in_…2207 partly refunded 14 Dec 2026, day 31 of 60 · $74,000.00 → $55,500.00
money outcome.adjusted → chg_7Hs2 refunded $2,775.00 to the seller, pro rata
lines the 5% $3,700.00 → $2,775.00 · the closer's $7,400.00 → $5,550.00, still held · the take on $2,475.00
stands the setter's $250.00 and placement $50.00
ledger +$8,325.00 · closer $5,550.00 · setter $250.00 · placement $50.00 · AI $11.34 · you $2,463.66 before the takeoutcome.tail keeps a lost or withdrawn deal watched, 90 days unless set, so no one marks it lost and closes it around you. A payment from that account inside the tail verifies it won and pays the roles that worked it. A revival mints a new deal with its lineage and never charges twice:
→ GET /owner/deals/job_7Hw7 # marked lost 2 Dec; the account paid on 19 Dec ended lost on 2 Dec 2026, by the seller · watched for 90 days (outcome.tail)
proof payment · in_…3391 from Contoso Logistics, on the seller's Stripe account · $62,000.00 paid 19 Dec 2026
outcome verified by the tail · lost → won · the same deal, charged once
pay closer mbr_7Hq4, whose gig worked it · $6,200.00, held 60 days
ledger +$9,300.00 · closer $6,200.00 · setter $250.00 · placement $50.00 · AI $14.20 · you $2,785.80 before the take→ GET /owner/deals/job_7Hq5/outcome
{
"object": "outcome",
"job": "job_7Hq5",
"amount": { "field": "closed_value", "amount": 7400000, "currency": "usd" },
"proof": { "by": "payment", "ref": "in_…8841", "match": { "metadata": { "deal": "job_7Hq5" } }, "verified": true, "at": "2027-01-04T09:20:05Z" },
"collect": "first",
| Event | When |
|---|---|
outcome.reported | your code reported an amount: self-reported |
outcome.verified | the proof confirmed the amount; the share is collected and held |
outcome.contested | the buyer contested a self-reported amount; it stays held until a peer rules |
outcome.adjusted | part of the money behind it went back; each share line moved pro rata |
outcome.unpaid | the collection stayed declined when dunning ran out |
outcome.lapsed | no matching payment came by outcome.due; nothing is charged |
outcome.final | the window closed; held money is final and held pay accrues |
outcome.reversed | the money behind it went back inside the window |
| App | What it shows |
|---|---|
| Customer portal | The amount, its proof, the share charged, the date it's final, and the contest, open until the window closes on a self-reported amount |
| Delivery portal | A share as held, with its window's end, before the claim and after it, and the flat pay already paid |
| Owner console | Held outcome money by the date it's final, the live win rate against the break-even, and every reversal with its cause |
| Key | Written | Left out | Does |
|---|---|---|---|
outcome.amount | a money output | the price's of | The amount the share is of |
outcome.proof | 'payment' | 'signature' | 'counterparty' | 'receipt' | 'self' | 'self' | Who confirms it |
outcome.match | { customer?: <party input>; metadata?: string; invoice? } | the job's id under metadata.<your word for job> | How a payment proof finds its payment |
outcome.due | Within | '90d' | How long after the end the payment may come before the outcome lapses |
outcome.collect | 'first' | 'each' | 'full' | 'first' | When the share is charged on money paid over time |
outcome.window | Within | { card?; bank? } | the proof's horizon | How long outcome money stays held, from the charge's collection |
outcome.contest | 'buyer' | false | 'buyer' on 'self' | Who can contest a self-reported amount inside the window |
outcome.tail | Within | '90d' | How long a lost or withdrawn deal stays watched for its payment |
price.share, price.of, price.on | Pct, a money output, an end | none | The share, of what, at which end |
input.expansion | 'boolean?', declared on the Service | none | Takes the share on what a party that already pays adds |
price.usd | number | required | Charged at delivery and counted toward the share; 0 is nothing on effort |
price.ai | number | { usd; then } | a fifth of usd | The AI you fund per job: needed when usd is 0 |
| Code | When |
|---|---|
ai_unfunded | check: usd: 0 and no ai budget |
effort_charged | check: a usd: 0 price charges the buyer before its end, unnamed |
share_pay_unverified | check: share pay rests on a self-reported amount |
window_too_long | check: a window longer than its proof's horizon |
tail_unproven | check: a tail with a proof other than payment, signature or receipt |
account_is_customer | 409 on an order: the party already pays the buyer; next offers expansion: true |
proof_unconnected | deploy --live: a payment proof whose buyer hasn't installed services.dev's Stripe app |
win_rate_unmet | check, under margin: 'strict': the last 30 days won less often than the break-even |
fee_policy | check: a share your brand's fee policy forbids |
Outcome prices and flat pay at an end run live on Launch, and share pay, which scales with the deal, on Team. A share charges at most the buyer's max_usd.