Juicer Payouts
When a Juicer completes a drop cleanly, the platform queues a payout row. Payouts clear via Stripe Connect Express transfer to the Juicer's bank, typically within 24 to 48 hours of drop completion.
The Payout Row
Every completed session generates exactly one juicer_payouts row with:
session_id— the session that triggered the payoutjuicer_id— recipientamount_cents— net to Juicerplatform_fee_cents— Levy's cut (default 15%, configurable per subaccount)stripe_transfer_id— populated when the transfer succeedsstatus—queued,paid,on_hold,reversed, orcanceledscheduled_for— earliest time the payout can draincreated_at— when the bounty completed
How the Platform Fee Is Computed
| Component | Cents | Example ($5 base, 1.5x surge) |
|---|---|---|
| Bounty payout | gross | $7.50 |
| Platform fee | 15% | $1.13 |
| Net to Juicer | gross - fee | $6.37 |
The platform fee is configurable per subaccount. Lower fees subsidize the marketplace; higher fees take more margin. The default 15% is the post-pilot equilibrium.
A separate subaccount_settings toggle controls whether the platform fee is paid by the Juicer (default — fee comes off the Juicer's payout) or by the operator (fee added on top of the operator's bounty cost). Most operators use the default.
The Payout Cron
The juicer-payouts cron runs every 15 minutes. It:
- Reads every
juicer_payoutsrow wherestatus = 'queued'andscheduled_for <= now() - For each row, calls Stripe
transfers.createwith:amount = amount_centsdestination = juicer.stripe_account_ididempotency_key = juicer_payout_<id>
- On success, writes
status = 'paid'andstripe_transfer_id - On failure (insufficient balance, account not enabled, etc.), the row stays
queuedand the cron retries next cycle
The scheduled_for field defaults to 24 hours after the session completes — gives the operator a window to review and hold if anything looks off. Operators can set the default lower per subaccount (e.g., 1 hour) once they trust their Juicer roster.
When Payouts Are Held
A payout is created in on_hold state instead of queued when any of the following are true at session completion:
- The session has fraud flags (
fraud_flagsJSON is non-empty) - The Juicer has
lifetime_fraud_score >= 25 - The operator has
auto_hold_payouts = trueon their subaccount - Manual hold action by the operator
Held payouts don't drain automatically — they wait for an operator decision. From the dashboard:
- Release → moves
on_holdtoqueuedwith a freshscheduled_for - Cancel → moves to
canceled; never paid - Reverse (only for
paidrows) → calls Stripetransfers.createReversaland moves toreversed
Reversals can take 5–7 business days to actually pull funds from a Juicer's account; if the Juicer has spent the money, the reversal can fail and Levy carries the loss until the Juicer's next payout offsets it.
What Juicers See
Juicers see their payout history in the Juicer app under Earnings:
- Lifetime total earned
- Lifetime payouts paid
- Last 30 days breakdown
- Per-session detail with the bounty amount, platform fee, and net
- Stripe-backed transaction IDs for each paid transfer
If a payout is on hold or reversed, they see "Under review" with the operator's contact info. They never see the operator's internal note or fraud-score detail — just the high-level state.
Operator View
The operator dashboard at Dashboard → Swap → Payouts is the ledger of every payout for every Juicer in the subaccount:
| Column | Meaning |
|---|---|
| Date | When the session completed |
| Juicer | Name + email |
| Session | Click for the full claim → drop trail |
| Gross | Bounty payout pre-fee |
| Platform fee | Levy's cut |
| Net | What the Juicer received |
| State | queued / paid / on_hold / reversed / canceled |
| Stripe transfer | Click out to Stripe dashboard |
The page totals across the visible date range and supports CSV export for bookkeeping.
1099 / Year-End Tax
Stripe Connect Express generates and delivers the 1099-NEC to each Juicer at year-end based on the transfers Stripe processed during the year. The Juicer's legal name and SSN come from their Stripe Identity verification — they must match exactly or the 1099 is invalid.
Levy doesn't issue separate 1099s — Stripe handles the issuance. The operator's accounting needs to reconcile:
- Bounty cost — total gross paid (from the Payouts CSV export)
- Platform fee — total Levy received (also on the export)
- Net to Juicers — what Stripe transferred (matches Stripe's connect ledger)
If Stripe's records and Levy's records ever drift, contact support — the Stripe transfer is the source of truth and the platform row should match.
Refunds and Disputes
Disputed drops (the operator believes the vehicle wasn't actually charged or wasn't actually in zone) follow the hold → review → release/reverse path:
- Operator flags the session from the dashboard
- Payout moves to
on_holdif not yet paid, or stayspaidpending review - Operator reviews the photos, GPS, charge duration
- If valid drop → release / no action
- If invalid → reverse + add a fraud-score hit + optional Juicer ban
There's no Juicer-initiated dispute path today — Juicers contact the operator directly through the support email exposed on the Earnings page. Operator-initiated reversals are final and not appealed through the platform.
Common Issues
- Payout stuck in
queuedpastscheduled_for— the Stripe Connect account isn't fully enabled. Check the Juicer'sstripe_account_payouts_enabledflag and the Stripe Connect dashboard. - Transfer failed with "insufficient balance" — Levy's Stripe account doesn't have enough float. Usually transient; the cron retries.
- Juicer says they didn't get paid but
status = 'paid'— confirm the Stripe transfer ID and verify the destination account in Stripe. Sometimes the Juicer's bank takes 1–3 business days even after Stripe markspaid. - Reversal failed — Juicer has spent the money. Levy carries the loss; the next payout to that Juicer is reduced by the reversal amount until balanced.