Skip to main content

The dunning lifecycle model

When a subscription charge fails, Orbit does not pause your outbound on the first failure. It counts. A per-organization dunning counter tracks consecutive failed subscription payments, and only the third consecutive failure freezes outbound sending with the pause reason dunning_failed_3x. This page is the model behind that number. The pause-gate taxonomy lives on the outbound pause gate and free-channel exemption; the refusal → recovery → poll loop lives on outbound pause and recovery. This page owns the counter’s state machine: how it increments, when it freezes, why a card update leaves it untouched, and which event clears it.

The payment_failed counter lifecycle

Every failed subscription charge increments the counter by one. The subscription retries that the payment processor runs across a few days each register their own failure, and the counter climbs 1, 2, 3. A successful charge at any point clears it back to zero — the counter counts consecutive failures, not lifetime failures, so one settled invoice at count 1 or 2 resets the window. The poll endpoint names the live count. GET /api/v1/organization/account-status derives its restrictions from the same organization row the gate reads, and while it reports the froze restriction, the raw count is the state machine’s visible cursor. Below the threshold there is no freeze — only the count.

The three-failures freeze

At count 3 the freeze flips in the same write that incremented the counter — one statement, so a race between two failed-charge events can never double-freeze or both notify. The organization’s outbound pauses with reason dunning_failed_3x, and every send on a paid channel rejects with 402 SENDING_PAUSED. The increment, the freeze, and the notification that the freeze fired happen exactly once, even when the processor redelivers the failure event. The freeze carries through to the public surface deterministically: GET /api/v1/organization/account-status reports the restriction with the billing code and remediation update_payment_method. The pause flips only when the threshold is crossed — the count just predicts it.

Counter-forward — a card update never resets

Updating the payment method deliberately leaves the counter where it was. The idea is to prevent pause-flapping: if a card update reset the counter to zero, an account could dance between froze and unfreeze by re-entering a doomed card each cycle. The freeze is sticky and the counter is forward-only — a charge either settles or it does not, and only settling resets. The settlement check is narrow on purpose: only a subscription renewal or a plan-change charge that actually succeeded clears the counter. A $0 metered usage invoice, a manual invoice, or any other non-subscription payment event does not — a settled micro-invoice never unfreezes an account whose subscription card keeps failing. This is what keeps the freeze honest.

Resolution — only a successful charge lifts the hold

Two resets exist, and each clears the counter back to zero and removes the dunning freeze when it was the reason:
  1. A successful subscription payment. When the retried charge settles, the payment-success event resets the counter and drops the freeze if — and only if — the freeze was the dunning one. A freeze set by an admin for any other reason survives untouched.
  2. Reactivate or change plan. Reactivating a canceled subscription or moving to a new plan is treated as a fresh billing relationship: both paths start the counter from zero again so a re-activated account is never one failure away from an immediate freeze.
No third path exists. A topped-up wallet leaves the freeze exactly where it was — wallet funding clears wallet_empty pauses, and only those. All credit routes (card top-up, crypto, auto-top-up, refunds, operator-issued credits) funnel through a shared resume block that touches only wallet_empty-marked pauses; a dunning_failed_3x hold falls outside its guard by design. Positive balance is never the unlock condition for dunning.

How dunning interacts with other subscription states

Dunning is one hold among several, and the restrictions add up rather than replace each other. A past_due or unpaid subscription state and a dunning freeze are different facts on the same account — the status endpoint reports both, ordered so the most limiting restriction surfaces first. None of the following overlap with each other:
  • wallet_empty — a balance-driven soft pause that a top-up clears; dunning is an account-lifecycle hold that a top-up cannot clear.
  • billing_alert:<id>:<name> — a spend-threshold hold you resolve under Dashboard → Billing → Alerts; nothing to do with charge failures.
  • Admin hold, suspension, dispute — hard holds on the organization that a freeze reset will not disturb; the dunning reset only ever touches freezes it set itself.

Poll-loop for recovery

Treat every 402 as terminal, then drive the loop:
  1. Update the payment method under Settings → Billing.
  2. Settle the outstanding invoice. The retried charge runs against the new card; once it succeeds the counter resets and, if the freeze was dunning, the freeze clears inline.
  3. Poll the status endpoint until the dunning restriction disappears:
Poll on a moderate cadence (every few seconds) — do not hot-retry a send that returned 402. Once the restriction clears, resume sending.

Read the model alongside the gate chain

The freeze is one cause in the wider outbound gate. The ordered pre-flight → flags → fund-path → decrement chain that every send walks is narrated on the billing gate chain; its stage-2 flags gate is where a failed third charge surfaces as a 402. The full cause taxonomy — which holds exempt free channels and which do not — is on the pause gate and free-channel exemption.