How it works
Connect, scan, recover. Then prove what Gasket added.
1. Connect with a read-only key
You create a Stripe restricted key with read access to:
- Customers: customer email and name for the invoice
- Invoices: failed invoices and the hosted payment link
- Subscriptions: which subscription an invoice belongs to
- Charges: the decline reason
- PaymentIntents: the decline reason for newer payments
- Events: to notice new failures and payments
You paste it on a page hosted by Composio, the connection provider Gasket uses. Gasket stores only a reference to the connection. Before activating, Gasket checks that every read works and sends a harmless test write (an update to a customer that can't exist); if Stripe doesn't refuse it for lack of permission, the key is rejected and deleted.
Stripe's OAuth connection can't grant read-only access to apps like Gasket, which is why Gasket uses a restricted key instead.
2. Scan 12 months
Gasket lists every automatically-charged invoice created in the last 12 months and keeps the ones where at least one charge failed. Invoices paid on the first attempt, invoices you send manually, and zero-amount invoices are ignored. A failed invoice ends up in one of four states: recovered, still unpaid, written off (voided or marked uncollectible), or still being retried by Stripe.
After the scan, Gasket checks Stripe for new invoice events about every 5 minutes, and re-reads recent invoices nightly to catch anything an event missed. Mismatches are fixed and logged.
3. Recover, only if you turn it on
Accounts start read-only. In shadow mode Gasket drafts up to four emails per failed invoice over about two weeks, worded for the decline reason Stripe reported, and you approve each one. Auto-send becomes available after five approvals. Every email links to Stripe's hosted invoice page, where the customer pays or updates their card. Gasket checks the invoice with Stripe moments before each send and cancels the email if it's already paid.
Emails go out from your own verified domain, with your name and reply-to address, and include a one-click opt-out.
4. Attribution method
Every recovered invoice gets exactly one label. Gasket counts a recovery as its own only if all three of these are true:
- Gasket sent at least one email about the invoice before it was paid.
- The payment came within the attribution window (7 days by default) after the most recent Gasket email.
- There's evidence the customer acted, not Stripe's automatic retry: they paid with a different payment method than the one that failed, or paid before Stripe's next scheduled retry, or paid after Stripe had stopped retrying.
Everything else is labeled as follows:
| paid_before_any_gasket_email | Paid before Gasket sent anything. Stripe's own recovery. |
| paid_outside_stripe | Marked paid outside Stripe. Not attributed. |
| no_paid_time | Stripe didn't report a payment time. Not attributed. |
| outside_attribution_window | Paid after Gasket's attribution window closed. Not attributed. |
| customer_changed_payment_method | Customer paid with a different payment method after a Gasket email. |
| paid_before_next_stripe_retry | Customer paid after a Gasket email, before Stripe's next scheduled retry. |
| paid_after_stripe_stopped_retrying | Customer paid after a Gasket email, after Stripe had stopped retrying. |
| paid_by_scheduled_stripe_retry | Paid by Stripe's scheduled retry on the same card. Stripe's recovery. |
| insufficient_evidence | Not enough data to tell who recovered it. Not attributed. |
What this method can't know: if Stripe's own reminder emails are switched on, a customer might have updated their card because of Stripe's email rather than Gasket's. When both were sent, Gasket can't tell which one the customer read, so its number can be slightly high in that case. Turning off Stripe's failed-payment emails while Gasket runs removes the overlap. The historical scan never credits Gasket with anything: it only shows what Stripe recovered and what is still lost.