Settlement Timing Policy — InferLane Exchange Phase 1.5.1
§1 Purpose
§1.1 What this document does
This policy defines the timing windows, state transitions, and money-movement cadence governing settlement of third-party compute provider earnings on the InferLane Exchange platform during Phase 1.5.1 (the first phase in which non-anchor providers receive funds).
It is the operational companion to commercial/legal/LEGAL-PERIMETER.md §4 ("Settlement perimeter and the non-fiduciary posture"). Where this document and LEGAL-PERIMETER.md disagree, LEGAL-PERIMETER.md wins — that document is the legal posture; this document is the implementation contract.
§1.2 What this document does NOT do
- It does not govern anchor-provider settlement. Anchor providers (isAnchor = TRUE) are billed directly by the platform under a contractor agreement and do not transit the settlement state machine described here.
- It does not govern buyer-side wallet mechanics. See
marketplace_compute_walletschema + paired-ledger trigger (DF19) for buyer fund mechanics. - It does not govern dispute adjudication beyond the auto-resolve threshold (§5.4 of LEGAL-PERIMETER.md). The $5 disputed-auto-resolve rule is referenced here but defined there.
- It does not authorize fund movement absent the
STRIPE_BOND_ESCROW_WIREDenv flag. Every state transition listed below assumes the kill-switch is enabled. If the flag is FALSE, the state machine is read-only.
§1.3 Phase scope
This policy governs Phase 1.5.1 only (US-only providers, DB-level country = 'US' enforcement, Stripe Connect Express as custodian). Phase 1.5.2+ (non-US providers, batched daily transfers, anchor-bond mechanics) will require a new policy revision and is OUT of scope here.
§1.4 Founder-readable summary
In one sentence: a third-party provider's earnings sit in a Stripe-held balance from the moment compute is delivered until either the audit grace window expires or an explicit pass verdict lands, at which point Stripe pushes the net (gross − 4% platform fee − Stripe transfer fee) directly to the provider's Stripe Express account. The platform never custodies fiat. Day 30 is the hard outer envelope: any settlement still un-paid at day 30 is force-clawed-back and refunded to the buyer.
§2 Vocabulary — Settlement, not Escrow
§2.1 The terminology rule
On the provider side of the platform, in every user-facing surface, every legal document, every UI string, every email, every API field name, and every database column, the operative noun is settlement and the operative verb is to settle. The word escrow is forbidden.
The two exceptions:
- The env flag
STRIPE_BOND_ESCROW_WIREDretains the historical name because renaming a production env flag mid-deployment introduces operational risk disproportionate to the marginal legal benefit. This flag is internal-only and never surfaced to users. - Internal engineering comments in code files may refer to "escrow-like behavior" when describing the Stripe-held balance, provided the comment is not in a string literal that reaches a user, log line, audit export, or API response.
§2.2 Why this matters (the legal mechanic)
The word "escrow," in US state money-transmission law and in common-law usage, implies a neutral third party holding funds for the benefit of two principals, with a fiduciary duty to release per agreed conditions. That description fits the statutory definition of money transmission in the majority of US states (see FinCEN 31 CFR 1010.100(ff)(5) for the federal definition; state definitions are typically broader).
The platform is not a neutral holder. The platform is a marketplace operator that bills the buyer, takes a 4% platform fee, and instructs Stripe Connect to pay the provider. Stripe is the custodian (Stripe holds the Money Transmitter Licenses in 49 US states; the platform does not). The platform's posture is: we provide a marketplace; Stripe handles the money; we never touch fiat.
The word "settlement," by contrast, describes the standard B2B contractor model: work completed → invoice payable → paid per terms. Net-30, net-15, net-7 are all "settlement" terms. A 1-hour-to-7-day "settlement window" is well within the range of normal commercial settlement practice. No statute construes "we pay our contractors on a 24-hour cycle after delivery" as money transmission.
The vocabulary is therefore the cheapest single mitigation for the CA-DFPI and FinCEN state-MTL exposure flagged in LEGAL-PERIMETER.md §7. We pay zero to implement it; it shifts the posture from "we hold money for two parties" (regulated) to "we pay our contractors on a short cycle" (not regulated). It must be enforced before any data exists in production using the wrong word.
§2.3 Enforcement
The CI pipeline includes a vocabulary linter (scripts/vocab_lint.py) that fails the build on any provider-facing surface containing the string escrow (case-insensitive, word-boundary). Allowlist:
| File / Pattern | Reason | |---|---| | .env* | env flag name retention | | commercial/legal/SETTLEMENT_TIMING_POLICY.md | this section | | commercial/legal/LEGAL-PERIMETER.md | discusses the legal mechanic explicitly | | **/migrations/*_rename_escrow_to_settlement.sql | rename migration audit trail |
Any new file matching the disallowed pattern fails CI and blocks PR merge.
§2.4 What providers see
Provider-facing terms ("Earnings", "Pending settlement", "Settled", "Held for audit", "Clawed back") are listed in §3 alongside their internal state names. The internal state names use UPPERCASE_SNAKE_CASE for grep-ability; the UI strings use sentence case.
§3 Settlement State Machine
§3.1 States
| State | Meaning | UI label (provider) | UI label (buyer) | |---|---|---|---| | RESERVED | Buyer wallet has been debited; provider has not yet delivered compute. Funds sit in the buyer's Stripe customer balance, earmarked for this invocation. | (not shown) | "Reserved" | | HELD_FOR_AUDIT | Compute delivered. Audit grace window is running (§4). Funds in Stripe platform balance under transfer_group. | "Pending settlement" | "Awaiting confirmation" | | SETTLEMENT_DUE | Audit grace expired OR explicit pass verdict. Eligible for Stripe Transfer to provider's Express account on next worker tick. | "Ready to pay" | "Confirmed" | | SETTLED | Stripe Transfer succeeded. Money is in provider's Express account. Terminal happy-path state. | "Paid" | "Complete" | | CLAWED_BACK | Audit returned a fail verdict OR the 30-day max-hold envelope (§5) tripped. Buyer refunded; provider receives $0 for this invocation. Terminal sad-path state. | "Reversed" | "Refunded" | | VOIDED | Pre-delivery cancellation. Buyer wallet hold released. Provider never delivered, never earned, nothing owed. Terminal. | (not shown) | "Cancelled" | | PAYOUT_FAILED | Stripe Transfer attempted from SETTLEMENT_DUE but failed (provider account restricted, deauthorized, requirements outstanding, etc.). Recoverable. | "Payout failed — action needed" | "Confirmed" | | DISPUTED | Buyer filed a dispute within the audit window (or post-settle within 7d). Held pending resolution per §5.4 of LEGAL-PERIMETER.md. | "Disputed" | "Disputed" |
§3.2 Transition diagram (text form)
`` [buyer wallet debited] | v +----------+ | RESERVED | +----------+ | +----------------+----------------+ | | | [provider delivers] [provider fails] [buyer cancels | [or 5xx/timeout] pre-delivery] v | | +-----------------+ v v | HELD_FOR_AUDIT | +-------------+ +--------+ +-----------------+ | CLAWED_BACK | | VOIDED | | +-------------+ +--------+ +---------+---------+ | | | [grace [explicit [buyer expires] pass] disputes] | | | v v v +----------------+ +----------+ | SETTLEMENT_DUE | | DISPUTED | +----------------+ +----------+ | | +---------+---------+ +----+----+ | | | | [Stripe [Stripe [resolved [resolved Transfer ok] fails] for for buyer] | | provider] | v v | v +---------+ +----------------+| +-------------+ | SETTLED | | PAYOUT_FAILED || | CLAWED_BACK | +---------+ +----------------+| +-------------+ | v [retry ok] +----------------+ | | SETTLEMENT_DUE | v +----------------+ +---------+ | | SETTLED |<-----+ +---------+ ``
§3.3 Valid transitions (authoritative table)
| From | To | Trigger | Side-effects | |---|---|---|---| | RESERVED | HELD_FOR_AUDIT | Provider delivery callback received (200 from provider, response signature verified) | Stripe transfer_group created; HELD_AT timestamp set | | RESERVED | VOIDED | Buyer cancels before delivery OR delivery timeout (provider 5xx or no response within 60s) | Release Stripe customer hold; emit wallet_credit reversal | | HELD_FOR_AUDIT | SETTLEMENT_DUE | Audit grace window expired (§4) OR explicit pass verdict from audit pipeline | DUE_AT timestamp set; worker picks up on next tick | | HELD_FOR_AUDIT | CLAWED_BACK | Explicit fail verdict from audit pipeline | Buyer refund issued; emit wallet_credit to buyer; CLAWED_AT set | | HELD_FOR_AUDIT | DISPUTED | Buyer files dispute via dispute endpoint | DISPUTED_AT set; audit grace clock paused | | SETTLEMENT_DUE | SETTLED | Stripe Transfer API returns succeeded | SETTLED_AT set; Stripe transfer_id stored | | SETTLEMENT_DUE | PAYOUT_FAILED | Stripe Transfer API returns error OR async failure webhook received | PAYOUT_FAILED_AT set; failure reason stored; founder notified | | PAYOUT_FAILED | SETTLEMENT_DUE | Retry job (manual or automated) | retry_count incremented (max 5) | | PAYOUT_FAILED | CLAWED_BACK | Retry exhausted (5 attempts over 5 days) OR provider account permanently deauthorized | Buyer refunded; founder notified | | DISPUTED | SETTLEMENT_DUE | Dispute resolved provider-favorable (incl. $5-auto-resolve per LEGAL-PERIMETER.md §5.4) | Resume normal settle path | | DISPUTED | CLAWED_BACK | Dispute resolved buyer-favorable | Buyer refunded | | * (any non-terminal) | CLAWED_BACK | 30-day max-hold envelope tripped (§5) | Forced. Buyer refunded. Logged as force_clawback_30d. |
§3.4 Invalid transitions (explicitly forbidden)
The state machine MUST reject the following with a forbidden_transition error logged to the settlement audit table:
| From | To | Why forbidden | |---|---|---| | SETTLED | | Terminal. Money already with provider. Reversals only via Stripe Refund + new buyer wallet credit (separate flow). | | CLAWED_BACK | | Terminal. Buyer already refunded. | | VOIDED | * | Terminal. Never delivered. | | RESERVED | SETTLEMENT_DUE | Cannot skip audit window. Delivery must precede settlement-eligibility. | | RESERVED | SETTLED | Same. | | HELD_FOR_AUDIT | SETTLED | Same — must transit SETTLEMENT_DUE so reconciliation (§8) has a checkpoint. | | PAYOUT_FAILED | SETTLED | Cannot skip the retry-then-settle path. Recovery must re-enter SETTLEMENT_DUE so a fresh Stripe Transfer attempt creates a fresh transfer_id. |
§3.5 Implementation note
State transitions are implemented in marketplace_settlement_state.py as a single transition(from, to, reason, actor) function that:
- Validates the transition against §3.3.
- Acquires a row-level lock on
marketplace_settlementfor the affected row. - Writes the transition to
marketplace_settlement_audit(immutable append-only). - Updates the state column.
- Releases the lock.
No other code path may mutate marketplace_settlement.state directly. The DB enforces this via a CHECK constraint and a BEFORE UPDATE trigger that compares OLD.state, NEW.state against the allowed-transitions table.
§4 Audit Grace Windows
§4.1 The three tiers
The audit grace window is the period between HELD_FOR_AUDIT entry and automatic transition to SETTLEMENT_DUE in the absence of an explicit verdict.
| Tier | Grace window | Applies to | |---|---|---| | L1 | 1 hour | Synchronous low-stakes invocations: classification, extraction, simple Q&A, summarization. Default for invocations < $0.50 worth of compute. | | L2 | 24 hours | Standard invocations: code generation, multi-step reasoning, content generation. Default for invocations $0.50–$5.00. | | L3 | 7 days | High-stakes / high-value invocations: agentic chains, multi-tool workflows, anything > $5.00 OR explicitly flagged high_stakes = TRUE by the buyer. |
The tier is determined at HELD_FOR_AUDIT entry by:
- Buyer-provided
audit_tier_overrideif present and ≥ system-default for that invocation cost. - Otherwise system default by invocation cost bracket.
Buyers may lengthen the grace window via override (e.g. force L3 on a $0.30 invocation to enable longer dispute time). Buyers MAY NOT shorten it below the system default. The system default is the floor.
§4.2 What runs during the grace window
The audit pipeline (defined in commercial/operations/AUDIT_PIPELINE.md) runs the following checks during the grace window:
- Response schema validation (instant)
- Output-toxicity classification (instant)
- Sampled human review for high-stakes tier (asynchronous, may take days)
- Buyer-initiated dispute window (open for the entire grace period)
The grace window expires when the wall clock passes (HELD_AT + grace_duration), regardless of whether the audit pipeline has emitted a verdict. Absence of a fail verdict is treated as pass-by-default.
§4.3 Explicit pass / fail verdicts
A verdict from the audit pipeline short-circuits the timer:
pass→ transitionHELD_FOR_AUDIT→SETTLEMENT_DUEimmediately.fail→ transitionHELD_FOR_AUDIT→CLAWED_BACKimmediately; buyer refunded.
Verdicts emitted after the grace window has expired and the row is already in SETTLEMENT_DUE or SETTLED are logged but DO NOT cause retroactive state changes. The grace window is the canonical decision boundary.
§4.4 Clock pausing
The grace clock pauses only when the row enters DISPUTED. On DISPUTED → SETTLEMENT_DUE (provider-favorable resolution), the clock does NOT resume — the row goes directly to SETTLEMENT_DUE. Reasoning: by the time a dispute is resolved, the audit signal is no longer time-sensitive; the dispute resolution IS the verdict.
§4.5 Why these specific numbers
| Tier | Grace | Rationale | |---|---|---| | L1 | 1h | Long enough for buyer to notice obvious failure on a real-time use case (chatbot UI); short enough that providers see fast settlement and trust the platform. | | L2 | 24h | Matches typical SaaS dispute-window expectations. Long enough for batch buyers to spot-check overnight runs. | | L3 | 7d | Aligns with PCI / chargeback windows on the buyer side and gives time for human review on agentic chains where output quality is non-obvious. |
These numbers are tunable per-provider via MarketplaceComputeProvider.audit_tier_overrides but the platform-wide default applies absent explicit override.
§5 Thirty-Day Maximum Hold Envelope
§5.1 The rule
No settlement row may remain in any non-terminal state for more than 30 calendar days from RESERVED_AT. At day 30, the row is force-transitioned to CLAWED_BACK regardless of audit state, dispute state, payout-retry state, or provider availability. The buyer is fully refunded.
This is the outer envelope referenced in LEGAL-PERIMETER.md §4.3 and is not negotiable per-provider, per-buyer, or per-invocation.
§5.2 Why 30 days
US state money-transmission statutes typically treat "held funds for longer than X days" as one of several indicia of money transmission. The specific X varies by state (some are silent; California's DFPI guidance is unsettled; New York's Department of Financial Services has informally cited 30 days in unrelated contexts). 30 days is the conservative ceiling — beyond it, the "we're just paying our contractors on a short cycle" framing in §2 becomes untenable.
The 30-day envelope also bounds the platform's reconciliation surface: at any given moment, the SUM of non-terminal settlement rows is bounded by 30 days of platform GMV × (1 − immediate-settle-rate). This is operationally crucial for §8 reconciliation.
§5.3 Implementation
A daily cron (force_clawback_30d.py) runs at 04:00 UTC and:
- Selects all rows where
state NOT IN ('SETTLED', 'CLAWED_BACK', 'VOIDED')ANDreserved_at < NOW() - INTERVAL '30 days'. - For each row, calls
transition(state, 'CLAWED_BACK', reason='force_clawback_30d', actor='system'). - Issues the buyer refund (see §7).
- Emails the founder a summary with row IDs, providers, dollar values, and the state each row was force-clawed-back FROM.
§5.4 Operational expectation
In a healthy platform, force_clawback_30d should fire ZERO rows on any given day. A non-zero result is an incident — it means something upstream is stuck (audit pipeline backlog, provider payout failure with no retry, dispute that's been DISPUTED for 30 days). The founder email is the alert.
Provider trust expectation: providers will see a 30-day clawback as a platform failure (not an audit fail), so we MUST proactively notify the affected provider with reason = "Platform was unable to complete settlement within 30 days; this invocation has been reversed. This was not an audit finding." and offer (where appropriate) a goodwill credit on next invocation.
§6 Per-Invocation Transfer Cadence
§6.1 The decision
Phase 1.5.1 ships per-invocation Stripe Transfers — not batched. Each SETTLEMENT_DUE → SETTLED transition triggers a single Stripe Transfer to the provider's Connect Express account.
§6.2 Why not batched
Daily batched transfers (the obvious cost-optimization) introduce two real problems:
- Reconciliation ambiguity. A batched transfer of
$Xcovering N invocations is harder to reconcile against per-invocation settlement records than N matched 1-to-1 transfers. Stripe'stransfer_groupfield gives us the join key when we go 1-to-1. - Force-clawback complications. If a batched transfer of $100 covering 50 invocations has just gone out, and 3 of those invocations were later disputed, we'd need to net-deduct the disputed amounts from the next batch — which is fine until the provider has no next batch (low activity, deauthorization, etc.). Per-invocation transfers + per-invocation clawbacks keep the books symmetric.
§6.3 The cost
Stripe charges per-transfer (currently $0.25 per Connect Express transfer in the US). At a $0.50 minimum invocation cost (§6.4) and 4% platform fee, the per-invocation economics are tight. See commercial/finance/PHASE_1_5_1_UNIT_ECONOMICS.md for the spreadsheet.
The decision was made eyes-open: we eat the per-transfer cost in Phase 1.5.1 in exchange for clean reconciliation and a defensible audit story. Phase 1.5.2 will revisit batched transfers once we have 30 days of clean reconciliation history.
§6.4 Minimum invocation cost
Invocations costing less than $0.50 (provider gross) are rejected at the routing layer. Reasoning:
- Stripe transfer fee is $0.25.
- 4% platform fee on a $0.50 gross = $0.02.
- Provider net = $0.50 − $0.02 − $0.25 = $0.23 on a $0.50 invocation.
- Below $0.50, provider net approaches or goes negative, which would generate provider complaints and is operationally embarrassing.
The minimum is enforced in:
- The routing layer (
route_via_platform) — rejects withinvocation_below_minimumbefore any Stripe call. - The DB CHECK constraint on
marketplace_settlement.gross_amount_cents >= 50.
§6.5 Transfer mechanics
When SETTLEMENT_DUE → SETTLED:
- Compute net:
net = gross − floor(gross × 0.04) − stripe_transfer_fee_cents. - Call Stripe
transfers.create({amount: net, destination: provider.stripe_express_id, transfer_group: settlement.id, metadata: {...}}). - On success: store
transfer_id, transition toSETTLED. - On failure: transition to
PAYOUT_FAILED, store failure code + message.
The Stripe API surface used: POST /v1/transfers (idempotent via Idempotency-Key header, see PR3). NOT POST /v1/payouts — payouts are Stripe → bank, governed by Stripe's own schedule. Transfers are platform-balance → connected-account balance, instant, and what we control.
§6.6 Provider sees the money when?
Per Stripe Connect Express defaults: funds land in the provider's Express balance within seconds of a successful transfer call. Provider-initiated payouts to their bank follow Stripe's standard 2-day rolling schedule (configurable by the provider in their Express dashboard, NOT by us). We make no commitment to provider regarding bank-arrival timing — only that we Transfer per the settlement state machine.
§7 Buyer-Side Refund Mechanics
§7.1 When refunds happen
Refunds to the buyer are triggered on these state transitions:
| Transition | Refund amount | |---|---| | RESERVED → VOIDED | Full reservation amount (no compute delivered, no fees taken) | | HELD_FOR_AUDIT → CLAWED_BACK | Full reservation amount (compute delivered but failed audit; platform absorbs the wash) | | DISPUTED → CLAWED_BACK | Full reservation amount | | PAYOUT_FAILED → CLAWED_BACK | Full reservation amount (provider unable to receive; buyer made whole) | | * → CLAWED_BACK via 30-day force | Full reservation amount |
§7.2 The mechanic
Refunds credit the buyer's marketplace_compute_wallet balance, NOT the buyer's original payment instrument. Reasoning:
- The buyer pre-funded the wallet via a Stripe Customer payment that has long since cleared.
- Restoring wallet balance is instant, idempotent, and reversible.
- Refunding to original payment instrument would incur a second Stripe processing fee and add operational complexity (which card? what if the card is gone?).
The wallet credit is implemented via the existing wallet_credit ledger entry, paired by the DF19 trigger to a settlement_clawback debit, preserving the paired-ledger invariant.
§7.3 The exception: buyer-requested cash-out
A buyer may request that their wallet balance be cashed out to their original payment instrument under the conditions defined in commercial/legal/BUYER_TERMS.md §6 (refund policy). That is a separate flow — settlement clawback always credits the wallet first.
§7.4 Platform fee on clawback
When a settlement is clawed back, the buyer is refunded the full gross, NOT gross-minus-platform-fee. The platform eats the 4% fee on clawed-back invocations. Reasoning:
- Charging a platform fee on a transaction we just clawed back is operationally indefensible — the buyer didn't get the service.
- The 4% fee was never taken — it sits in the platform Stripe balance from the moment we transit
HELD_FOR_AUDIT → SETTLEMENT_DUE. On clawback we simply don't keep it. - Provider-side: the provider also gets $0 on clawback. Symmetric. Both parties zeroed out.
Edge case: if the platform has already transferred to the provider (state was SETTLED) and a post-settle issue arises, the buyer-refund-from-wallet flow still applies, but the platform absorbs the loss as bad debt. The platform does NOT chase the provider for clawback post-SETTLED. This is the cost of having a fast settlement cycle and is bounded by audit grace windows (§4).
§8 Reconciliation
§8.1 The invariant
For every provider P, at any reconciliation checkpoint, the following must hold within tolerance:
`` SUM(marketplace_settlement.net_amount_cents WHERE provider_id = P AND state = 'SETTLED') == SUM(stripe_balance_transactions.amount WHERE type = 'transfer' AND transfer_group LIKE 'ms_%' AND destination = P.stripe_express_id) ``
Tolerance: $0.01 (one cent) absolute difference, to accommodate Stripe rounding edge cases in fee calculations.
§8.2 The cron
reconcile_settlements.py runs nightly at 03:00 UTC and:
- Fetches all SETTLED rows from
marketplace_settlementfor the prior 24h (with a 48h overlap window to catch late-arriving transactions). - Fetches all Stripe balance transactions of type
transferwithtransfer_groupprefixms_for the prior 48h. - Joins by
transfer_id(preferred) or bytransfer_group(fallback for any transfer_id-less edge case). - For each provider, computes the SUM on both sides.
- If absolute drift > $0.01: emails the founder with provider, drift amount, sample row IDs, and the suspect transfer IDs.
§8.3 Drift triage protocol
A drift email is a STOP-THE-LINE event. Triage protocol:
- First: do not modify
marketplace_settlementuntil the source of drift is identified. Adjusting our books to match Stripe's hides bugs. - Check the Stripe Dashboard for unmatched transfers in either direction.
- Check
marketplace_settlement_auditfor unusual transition patterns in the prior 48h. - Check Stripe webhooks log for failed/replayed transfer events.
- Once cause identified: apply a correcting entry via the standard
transition()function (NOT a raw UPDATE). - Document the incident in
commercial/operations/incident_log.md.
§8.4 Why the prefix ms_
All transfer_group values for marketplace settlements use the prefix ms_ followed by the settlement row's UUID: e.g. ms_8f3a1b2c-.... This prefix exists so that any platform-side Stripe transfers UNRELATED to marketplace settlement (refunds, anchor-provider payments, bond-escrow movements, etc.) are trivially filterable out of the reconciliation join.
The prefix MUST be set on the original transfers.create call. Stripe does not allow modifying transfer_group after creation.
§8.5 Reconciliation across providers, not just per-provider
A secondary check confirms that:
`` SUM(marketplace_settlement.platform_fee_cents WHERE state = 'SETTLED') + SUM(marketplace_settlement.net_amount_cents WHERE state = 'SETTLED') + SUM(marketplace_settlement.stripe_fee_cents WHERE state = 'SETTLED') == SUM(marketplace_settlement.gross_amount_cents WHERE state = 'SETTLED') ``
This is internal arithmetic, not a Stripe comparison, and is enforced as a CHECK constraint at row-insert time — but the cron re-checks the aggregate as a defense against silent corruption.
§9 Replicated Mode and Partial-Replica Payout Semantics
§9.1 The replicated execution model
For redundancy on high-stakes invocations (typically L3 tier), the routing layer may dispatch the same inference job to N providers in parallel ("replicated mode"). The buyer receives the majority-quorum response. The minority replicas have nonetheless delivered a good-faith service.
§9.2 The payout rule
Each replica is paid in FULL on good-faith service, regardless of whether its response was selected as the majority quorum.
A replica is considered to have delivered good-faith service if and only if:
- It returned a syntactically valid response matching the requested schema.
- It returned within the buyer-specified timeout window.
- It did NOT return a 5xx error or transport-layer failure.
If any of those three conditions fails, the replica enters CLAWED_BACK for that invocation. Otherwise it transits the normal state machine and gets paid.
§9.3 Rationale (why not pay only the winner)
Three reasons we pay all good-faith replicas:
- Provider acquisition. A provider who completes a job correctly and gets $0 because another provider's output was selected will not return to the platform. We need provider supply to compete with AWS pricing; pay-per-attempt is the model providers expect from comparable platforms (Anyscale, Together, Modal don't have replicated mode but their per-call pricing implies it).
- Honest accounting. The platform DID receive useful compute from each good-faith replica. Stiffing them on the theory that "the buyer didn't use it" is the kind of platform behavior that triggers reputational decay quickly.
- Audit signal preservation. Minority replicas are valuable as audit signal (disagreement among replicas is a red flag worth surfacing to the buyer). If we don't pay them, providers will refuse replicated jobs, and we lose the audit signal.
§9.4 What buyers pay in replicated mode
Buyers pay N × invocation cost for an N-replica dispatch (plus the per-invocation platform fee on each). This is disclosed up front in the routing UI; replicated mode is opt-in and explicit. Buyers who don't want N × cost don't enable replicated mode.
§9.5 Schema-fail / 5xx / timeout — the only refusal grounds
The strict rule: the ONLY grounds to refuse payment to a replica that responded are:
| Failure mode | Detection | |---|---| | Schema fail | Response doesn't deserialize against the requested schema OR fails the provider's declared output contract. | | 5xx | Transport returned a 5xx HTTP status. | | Timeout | No response within buyer-specified timeout (default: 60s synchronous, 24h batch). |
A response that is substantively wrong but syntactically valid is paid in full. The audit pipeline can flag it as low-quality (affecting provider rating), but the invocation is paid. We do not adjudicate quality at the settlement layer.
§10 Known Risks
§10.1 Stripe rate limits
Stripe's documented rate limits on transfers.create are 100 RPS in live mode. At Phase 1.5.1 scale this is non-binding. As volume grows, two failure modes:
- Burst saturation: a sudden flood of
SETTLEMENT_DUErows (e.g. cron tick after a large batch completion) could exceed 100 RPS briefly. - Provider-side limits: Stripe also rate-limits per-connected-account; a single very active provider could hit account limits.
Mitigation: the settle-worker enforces a 50 RPS soft cap with exponential backoff on 429. Sustained 429 emails the founder. There is no automatic escalation to higher Stripe rate limits — that requires manual Stripe support contact.
§10.2 Stripe Connect deauthorization mid-flight
A provider may deauthorize the platform's Connect access at any time (Stripe gives them a one-click in the Express dashboard). Failure modes:
- Pre-transfer deauth: settlement is in
SETTLEMENT_DUE; transfer fails withaccount_invalidor similar. We transit toPAYOUT_FAILEDand retry. After 5 retries OR detection of permanent deauthorization, transit toCLAWED_BACKand refund buyer. - Post-transfer deauth: settlement is
SETTLED. The money is already in the provider's Express balance. Whether they can withdraw it depends on their bank-linkage state with Stripe — this is between them and Stripe and outside our control. We do not claw back post-SETTLED.
The deauthorization webhook (account.application.deauthorized) is monitored. On receipt, the platform:
- Sets
MarketplaceComputeProvider.routable = FALSEimmediately (no new invocations routed). - Force-fails any
SETTLEMENT_DUErows for that provider through thePAYOUT_FAILED → CLAWED_BACKpath on next worker tick. - Emails the founder.
§10.3 Partial-replica failure semantics
If a replicated dispatch (§9) experiences a mix of success and failure across replicas, the platform must distinguish:
- A replica that DELIVERED a valid response: paid in full, regardless of quorum outcome.
- A replica that FAILED schema/5xx/timeout: clawed back.
Edge case: if N-1 of N replicas fail, the buyer may still receive a valid response from the surviving replica, but the buyer's confidence in that response is low (no quorum). The platform surfaces this to the buyer ("returned without quorum — N-1 replicas failed") but still bills the buyer for the surviving replica. The buyer may dispute under normal dispute rules.
Edge case: if ALL replicas fail, the invocation is treated as a failed delivery; all replica reservations go RESERVED → VOIDED and the buyer is refunded for all N reservations.
§10.4 Stripe webhook reliability
Settlement state transitions depend on async Stripe webhooks (transfer success/failure, account events). Stripe webhook delivery is best-effort, not guaranteed.
Mitigation: every state transition that depends on a Stripe webhook is backstopped by a polling worker (settle_state_polling.py) that runs every 5 minutes and queries Stripe's REST API for the canonical state. Stripe is the source of truth; webhooks are an optimization.
§10.5 Provider account requirements drift
A provider's Stripe Connect Express account may have outstanding requirements (additional KYC docs, address verification, etc.). Stripe enforces these by failing transfers with a specific error code. The platform surfaces "Action needed" on the provider dashboard and emails the provider; if requirements remain outstanding for 14 days, we set routable = FALSE and let outstanding settlements time-out into the 30-day force-clawback (§5).
§10.6 Foreign-jurisdiction provider attempt
A US-only enforcement at the DB level (MarketplaceComputeProvider.country = 'US' CHECK constraint) backstops the Stripe Connect Express country requirement. A provider who attempts to onboard with a non-US Stripe account will fail at the Stripe step; even if they spoof past that, the DB check refuses the insert. This is belt-and-suspenders for the FATCA / international payments exposure flagged in LEGAL-PERIMETER.md §8.
§10.7 CA-DFPI as the highest residual exposure
The single largest known unmitigated risk in this policy is the California DFPI's evolving interpretation of marketplace-payment platforms. The rolling-30d California-originated revenue dashboard required by LEGAL-PERIMETER.md must be wired before this policy ships. Red banner at $7.5K MRR (CA-originated, rolling 30d) and mandatory paid-counsel opinion at $10K MRR are operational gates ON the policy, not separate concerns.
If counsel returns an unfavorable opinion at $10K MRR, the operational response is: pause new CA-provider onboarding, hold CA-provider settlement queue, and consult before any further state action. This is a known kill-scenario for the policy and the founder should be mentally prepared for it.
§10.8 What this policy explicitly does NOT mitigate
- Bank-failure risk on the provider side (provider's bank goes under between Stripe Transfer and bank settlement — not our problem, but providers may blame us).
- Stripe Connect product changes (Stripe may modify Connect Express terms or pricing — we have no contractual stability with Stripe beyond Stripe's standard terms).
- Buyer-side fraud (stolen-card chargebacks creating retroactive buyer-wallet shortfalls — covered separately in
commercial/legal/BUYER_FRAUD_POLICY.md). - Provider tax obligations (1099-K reporting is Stripe's responsibility per Stripe Connect Express terms; the platform does not issue 1099s).
§10.9 Policy revision triggers
This policy MUST be revised (new version, founder sign-off, and rollout plan) on any of the following:
| Trigger | Action | |---|---| | Stripe modifies Connect Express terms in any material way | Revisit §6, §10.1, §10.4 | | CA-DFPI publishes formal guidance on marketplace settlement | Revisit §2, §5, §10.7 | | FinCEN publishes guidance touching marketplace-operator definition | Revisit §1.1, §2.2 | | First state MTL inquiry letter received | STOP-THE-LINE; founder + counsel review of entire policy | | Phase 1.5.2 launch (non-US providers, batched transfers, or anchor-bond changes) | Full revision required | | Reconciliation drift incident (§8.3) | Post-mortem; revise §8 if root cause is policy-level | | First force-clawback-30d (§5.3) firing on a real customer | Post-mortem; revise §4 and §5 if needed |
End of SETTLEMENT_TIMING_POLICY.md v1.0.0.