# Update a contract

`PUT https://app.shuttleglobal.com/c/api/instances/{instance_key}/contracts/{contract_id}`

Update specific properties of a contract. The following fields can be modified:
* next_charge: Next charge date or 'now'. Not accepted on a scheduled-charges contract or together with `charges` (move a charge's `due` instead), nor once every occurrence has been charged
* occurrences: Total number of charges. Send `0` to remove the cap (until cancelled). Raising or removing the cap is refused once every occurrence has been charged
* next_payment: Next payment attempt date, no more than 24 hours in the past, or 'now'. Send an empty string to clear it (not on a scheduled-charges contract)
* payment_method: New payment method ID. It must belong to the contract's account and be active. On a scheduled single payment it changes the card the payment will be taken with. Cannot be combined with `status: SUSPENDED`
* amount: New charge amount (creates new contract)
* resume: Resume cancelled contract
* scheduled_date: New schedule date
* status: SUSPENDED/ACTIVE
* metadata: Custom key-value pairs stored on the contract
* alt_key: Your reference for the contract
* description: Payment description
* event_date: Event date
* keep_active_until: Optional date that keeps the contract active past its final scheduled payment, so late amendments can still be applied. Send an empty string to clear it, or 'now' to end it now
* charges: Add, update, or remove scheduled charges on a scheduled-charges contract
* modify_in_place: Edit the existing contract and its charges directly instead of cancelling and cloning (only relevant when `charges` is supplied)

**Resume restriction:** when resuming a cancelled or suspended contract (by setting `resume: true` or `status: ACTIVE`), you cannot change `amount`, `frequency`, `charges`, `event_date` or `payment_method` in the same request.

**Legal entities:** a contract's `legal_entity` / `legal_entities` split cannot be changed after it is created. Sending the current value back is accepted and ignored; any other value is refused. Each charge's own `legal_entities` can be changed, and must add up to the charge amount.

**Dates:** `next_charge`, `next_payment`, `keep_active_until` and each charge's `due` must be ISO 8601 dates (e.g. `2026-12-31T12:00:00+01:00`). A date with an offset is honoured; a date without one is read as UTC. An invalid date, such as 30 February, is refused.

A value equal to the one already stored (for example a contract sent back unchanged) is not re-checked.

## Authentication

- basicAuth: Basic Authentication using secret key
- bearerAuth: OAuth 2.0 Bearer Token Authentication

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `contract_id` | path | string | yes | The contract's ID (`co_...`). |

## Request body (`application/json`), required

- `contract` (object)
  - `alt_key` (string): Your reference for the contract
  - `amount` (string)
  - `description` (string): Payment description
  - `event_date` (string (date-time)): Event date associated with the payment
  - `keep_active_until` (string (date-time)): Optional. When set to a future date, the contract stays active until this date even after its final scheduled payment is collected, so mid-term amendments can still be applied. Setting a past date, or an empty string to clear it, lets the contract complete normally once no balance is outstanding.
  - `metadata` (object): Custom key-value pairs to store on the contract
  - `next_charge` (string (date-time))
  - `next_payment` (string (date-time)): Next payment attempt date, no more than 24 hours in the past, or 'now'. An empty string clears it (not on a scheduled-charges contract).
  - `occurrences` (integer): Total number of charges. `0` removes the cap, so the contract continues until cancelled.
  - `payment_method` (string): Payment method ID of the contract's account, active or failing. Cannot be combined with `status: SUSPENDED`.
  - `resume` (boolean)
  - `scheduled_date` (string (date-time))
  - `status` (string) One of: `SUSPENDED`, `ACTIVE`.
  - `charges` (array of object): Change the scheduled charges on a scheduled-charges contract. Two modes are available, controlled by `modify_in_place`. ### Replacement mode (default) The existing contract is cancelled and a new one is created. The array must list every charge from the existing contract — this is how you acknowledge its current state. What each entry does depends on the state of the charge it refers to: * **Already-processed charges** (paid, in-flight, or written off) — pass `{ id }` *or* `{ alt_key }` with no other fields. Listing them is purely about acknowledging the prior state of the existing contract; they remain on the cancelled original as historical record and are **not** recreated on the new contract. Passing additional fields on these entries is rejected. * **Unpaid existing charges** are transferred across to the new contract. Pass `{ id }` or `{ alt_key }` to carry the charge over unchanged, or include `amount`, `due`, or other fields in the same entry to modify it during the transfer. * **Create** a new charge on the new contract — pass `{ amount, due }` (and optionally `alt_key`) with no `id` or `alt_key` matching an existing charge. ### In-place mode (`modify_in_place: true`) Edits the existing contract and its charges directly. No new contract is created and existing charge IDs are retained. Each entry takes one of three shapes: * **Add** a new charge — omit `id`. `amount` and `due` are required. * **Update** an existing charge — include its `id` plus the fields to change (e.g., `amount` or `due`). Omitted fields are left unchanged. * **Remove** an existing charge — include its `id` *or* `alt_key` with `delete: true`. ### Validation The same rules apply as on create: a new or moved `due` cannot be more than 7 days in the past (an existing charge whose `due` is unchanged is not checked), amounts must be greater than zero, and a charge's `legal_entities` must add up to its amount - changing the amount of a split charge means sending a matching split. Not permitted on a resume operation — see the resume restriction above.
    - `id` (string): Existing charge ID. In replacement mode (default), `id` (interchangeable with `alt_key`) identifies a charge from the existing contract: processed charges stay on the cancelled original, while unpaid charges are transferred to the new contract and may be modified by passing additional fields. In `modify_in_place` mode, include `id` to update or delete an existing charge.
    - `alt_key` (string): Your reference for this charge. When updating, can be used in place of `id` to identify an existing charge on the contract.
    - `amount` (string): The charge amount. Required when creating a charge.
    - `due` (string): When the charge is due. Required when creating a charge. Accepts an ISO 8601 timestamp or the literal string `"now"`. Cannot be more than 24 hours in the past.
    - `delete` (boolean): Only valid in `modify_in_place` mode. Set to `true` together with `id` or `alt_key` to remove the charge from the contract.
  - `modify_in_place` (boolean): Only relevant when `charges` is supplied. When `true`, edits the existing contract and its charges in place rather than cancelling the contract and creating a new one. See the `charges` description for the entry shapes each mode accepts. Defaults to `false`.

## Responses

### 200: Updated contract

```json
{
  "contract": {
    "account": "string",
    "alt_key": "string",
    "amount": "string",
    "channel": "string",
    "completed": "2026-01-01T12:00:00Z",
    "created": "2026-01-01T12:00:00Z",
    "currency": "string",
    "description": "string",
    "designation": "string",
    "frequency": "WEEKLY",
    "gateway": "string",
    "id": "string",
    "initiated": "2026-01-01T12:00:00Z",
    "initiative": "string",
    "keep_active_until": "2026-01-01T12:00:00Z",
    "last_charge": "string",
    "last_transaction": "string",
    "legal_entity": "string",
    "metadata": {
      "order_id": "12345",
      "customer_reference": "CUS-001"
    },
    "next_contract": "string",
    "next_payment": "2026-01-01T12:00:00Z",
    "nonce": "string",
    "account_alt_key": "string",
    "charge_count": 0,
    "event_date": "2026-01-01T12:00:00Z",
    "outstanding_amount": "string",
    "payment_method": "string",
    "recurring": true,
    "retry_complete": true,
    "retry_count": 0,
    "status": "SETUP",
    "updated": "2026-01-01T12:00:00Z"
  }
}
```

### 400: Bad request

```json
{
  "code": "string",
  "message": "string"
}
```

### 401: Unauthorised: the API key is missing or not valid

```json
{
  "code": "UNAUTHORIZED",
  "message": "Unauthorised"
}
```

## Example request

```sh
curl --request PUT \
  --url https://app.shuttleglobal.com/c/api/instances/YOUR_INSTANCE_KEY/contracts/CONTRACT_ID \
  --header 'Authorization: Basic <base64 of SECRET_KEY:>' \
  --header 'Content-Type: application/json' \
  --data '{
  "contract": {
    "metadata": {
      "order_id": "12345",
      "customer_reference": "CUS-001"
    },
    "charges": [
      {
        "id": "cg_11842_10005",
        "amount": "75.00"
      },
      {
        "alt_key": "INSTALMENT_4",
        "amount": "50.00",
        "due": "2026-08-01T09:00:00Z"
      },
      {
        "id": "cg_11842_10006",
        "delete": true
      }
    ]
  }
}'
```

Reference page: https://docs.shuttleglobal.com/merchants/reference/operations/updatecontract/