# 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 agai

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.

<br />

This page is the model: what the scheduler does and why. What one operation accepts, refuses and changes is per operation in [Business Rules](/platforms/user-guides/rules/), which the screens and the API both follow, and each section below links to the ones that matter.

## 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.

<br />

Moving either of them by hand is an operation in its own right: [Move the next charge](/platforms/user-guides/rules-contract-reschedule-next-charge/) and [Reschedule the next payment](/platforms/user-guides/rules-contract-reschedule-payment/) say what each accepts, and what one does to the other.

## 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_hours` before `next_charge`, and `next_payment` is 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_payment` is similarly pulled forward from each charge's `due` date.

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 |

### When an Automated Retry Is Not Attempted

A retry is only scheduled if there is a usable payment method to retry against. In the cases below no further automated attempt is made against the outstanding amount: `next_payment` is cleared and `retry_complete` is set to `true`.

| Reason | What is happening |
|---|---|
| No payment method on the contract | There is nothing to charge — typically the method was removed rather than replaced |
| The payment method can no longer be charged | It is archived, inactive, failed, single-use, or the card has already passed its expiry date. The retries stop because the method cannot be charged, not because of the contract's status — [archiving a method](/platforms/user-guides/rules-payment-method-archive/) does move the contracts using it to `FAILING`, but that only marks them as needing attention |
| The method's gateway has been retired | A payment method can still read **ACTIVE** while the gateway behind it is archived or inactive. Nothing can be routed through it, so nothing is retried |
| The saved payment method is no longer eligible for this contract | A contract collects to a legal entity, and that legal entity has to be configured to accept the saved method — its card type, its currency and the customer's country. If that configuration is changed or removed the method is no longer eligible for the contract, and no attempt can be made against it. The attempt records **Gateway unavailable for this payment** |
| The last attempt never reached the gateway | Such an attempt is recorded as **Not attempted** (`INVALID_PAYMENT_METHOD`) and is not retried. The cause is a payment method or configuration problem, not a transient one |
| The retry would fall after the next charge is due | On a recurring contract the retry is dropped rather than deferred — the next charge gets its own payment attempt when it is raised. On a scheduled-charges contract the attempt is brought forward to the next charge's due date instead |
| The contract is suspended | Suspending clears `next_payment`. [Resuming](/platforms/user-guides/rules-contract-resume/) does not restore the dropped retry; collection picks up at the next charge |
| The contract was terminated with `cancel_retries` | The block stays in place until the contract is resumed. See [Terminate a contract](/platforms/user-guides/rules-contract-terminate/) |
| The payment method is marked `no_retry` | Declines against that method are not retried. Technical-error retries still run |

None of this stops the next charge being attempted. When a new charge is created, the scheduler always makes one payment attempt against it — even when the payment method is already known to be unusable and the attempt cannot reach the gateway. That attempt is recorded against the charge, so the reason the invoice was not collected is visible where the invoice is rather than the charge simply sitting unpaid with no explanation.

What is suppressed is the retries. Repeat attempts exist to recover money from a method that might succeed later — funds arriving in the account, a temporary block lifted. There is nothing to recover by attempting a method that cannot be charged at all, so no retry is scheduled and the contract waits for either a new payment method or the next charge.

A single pay-now payment is never retried automatically — it does not go through the scheduler at all.

In all of these cases the contract stays in `FAILING` with its arrears intact. Collection resumes once the cause is dealt with: attach a working method with [Change the payment method on a contract](/platforms/user-guides/rules-contract-change-payment-method/), then [Collect a contract's arrears now](/platforms/user-guides/rules-contract-make-payment/) to attempt payment without waiting for the next charge.

### Retry State

* `retry_count` on the contract tracks how many retries have been attempted.
* Once the schedule is exhausted, `retry_complete` is set to `true` — 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_complete` is set — it only suppresses retries against the existing overdue amount.
* A successful payment resets `retry_count` to `0` and clears `retry_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.

<br />

Collecting arrears by hand, rather than waiting for the next attempt, is [Collect a contract's arrears now](/platforms/user-guides/rules-contract-make-payment/).

## 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_charge` is 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](/platforms/reference/webhook-events/)).

What each operation does to a contract's outstanding amount, and therefore to its completion, is set out per operation in [Business Rules](/platforms/user-guides/rules/): [Write off a charge](/platforms/user-guides/rules-charge-void/), [Amend a contract's terms](/platforms/user-guides/rules-contract-amend/) and [Suspend a contract](/platforms/user-guides/rules-contract-suspend/).

### 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](/platforms/reference/operations/updatecontract/), 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 `CANCELLED` as 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_until` set 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.

What an amendment made during that window is allowed to change is in [Amend a contract's terms](/platforms/user-guides/rules-contract-amend/) and [Amend a scheduled contract's instalments](/platforms/user-guides/rules-contract-amend-schedule/).

### Cancellation and the CANCELLING Status

Cancelling a contract ([cancel contract](/platforms/reference/operations/cancelcontract/)) 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`.

The thing to hold on to is that cancelling stops *new* charges, not collection: `CANCELLING` keeps collecting what is already owed, and a cancelled contract still reports what was owed because its charges are left exactly as they were. Clearing a balance for good is a separate act — [Write off a charge](/platforms/user-guides/rules-charge-void/).

A payment already submitted to the gateway cannot be recalled, so a cancel that finalises the contract while one is in flight can leave a declined amount landing on a closed contract. There is a parameter to refuse that case rather than accept it.

The parameters — writing off as you cancel, stopping the retries without writing off, and refusing to finalise over an in-flight payment — and exactly what each refuses and when, are in [Terminate a contract](/platforms/user-guides/rules-contract-terminate/). [Resume a contract](/platforms/user-guides/rules-contract-resume/) covers coming back from `CANCELLING`.

## 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 → the application's Developer tab → Configure Merchant View → Scheduler**

Once enabled, merchants on your platform will see the Scheduler option in their navigation.