Suspend a contract
Pauses a recurring contract: the status becomes SUSPENDED, the next charge date is cleared, and no further charges are created and no payments or retries are attempted until the contract is resumed (Resume a contract). Existing charges and their balances are left exactly as they are; nothing is cancelled or written off, so the contract still owes what it owed. It is sent as a contract update whose body carries status SUSPENDED; that is recognised and routed to the dedicated suspend call rather than to the general edit. This is a manual, operator-driven pause; it is not the automatic failure of a contract whose charge has passed its suspend date (that path ends in status FAILED, not SUSPENDED).
Where you can do this
- Merchant View: Contract (Suspend)
- Merchant View: Contract header (Suspend)
- API: Update a contract (
PUT /contracts/{contract_id}) — body carries status SUSPENDED
Before you start
- The contract exists and is an ongoing agreement (recurring, or with a charge schedule). A one-off is refused; a scheduled one-off (a one-off with a
scheduled_date) is routed to a charge edit instead and is never suspended. - There is no status precondition on the server: any status is accepted (see Rules).
Inputs
Suspending shares a route with updating a contract, so the only thing you send is the status that makes it a suspend. Nothing else is applied: Shuttle posts an empty body to the suspend itself. Fields belonging to the update route are still validated on the way past, though, so sending a stale one can refuse the suspend (see Rules).
| Input | Required | Meaning and constraints |
|---|---|---|
| status | yes | SUSPENDED. Any other status value is not a suspend and is handled by the general update or resume paths. |
Rules
Checked by the API means Shuttle refuses the request however it is sent; a screen name means only that screen refuses it, and a direct API call would be accepted.
| Rule | Checked by | What you see |
|---|---|---|
The fields go inside a contract object: {"contract": {"status": "SUSPENDED"}}. The contract itself is identified by the id in the URL, not in the body | The API | VALIDATION_ERROR: "API body error: missing contract object" |
| The contract must exist; Shuttle reads it before deciding what to do | The API | the error from the contract lookup is returned. Shuttle's own suspend call treats an unknown id as a silent OK, but the caller never reaches it |
A one-off contract (ONEOFF with no scheduled date) cannot be suspended | The API | VALIDATION_ERROR: "cannot update non-recurring contracts" |
A scheduled one-off (a one-off with a scheduled_date) is not suspended: the body is applied as an edit of its pending charge and status SUSPENDED is ignored | The API | OK; status unchanged |
The checks that run on every contract update run before the suspend branch: next_charge must not be in the past (Amend a contract's terms), resume needs next_charge (Resume a contract), and a payment_method must belong to the contract's account (Change the payment method on a contract) | The API | the sibling page's VALIDATION_ERROR, and the contract is not suspended |
| The public API declares amount, occurrences, charges, next_charge, next_payment, payment_method, alt_key, description, event_date, metadata, keep_active_until, modify_in_place and scheduled_date on this same PUT, but a body carrying status SUSPENDED applies none of them: the suspend call is sent an empty body, and it would ignore a body anyway (unlike resume, which reads one). Only the status and the next charge date change | The API | OK; the other changes are silently dropped |
| No in-flight guard: a suspend is accepted while a payment is pending or unresolved and while a concurrent edit is running (contrast Terminate a contract) | The API | OK |
Merchant View offers Suspend only on a recurring contract that is not completed, not superseded by an amendment and not already SUSPENDED, and only to a user with payment_contract; the server enforces none of the status gates | Merchant View screen | control hidden |
| Merchant View asks for confirmation before sending | Merchant View screen | confirmation modal |
What happens
- The contract's status becomes SUSPENDED and its
next_chargeis cleared. The response carries the contract with status SUSPENDED and nonext_charge. - No new charges are created and no payment or retry is attempted until the contract is resumed (Resume a contract). A retry sequence in progress is abandoned rather than paused.
- Existing charges keep their status and their outstanding and overdue amounts. Nothing is cancelled or written off; the contract still owes what it owed, and it is not completed automatically even if fully paid.
- A payment submitted before the suspend still settles. Neither its success nor its failure changes the contract's status.
- Any other field sent in the same body is dropped; only the status and
next_chargechange. - A CONTRACT.UPDATE webhook is raised. No email or SMS is sent.
Who can do this
- Roles: Admin only today; intended for Support as well, and being aligned.
- Permission keys (for clients managing permissions directly):
payment_contract(oragreement) on the client or the contract's division, scoped to the contract's account; checked by Shuttle for every entry point. Merchant View additionally requires the same permission to render the Suspend control, and contract view permission to reach the screen. - No feature option gates this function.
Related
Updated 32 minutes ago