planId returned when the plan was created.
Quick Start
Pause a plan for two months, with automatic resume:The Basics
Never call these from the browser. Your API key must stay on the server.
All four endpoints require only the
x-api-key header, and only the merchant
that owns the plan can manage it.Which Actions Are Allowed When
Whether an action is allowed depends on the plan’s current status:
Disallowed combinations return a
400 with an error message pointing at the right endpoint.
Pause a Plan
Halts all future billing while keeping the plan’s schedule anchor intact. Pausing removes the plan’s next scheduled charge; nothing bills until the plan is resumed.Request Body (all fields optional)
Send{} (with Content-Type: application/json) for an indefinite pause.
Behavior Notes
- Pausing an already-paused plan is a re-pause:
pausedUntilandpauseReasonare replaced with the values you send, and a newpausedwebhook event fires. OmittingpausedUntilcancels any scheduled automatic resume; omittingpauseReasonresets it to"merchant_requested". Treat webhook events as at-least-once. - If the automatic resume can’t be scheduled, the pause still succeeds (
200), but the plan is paused indefinitely.pausedUntilisnullin both the response and the webhook event, and the responsemessagesays so. Call resume when you’re ready, or pause again withpausedUntil. - An in-flight charge cannot be stopped. If a charge is already being processed at the card processor when you pause, that charge may still complete. The pause takes effect for all subsequent billing either way.
nextChargeAtbecomesnullwhile paused;nextCycleAtkeeps the plan’s billing anchor so resume can compute the right date.
Response
200 with the full plan object (same shape as Query Recurring Plans). Key fields after a pause:
Resume a Plan
Reactivates a paused plan. No request body.Billing After a Resume
Cycles missed during a pause are skipped — the customer is never retroactively charged. Billing resumes on the next future billing date, preserving the plan’s original anchor day. Example: a monthly plan billing on the 31st is paused in January and resumed on August 15. The missed February–July cycles are skipped, and the next charge lands on August 31 — the original anchor day, not the resume date. (In months without that day, billing clamps to the last day of the month, exactly as it always has.)- If the pause was short and the original billing date hasn’t passed yet, the plan charges on that original date — a brief pause-and-resume changes nothing.
- If the plan’s
endDatepassed during the pause, the plan is completed instead of resumed (no charge fires), and acompletedwebhook event is sent. - If the plan was paused while a failed charge was waiting on an automatic retry, the first charge after resuming counts as that cycle’s next retry attempt. The cycle does not get a fresh set of
maxAttempts. It bills on the next future billing date, like any other resume. - Resuming an already-active plan is a harmless no-op (
200). - If the next charge can’t be scheduled, resume returns
500and the plan stays paused with its previous settings, including anypausedUntil. It’s safe to call resume again. - If another change lands on the plan at the same moment (an automatic resume, a concurrent request, or a pause or cancel), resume returns
409. Query the plan for its current state.
Automatic Resume (pausedUntil)
If the plan was paused with pausedUntil, it resumes automatically at that time — same skip-missed-cycles rules, and a recurringPayment.plan event with status: "active" fires. An explicit resume before pausedUntil simply cancels the scheduled automatic one.
If the automatic resume hits a temporary error, it is retried automatically (up to 3 attempts, with backoff). If it still can’t complete, the plan stays paused. Call resume to restart billing.
Cancel a Plan
Permanently stops the plan. Cancellation is terminal and cannot be undone.- Allowed from
active,paused, andfailed(cancelling a failed plan is the normal way to close it out if you don’t intend to retry). - A
completedplan cannot be cancelled — every contracted cycle was already billed. - Cancelling an already-cancelled plan is a harmless no-op (
200).
200 with the plan object showing "status": "cancelled" and a populated cancelledAt timestamp.
Manually Retry a Failed Plan
When a plan exhausts its automatic retries (maxAttempts), it moves to failed and stops billing. After resolving the underlying issue with your customer (e.g. they’ve updated their card balance), use this endpoint to retry the failed cycle immediately.
How It Works
- The plan is reactivated and the failed cycle’s charge is queued immediately.
- The endpoint returns
202right away — the charge itself runs asynchronously. - The charge outcome arrives via the
recurringPayment.chargewebhook event. - On success, the plan resumes its normal schedule on the original billing anchor. Cycles missed while the plan was failed are skipped, never retroactively charged.
- On failure, the plan returns to
failed. You can retry again or cancel.
409 and no manual_retry_initiated event fires. As with any pause or cancel, a charge that had already started may still complete, so query the plan to confirm its state. If the retry can’t be scheduled at all, it returns 500 and the plan stays failed, so it’s safe to try again.
Eligibility
- Only plans with
status: "failed". - Not available if the plan’s
endDatehas passed (400) — a lapsed plan can only be cancelled.
Response
202 Accepted with the plan object (now "status": "active"):
A
202 means the retry was queued, not that the charge succeeded. Always
confirm the outcome via the recurringPayment.charge webhook or by querying
the plan. The charge runs
immediately, so the charge result event can arrive before the status: "manual_retry_initiated" event.Errors
All four endpoints share these responses:Rate Limits
Webhook Events
Every lifecycle change fires arecurringPayment.plan event with the new status:
paused,active(explicit or automatic resume),cancelled, andcompleted.failed, but only when the plan actually moves tofailed. If you pause or cancel a plan while its final retry is in flight, the charge failure is still reported, but nofailedplan event fires, because the plan keeps the status you set.
recurringPayment.charge events: one per successful or failed charge attempt, plus one with status: "manual_retry_initiated" when you call retry. If a charge is flagged for payment reconciliation, a recurringPayment.charge event fires with status: "stalled_pending_manual_reconciliation", and its planPaused field shows whether the plan was system-paused.
Events are delivered at least once and may arrive out of order. Handle duplicates idempotently, and treat the plan’s current state (from querying the plan) as the source of truth.