A subscription connects a customer to a plan. Subby's subscription engine renews it on schedule. If a charge fails, the engine retries, reminds the customer and, if the customer still does not pay, takes the escalation action you chose on the plan.
Subby is not a payment engine. The payment rail attempts each charge. The subscription engine owns status, retries, nudges and what happens next.
Statuses
| Status | Meaning | Should the customer have access? |
|---|---|---|
incomplete |
Created, waiting for the first payment or for checkout to finish | No |
trialing |
In a free trial. No charge until trial_end |
Yes |
active |
Paid and in good standing | Yes |
past_due |
A renewal payment failed and Subby is retrying | Your choice. Most platforms keep access during retries |
paused |
Billing is paused, either by you, by the customer, or by the pause_service escalation |
No |
cancelled |
Ended. Terminal, won't be charged again | No |
checkout completes
incomplete ───────────────────────▶ trialing ──trial ends, paid──▶ active
│ │ ▲
│ first payment fails / expires renewal fails │ │ retry succeeds
▼ ▼ │
cancelled ◀── unpaid too long ── paused ◀── escalation ── past_due ───┘
▲ │
└──────── cancel ───────────────┘ resume ──▶ active
Renewals
At the end of each period Subby creates a charge for the plan amount (plus any usage-based overage_per_unit you reported) and tries the customer's default payment method.
- Success: the period advances,
current_period_endmoves forward, and you receivecharge.succeededandsubscription.renewed. - Failure: the subscription moves to
past_due, you receivecharge.failedandsubscription.past_due, and the retry schedule starts.
Retries
Each plan has a retry_logic object:
{
"enabled": true,
"max_attempts": 3,
"retry_intervals_days": [1, 3, 7],
"escalation_on_failure": "pause_service",
"cancel_after_days_unpaid": 30
}
With this setup, if a renewal fails on 1 October, Subby retries on 2, 4 and 8 October. Each retry creates a new charge with an increasing attempt_number, and sends charge.retry_scheduled before the next attempt.
Subby skips the remaining retries when the failure code isn't retryable, like expired_card. Instead it moves straight to asking the customer for a new payment method. See payment failure codes.
Automatic retries are free. They are not API requests and are never metered.
Nudges
A nudge is one reminder delivered to one customer on one channel. The plan's reminders object controls automatic nudges:
{
"enabled": true,
"channels": ["whatsapp", "sms", "email"],
"pre_due_days": [3],
"post_due_days": [1, 3, 7]
}
pre_due_dayssends a reminder before a renewal is due.post_due_dayssends a reminder after a payment has failed.channelsis in order of preference. Subby tries the first channel the customer has contact details for, and falls back to the next if delivery fails.
Every nudge message includes a secure link where the customer can pay or update their payment method.
A nudge counts toward your nudge allowance when it is delivered or sent without a delivery receipt. Nudges that fail before sending (for example, no phone number) don't count. You receive nudge.sent, and then nudge.delivered or nudge.failed.
You can also send a nudge yourself with POST /nudges. That's 1 write request plus 1 nudge.
Escalation
When the last retry fails, Subby takes the plan's escalation_on_failure action and sends subscription.escalated:
| Value | What Subby does | What you should do |
|---|---|---|
pause_service |
Sets the subscription to paused and sends subscription.paused |
Remove access until the customer pays |
restrict_access |
Keeps the subscription past_due and sends subscription.escalated with action: "restrict" |
Limit access, such as read-only mode |
hold_delivery |
Keeps the subscription past_due and sends subscription.escalated with action: "hold" |
Stop physical deliveries or service visits |
none |
Keeps the subscription past_due. No action |
Decide yourself |
If the customer pays through a nudge link at any point, Subby charges them, sets the subscription back to active, and sends charge.succeeded and subscription.recovered.
If the customer is still unpaid cancel_after_days_unpaid days after the first failure, Subby cancels the subscription with cancellation_reason: "nonpayment" and sends subscription.cancelled. Set cancel_after_days_unpaid to null to never cancel automatically.
Pausing and resuming
If a plan has pause_enabled: true, you can pause a subscription:
curl -X POST "https://api.mysubbyapp.com/v1/subscriptions/sub_9fK3mQ2xLp/pause" \
-H "Authorization: Bearer $SUBBY_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "resumes_at": "2026-11-01T00:00:00Z", "reason": "customer_travelling" }'
- No charges or reminders happen while paused.
- Leave out
resumes_atto pause until you callPOST /subscriptions/{id}/resume. - On resume, the new period starts on the resume date. With
pro_rate: trueon the plan, the first charge is prorated.
Cancelling
curl -X POST "https://api.mysubbyapp.com/v1/subscriptions/sub_9fK3mQ2xLp/cancel" \
-H "Authorization: Bearer $SUBBY_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "at_period_end": true, "reason": "customer_request" }'
at_period_end: truekeeps the subscriptionactiveuntilcurrent_period_end, setscancel_at_period_end: true, then cancels. You can undo this withPATCH /subscriptions/{id}and"cancel_at_period_end": false.at_period_end: falsecancels immediately.
Events by stage
| Stage | Events |
|---|---|
| Created | subscription.created |
| Trial ending in 3 days | subscription.trial_will_end |
| Paid | charge.succeeded, subscription.renewed |
| Payment failed | charge.failed, subscription.past_due, charge.retry_scheduled |
| Reminder sent | nudge.sent, nudge.delivered or nudge.failed |
| Recovered | charge.succeeded, subscription.recovered |
| Escalated | subscription.escalated, and subscription.paused for pause_service |
| Paused / resumed | subscription.paused, subscription.resumed |
| Updated | subscription.updated |
| Cancelled | subscription.cancelled |
See the full event catalogue.
Frequently asked questions
What does the subscription engine do after a charge fails?
It retries on the plan schedule, sends configured nudges, and applies the plan's escalation action if the subscriber still does not pay.
Does a failed charge mean Subby is the payment engine?
No. The payment rail attempts the charge. The subscription engine updates status, recovery and access rules around that result.