subscription.past_due
Fires when a subscription transitions to past_due — the first time a charge attempt fails. After this, the dunning loop kicks in (default schedule: retry at T+1d, T+3d, T+7d) until a charge succeeds (returning the subscription to active) or all retries exhaust (moving it to unpaid and eventually canceled).
This event is your start-of-grace-window signal — it's where you'd decide to send a "your subscription needs attention" notification distinct from per-attempt failures.
When it fires
Single-shot per dunning episode. The subscription status transitions active → past_due exactly once per failure-cycle:
- Renewal charge fails.
failedPaymentCountincrements to 1.- Status flips to
past_due. - This event fires.
subscription.payment_failedalso fires for the underlying attempt.
If the retry succeeds, status goes back to active and subscription.payment_succeeded + subscription.renewed fire. If the failure repeats, you get subscription.payment_failed per attempt but subscription.past_due is not re-fired (the subscription was already past-due).
Payload
{
"id": "evt_01HX...",
"type": "subscription.past_due",
"createdAt": "2026-06-01T00:00:19Z",
"accountId": "acc_01HX...",
"data": {
"subscriptionId": "sub_01HX..."
}
}
Slim — fetch the subscription for current cycle, plan, customer.
Handler examples
// Node
if (event.type === 'subscription.past_due') {
const sub = await client.subscriptions.retrieve(event.data.subscriptionId);
await emails.sendDunningKickoff(sub.customerId, { graceUntil: addDays(sub.currentPeriodEnd, 7) });
await access.markDegraded(sub.customerId);
}
# Python
if event["type"] == "subscription.past_due":
sub = client.subscriptions.retrieve(event["data"]["subscriptionId"])
emails.send_dunning_kickoff(sub["customerId"])
access.mark_degraded(sub["customerId"])
// Go
if event.Type == "subscription.past_due" {
var d struct{ SubscriptionID string `json:"subscriptionId"` }
_ = json.Unmarshal(event.Data, &d)
sub, _ := client.Subscriptions.Retrieve(ctx, d.SubscriptionID)
notify.DunningKickoff(ctx, sub)
}
What to do
- Notify the buyer that something's wrong with their payment method.
- Mark their account "degraded" but keep access alive until your grace window expires — that's the whole point of dunning.
- Surface the past-due state in your in-app UI so they can update their card without leaving your product.
Common pitfalls
- Hard-revoking on
past_due. This is the start of the grace window, not the end. Most past-due cycles recover within days. - Sending an email per failed attempt instead of one per dunning cycle. Subscribe to this event for the initial nudge; subscribe to
subscription.canceledfor the terminal state. The per-attemptpayment_failedevents are mainly for your CS team's escalation logic, not for user comms. - Forgetting to revert "degraded" state on recovery. When
subscription.renewedorsubscription.payment_succeededarrives, flip the user back to full access.
Related events
subscription.payment_failed— per-attempt failure (fires alongside on the first failure, then re-fires on each retry).subscription.payment_succeeded— retry success.subscription.canceled— dunning-exhausted terminal state.