Skip to content

Handling a failed card

Tutorial 10 of 12 follows Birchwood Lettings' failed renewal on 2026-10-13 through Stripe, the Events it raised and the follow-up Workflow.

On 2026-10-13, Birchwood Lettings' card failed on its $29 pro renewal. Stripe leads every money record, so the Invoice, the retry and the Events all start there.

What you build

You trace the failure from invoice LEAS-0018 to the Events it raised, then write the Workflow that tries the card again. Nothing in this tutorial moves money by hand.

Steps

Terminal4 steps
  1. To read the Invoice, get it by id. It is an upstream Resource: api.sb reads it through from Stripe and keeps a mirror:

    npx startup-builder do Invoice.get --json '{"id":"inv_FXANyzQojc"}'

    It is open, due 2026-10-13, for $29.

  2. To see what the failure set off, list the Events of 2026-10-14:

    npx startup-builder show /events --fields 'seq,name,at'

    Invoice.overdue fired at 08:00 and Subscription.pastDue at 08:05.

  3. To try the card again, write the Workflow that wakes on the first of those Events:

    workflows/billing/past-due-follow-up.ts
    on.Invoice.overdue(async (invoice, $) => {
      await $.step('retry', () => $.Invoice.collect({ invoice: invoice.$id }))
    })
  4. To rehearse it in test, fire the Event and read the delivery:

    npx startup-builder trigger Invoice.overdue

Result

The morning after an Invoice goes overdue, Leaseline asks Stripe to collect it again, once. Leaseline's own follow-up also emails the pay link and opens a Task for the founder after 7 unpaid days. Checkpoint leaseline@ch10 (status planned) adds workflows/billing/.

Next steps