Scheduler
Any payment that isn't a single pay-now runs through Shuttle's scheduler — the background process that picks up charges on their due date, attempts payment against the contract's saved payment method, and manages automatic retries when payments fail.
Which Payment Models Use the Scheduler
| Payment model | Scheduler involvement |
|---|---|
| Single pay-now | None — payment is taken synchronously |
| Scheduled one-time | Triggers the single payment on the scheduled date |
| Recurring (frequency-based) | Generates the next charge when due, then triggers the payment |
| Scheduled charges | Triggers each future-dated charge on its due date (charges with a due of "now" or in the past are taken synchronously during contract creation) |
Precision
The scheduler is a polled background process, not a real-time trigger. A charge due at 09:00:00 will normally be attempted within a few minutes of that time. Treat a charge's due date as "no earlier than this time" rather than "exactly this time".
next_charge vs next_payment
Two timestamp fields on a contract describe what will happen next:
| Field | Meaning | Populated for |
|---|---|---|
next_charge | When the next charge/invoice will be generated | Recurring (frequency-based) contracts only |
next_payment | When the next payment attempt will be made | Any contract with an outstanding payment — recurring retries, scheduled one-time, scheduled charges |
On a recurring contract, as soon as a charge is created, next_charge is advanced by one frequency cycle and next_payment is set to the imminent attempt date for the newly-created charge. If the payment fails, next_charge is unaffected but next_payment is pushed out by the applicable retry delay.
On scheduled one-time and scheduled-charges contracts, next_charge is not used — next_payment tracks the next scheduled attempt directly.
Advance Notice (BACS, SEPA, ACH and similar)
Some payment methods need to be submitted to the bank a number of days before the target settlement date. When a gateway tokenises one of these methods (e.g., BACS, SEPA, ACH via GoCardless; SEPA via Stripe) it returns an advanced_notice_hours value on the payment method indicating how far in advance the bank needs to be notified.
When a payment method has advanced_notice_hours set, Shuttle adjusts scheduling so payments are initiated that many hours before the due date:
- For a recurring contract, the next charge is generated
advanced_notice_hoursbeforenext_charge, andnext_paymentis pulled forward by the same window so the gateway receives the instruction in time for the bank to settle on the target date. - For scheduled one-time and scheduled charges,
next_paymentis similarly pulled forward from each charge'sduedate.
The due date itself is unchanged — the customer's account is still debited on the due date. What changes is when the invoice/charge is raised in Shuttle and when the payment instruction leaves for the gateway.
Representative values returned by gateways today:
| Scheme | advanced_notice_hours | Approx lead time |
|---|---|---|
| BACS (UK) | 180 | ~7.5 days |
| SEPA (EU) | 180 | ~7.5 days |
| PAD (Canada) | 180 | ~7.5 days |
| BECS (Australia / NZ) | 132 | ~5.5 days |
| ACH (USA) | 108 | ~4.5 days |
Cards and wallet payment methods do not set advanced_notice_hours; they are scheduled on the due date itself.
The practical implication: for merchants on these schemes, charges on a contract may appear in reporting/dashboards several days before their due date. That is expected behaviour, not a bug.
Retries
Automated payment attempts that fail are retried automatically. Shuttle applies different retry schedules depending on the failure type.
Technical Error Retries
Used when an attempt fails for an infrastructure reason — gateway offline, connectivity problem, internal error. The intent is to retry quickly because the underlying problem is often transient.
| Attempt | Delay from previous attempt |
|---|---|
| 1st retry | 5 minutes |
| 2nd retry | 60 minutes |
| 3rd retry | 3 hours |
| 4th retry | 6 hours |
| 5th retry | 1 day |
Decline Retries
Used when the issuing bank declines — insufficient funds, card blocked, limit exceeded. The intent is to space attempts so funds have time to become available.
| Attempt | Delay from previous attempt |
|---|---|
| 1st retry | 1 day |
| 2nd retry | 3 days |
| 3rd retry | 7 days |
| 4th retry | 14 days |
Retry State
retry_counton the contract tracks how many retries have been attempted.- Once the schedule is exhausted,
retry_completeis set totrue— no further automated retries are made against that outstanding amount. - New charges on a recurring contract still have their own fresh payment attempt even after
retry_completeis set — it only suppresses retries against the existing overdue amount. - A successful payment resets
retry_countto0and clearsretry_complete.
Retry schedules are configured per merchant; the values above are defaults and may be adjusted by your account manager.
Gateway-Managed Retries
Some gateways run their own retry policy and override Shuttle's decline-retry schedule. At present this applies to GoCardless: once GoCardless has a live mandate and has notified the customer of an upcoming collection, it can retry a declined collection faster than Shuttle's default schedule because the advance-notice lead time has already been served. In those cases Shuttle defers to the gateway's own retry cadence for declines.
Technical-error retries still use Shuttle's schedule — a technical failure means the instruction never reached the gateway, so there is nothing for the gateway to retry. Shuttle retries the instruction using the Technical Error Retries schedule above.
The practical implication for merchants on GoCardless (and any future gateway with equivalent behaviour): a declined collection may be retried on a different cadence to what is shown in the Decline Retries table. Consult the gateway's documentation for its schedule.
Contract Completion & Cancellation
A contract managed by the scheduler ends in one of two terminal states: COMPLETED (it ran its natural course) or CANCELLED (it was ended deliberately).
When a Contract Completes
The completion check runs every time a contract is saved — after each payment, write-off, or update. A contract transitions to COMPLETED when all of the following are true:
- No outstanding amount remains (everything charged has been paid or written off)
- No unresolved amount remains
- No further charge will be generated (
next_chargeis blank) - No payment is currently in flight
What that means per payment model:
| Payment model | Completes when |
|---|---|
| Scheduled one-time | The payment settles |
| Recurring, fixed number of payments | The final occurrence is collected |
| Recurring, open-ended | Never on its own — there is always a next charge. It ends by cancellation |
| Scheduled charges | Every charge in the schedule is paid or written off |
On completion, the CONTRACT.COMPLETE webhook fires (it also fires on transition to CANCELLED or FAILED — see Webhooks).
Holding a Contract Open: keep_active_until
Once a contract is COMPLETED, it can no longer be amended. If you need to apply changes after the final payment has been collected — for example, a mid-term amendment that adds a closing charge — set keep_active_until on the contract (via update contract, or when creating the checkout) before the final payment lands.
While keep_active_until is set to a future date, a fully-settled contract stays ACTIVE instead of completing. When the date passes, the scheduler wakes the contract and completes it — provided nothing is outstanding at that point. Amendments made during the window (extra charges, changed amounts) are processed as normal.
Points to note:
- It applies to contracts only (recurring and scheduled charges) — it has no effect on a one-time payment.
- It suppresses completion only. Cancellation is unaffected: a contract cancelled during the window transitions to
CANCELLEDas usual. - When the date passes on a fully-settled contract, completion happens even if the contract hasn't reached its configured number of occurrences — the hold was explicit and has expired.
- To release the hold early, update the contract with
keep_active_untilset to a past date (or blank) — the contract then completes as soon as nothing is outstanding. - A suspended contract is not completed while suspended; the window is re-evaluated when the contract is resumed.
Cancellation and the CANCELLING Status
Cancelling a contract (cancel contract) stops future charges immediately — next_charge is cleared and no new charges are generated. What happens next depends on whether an amount is still outstanding:
| Situation | Result |
|---|---|
Nothing outstanding, or outstanding written off (write_off: true) | Contract transitions straight to CANCELLED |
| Outstanding amount remains | Contract transitions to CANCELLING |
CANCELLING is a wind-down state: the contract accepts no new charges, but the scheduler keeps attempting to collect the outstanding amount, including automated retries. Once the outstanding amount is either collected or written off, the contract transitions to CANCELLED.
To cancel without collecting the remaining debt, pass write_off: true. To stop the automated retries but leave the debt on record, pass cancel_retries: true — the contract stays CANCELLING with next_payment blank until the amount is paid by other means or written off.
Cancelling While a Payment Is In Flight: strict
A payment that has already been submitted to the gateway cannot be recalled. If a cancel finalises the contract to CANCELLED while such a payment is still pending, and that payment later declines, the declined amount lands on an already-closed contract.
By default a cancellation proceeds regardless — any in-flight payment resolves independently afterwards. If you need certainty that no payment remains unresolved after cancellation, pass strict: true: a cancel that would immediately finalise the contract (nothing outstanding, or write_off: true) is rejected while a payment is pending or unresolved on the contract. Wait for the payment to resolve, then cancel again.
strict has no effect on a cancel that lands in CANCELLING — the wind-down state stays open until every amount resolves, so an in-flight payment poses no risk there.
Enabling the Scheduler View for Merchants
Merchants can see and manage upcoming scheduled activity (future charges, retry status, failing contracts) from a Scheduler page in their merchant view. This page is hidden by default.
If your platform uses scheduled one-time, recurring, or scheduled charges, enable the Scheduler page for your merchants:
Developer Portal → Developer Options → Merchant View → Scheduler
Once enabled, merchants on your platform will see the Scheduler option in their navigation.
Updated 8 days ago