Stripe live-mode cutover runbook

When Mercury account clears (~Monday 2026-05-04 / Tuesday 2026-05-05) and we're ready to flip to live mode, follow these steps. Sandbox-mode setup completed 2026-05-03 and the live-mode setup follows the same pattern with new IDs.

Pre-flight (Mercury-side)

Stripe live-mode setup (~30 min keyboard time)

  1. Toggle Stripe to live mode in dashboard (Test → Live switch top-right)
  2. Run setup script at /Users/lukeolson/discord-bot/_scripts/stripe_live_setup.py (TODO: build by porting the API calls from this conversation, replacing STRIPE_SECRET_KEY_TEST with STRIPE_SECRET_KEY_LIVE):
  3. Creates HF Floor product + $9,900/mo recurring price
  4. Creates HF Overage product + graduated tiered metered price
  5. Creates hf_completed_appeal Billing Meter
  6. Outputs new live-mode IDs to stripe_hf_objects_live.json
  7. Connect Mercury as payout destination:
  8. Stripe → Settings → External payouts → Add bank account
  9. Plaid OAuth (search "Mercury") OR manual routing + account number from Mercury dashboard
  10. Verify via micro-deposits (~1-2 business days)
  11. Configure live webhook endpoint:
  12. Register https://obb-stripe-webhook.pages.dev/stripe in live mode
  13. Copy live whsec_* → set as Pages Project secret STRIPE_WEBHOOK_SECRET_LIVE
  14. The Pages function already routes test vs. live by event.livemode flag
  15. Live customer portal config:
  16. Recreate the portal configuration in live mode (same features as sandbox)
  17. Document the new bpc_* ID

Day-of-signing for first HF customer (~10 min)

When Andrew signs the engagement letter:

  1. Create live customer: python POST /v1/customers email = <Andrew's billing email> name = "Hundredfold Management Services" (or "Hundredfold Consulting LLC" — check engagement letter §1) metadata[client] = "hundredfold" metadata[engagement] = "erc-appeals"
  2. Issue Initial Partial Month Floor invoice (one-off, separate from subscription): ```python # Calculate per Section 3.1(c) of engagement letter: days_in_month = billable_days = days_in_month - signing_day_of_month daily_rate = (9900 * 0.5) / days_in_month # cents: int(990000 * 0.5 / days_in_month) total_cents = round(daily_rate * billable_days * 100) # if working in dollars; or just int math in cents

POST /v1/invoiceitems customer = amount = currency = usd description = "HF Appeals — Initial Partial Month Floor (ramp-up, 50% rate × {billable_days} days)"

POST /v1/invoices customer = collection_method = send_invoice days_until_due = 10 auto_advance = true # Stripe will finalize on next cycle # Or finalize manually + email immediately: POST /v1/invoices//finalize POST /v1/invoices//send 3. **Create subscription starting first day of next full calendar month**:python POST /v1/subscriptions customer = items[0][price] = items[1][price] = billing_cycle_anchor = proration_behavior = none # we already handled the partial month manually collection_method = send_invoice days_until_due = 10 metadata[client] = "hundredfold" metadata[engagement] = "erc-appeals" ```

Operational hooks

Known trade-offs

Sandbox cleanup post-cutover

Once live mode is operational and validated: