Dunning
A dunning policy is the staged sequence of actions Kontorion takes after an invoice goes past its due_date. Each policy is an ordered list of steps; each step has a days_after_due offset, an action (send_email, grace, suspend, cancel), and - for send_email - an email_event identifier that downstream notification handlers route on. A scheduled background runner picks up overdue invoices, walks their policy, and either escalates or recovers based on payment status.
Step actions
| Action | Effect |
|---|---|
send_email | Publishes a dunning.step.email event with the configured email_event payload - your notification service renders + sends the message |
grace | Publishes dunning.step.grace and advances the subscription's DunningStage from WARNED to GRACED. The lifecycle status stays ACTIVE - dunning is a billing-mode overlay carried on a phase_kind=dunning phase, not a lifecycle status. No access change yet |
suspend | Publishes dunning.step.suspend, advances the DunningStage from GRACED to SUSPENDED (revoke access). The lifecycle status is still ACTIVE; only the dunning stage progresses |
cancel | Publishes dunning.step.cancel, transitions the lifecycle status to CANCELLED (terminal) with cancellation_reason=DUNNING_TERMINAL |
send_email requires a non-empty email_event value (the validator rejects empty). The other actions don't take a payload.
The lifecycle status enum is only DRAFT / ACTIVE / CANCELLED / EXPIRED - there is no DUNNING status. Throughout a dunning episode the subscription remains ACTIVE and the DunningStage (WARNED → GRACED → SUSPENDED) tracks escalation on the phase_kind=dunning phase. Note that customer-facing subscription events project a synthetic "DUNNING" status string while the episode is open; that is a derived event-payload label, not the lifecycle status. The cancel action is the only step that moves the lifecycle status (to CANCELLED).
Step ordering
Steps are validated to be monotonically increasing by days_after_due - each step must have a strictly larger offset than the previous, and all values must be ≥ 1. Same-day steps are explicitly rejected at create/update time.
days_after_due is measured from the invoice's due date, not from the previous step's execution time. A step with days_after_due: 14 always fires on day 14 past due, regardless of when earlier steps ran.
Policy shape
Code
| Field | Notes |
|---|---|
name | Required, free-form |
is_active | When false the runner skips this policy |
plan_id | null for the org-level default; set for a per-plan override |
steps | Required, at least one - see Step ordering |
Configuration
The org has a single default dunning policy at /v1/dunning-policy (singular). Use PUT to upsert it; the body carries the name and the ordered steps. The first call creates it (201 Created); subsequent calls update it (200 OK).
Code
days_after_due must be at least 1 per step (validated server-side).
Per-plan overrides live at /v1/plans/{planId}/dunning-policy; same PUT/DELETE shape. There is one override per plan, and it takes precedence over the org default for subscriptions on that plan.
See also
- Subscriptions - state transitions driven by dunning
- Payments - the retry layer dunning sits on top of
- Webhooks - event types emitted by the runner