---
title: Update a subscription contract
description: Learn how to update an existing subscription contract with new information.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/purchase-options/subscriptions/contracts/update-a-subscription-contract
  md: >-
    https://shopify.dev/docs/apps/build/purchase-options/subscriptions/contracts/update-a-subscription-contract.md
api_name: admin
---

# Update a subscription contract

Subscription contracts often require updates, such as when a customer needs to update their payment method or requests a change to their subscription.

This guide shows you how to submit the desired changes to [`subscriptionContractUpdateCalculate`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractUpdateCalculate), review Shopify's calculated result, and commit a new active version of the contract. Fields that you omit remain unchanged.

**Available in the 2026-10 release candidate:**

The `SubscriptionContractCalculation` API is available in the GraphQL Admin API `2026-10` release candidate. If your app uses the `SubscriptionDraft` API, refer to the [legacy contract update guide](https://shopify.dev/docs/apps/build/purchase-options/subscriptions/contracts/update-a-subscription-contract-with-subscription-draft).

***

## Requirements

**Note:**

* Most subscriptions, pre-order and try before you buy apps need to request API access through the [Partner Dashboard](https://partners.shopify.com/current/apps). We give API access to apps that are designed according to our [principles for subscriptions, pre-order and TBYB apps](https://shopify.dev/docs/apps/build/purchase-options#shopifys-principles).
* Public apps that use subscriptions, pre-order or TBYB need to meet [specific requirements](https://shopify.dev/docs/apps/launch/shopify-app-store/app-store-requirements#purchase-option) to be published on the Shopify App Store.
* Custom apps [created in the Shopify admin](https://shopify.dev/docs/apps/build/authentication-authorization/legacy/admin-custom-apps) can't use subscriptions, pre-order or TBYB because these apps can't use extensions or request access to protected scopes. If you're building a solution for a single store, then [create your custom app in the Dev Dashboard](https://shopify.dev/docs/apps/build/dev-dashboard/create-apps-using-dev-dashboard).

- Your app can make [authenticated requests](https://shopify.dev/docs/api/admin-graphql#authentication) to the GraphQL Admin API.
- Your app has the `read_own_subscription_contracts` and `write_own_subscription_contracts` [access scopes](https://shopify.dev/docs/api/usage/access-scopes#authenticated-access-scopes). Learn how to [configure your access scopes using Shopify CLI](https://shopify.dev/docs/apps/build/cli-for-apps/app-configuration).
- You've created [products](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productcreate) and [product variants](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productVariantsBulkCreate) in your development store.

* Use the GraphQL Admin API version `2026-10` or later.
* Create a [subscription contract](https://shopify.dev/docs/apps/build/purchase-options/subscriptions/contracts/build-a-subscription-contract).

***

## How contract calculation works

Contract updates use an asynchronous calculate and commit flow:

1. Query the current contract state.
2. Submit the fields that you want to change.
3. Poll the calculation or wait for a webhook. Shopify emits `subscription_contract_calculations/succeed` when a calculation succeeds and `subscription_contract_calculations/fail` when it fails or is voided.
4. Review the complete calculated contract, projected totals, warnings, or errors.
5. Commit a successful calculation.

Committing creates a new version of the contract. It doesn't modify orders or fulfillments that have already been created. Use [`orderEditBegin`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/orderEditBegin) to edit an existing order.

***

## Step 1: Query the subscription contract

Query the contract before calculating an update. Include the IDs and complete values for any collection that you plan to replace, such as lines or manual discounts. When you rebuild the `lines` array, a line's `variantId` is the value that you pass as `productVariantId` in the input:

## POST https://{shop}.myshopify.com/admin/api/2026-10/graphql.json

## GraphQL query

```graphql
query GetSubscriptionContract($id: ID!) {
  subscriptionContract(id: $id) {
    id
    status
    note
    lines(first: 50) {
      nodes {
        id
        quantity
        variantId
        currentPrice {
          amount
          currencyCode
        }
        customAttributes {
          key
          value
        }
      }
    }
    billingPolicy {
      interval
      intervalCount
    }
    deliveryPolicy {
      interval
      intervalCount
    }
    deliveryMethod {
      __typename
    }
    customerPaymentMethod {
      id
    }
  }
}
```

## Variables

```json
{
  "id": "gid://shopify/SubscriptionContract/123"
}
```

***

## Step 2: Calculate the update

Call `subscriptionContractUpdateCalculate` with the contract ID and desired changes. The following example changes an existing line's quantity and applies a discount code. Because `lines` replaces the entire collection, this example assumes a single-line contract. For a contract with multiple lines, include every line you want to keep:

## POST https://{shop}.myshopify.com/admin/api/2026-10/graphql.json

## GraphQL mutation

```graphql
mutation UpdateSubscriptionContract {
  subscriptionContractUpdateCalculate(
    contractId: "gid://shopify/SubscriptionContract/123"
    contractUpdateInput: {
      withMerchandiseCustomizations: true
      lines: [
        {
          productVariantLine: {
            id: "gid://shopify/SubscriptionLine/existing-line-uuid"
            productVariantId: "gid://shopify/ProductVariant/111"
            quantity: 2
            customAttributes: []
            discounts: []
          }
        }
      ]
      discountCodes: [{ redeemCode: "SAVE10" }]
    }
  ) {
    subscriptionContractCalculation {
      ... on SubscriptionContractCalculationPending {
        id
      }
    }
    userErrors {
      field
      message
      code
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "subscriptionContractUpdateCalculate": {
      "subscriptionContractCalculation": {
        "id": "gid://shopify/SubscriptionContractCalculation/789"
      },
      "userErrors": []
    }
  }
}
```

If the mutation returns a user error, then correct the input before polling. Shopify doesn't create an asynchronous calculation when input validation fails.

**Collections replace existing values:**

When you provide `lines` or `manualDiscounts`, the array replaces the complete existing collection. Include the IDs and values for every existing item that you want to keep. Omit an existing item to remove it, and omit `id` from a new item.

Providing a line also replaces that line's `customAttributes` and `discounts`, and you must state both whenever you include the line. Pass the line's existing `customAttributes` (queried in Step 1) and re-declare any line-level discounts to keep them, or pass an empty list to clear either.

***

## Step 3: Poll for the calculation result

The calculate mutation returns a pending calculation. Poll for the result and review it before committing.

Contract calculations run asynchronously. Most calculations finish in less than three seconds, but Functions and external services can increase processing time. Use the calculation ID returned by the calculate mutation to query [`subscriptionContractCalculation`](https://shopify.dev/docs/api/admin-graphql/2026-10/queries/subscriptionContractCalculation):

## POST https://{shop}.myshopify.com/admin/api/2026-10/graphql.json

## GraphQL query

```graphql
query PollSubscriptionContractCalculation($id: ID!) {
  subscriptionContractCalculation(id: $id) {
    __typename
    ... on SubscriptionContractCalculationPending {
      id
    }
    ... on SubscriptionContractCalculationSuccess {
      id
      calculatedContract {
        lines(first: 10) {
          nodes {
            id
            quantity
            title
            variantId
          }
        }
      }
      warnings {
        code
        message
      }
      projectedOrderTotals {
        subtotal {
          amount
          currencyCode
        }
        totalDelivery {
          amount
          currencyCode
        }
        totalTax {
          amount
          currencyCode
        }
        totalMerchandiseDiscounts {
          amount
          currencyCode
        }
        totalDeliveryDiscounts {
          amount
          currencyCode
        }
        total {
          amount
          currencyCode
        }
      }
    }
    ... on SubscriptionContractCalculationFailure {
      id
      errors {
        code
        message
      }
    }
  }
}
```

## Variables

```json
{
  "id": "gid://shopify/SubscriptionContractCalculation/789"
}
```

## Pending response

```json
{
  "data": {
    "subscriptionContractCalculation": {
      "__typename": "SubscriptionContractCalculationPending",
      "id": "gid://shopify/SubscriptionContractCalculation/789"
    }
  }
}
```

The query returns one of the following types:

| Type | Meaning | Action |
| - | - | - |
| `SubscriptionContractCalculationPending` | Shopify initiated or is processing the calculation. | Continue polling. |
| `SubscriptionContractCalculationSuccess` | The calculated contract is ready to review and commit. | Review the result before committing. |
| `SubscriptionContractCalculationFailure` | The calculation failed (processed with errors) or Shopify voided it (never processed, for example, because of an infrastructure issue). | If it failed, read `errors` and correct the input before calculating again. If it was voided, `errors` is empty and there's nothing to fix in the input, so retry the calculation. |

Wait two seconds before the first poll, poll every second, and stop after 30 seconds. If the calculation is still pending, then retry later or wait for a webhook instead of increasing the polling frequency.

### Review the calculated contract

Before committing, inspect the following fields on `SubscriptionContractCalculationSuccess`:

* `calculatedContract`: The complete contract snapshot that the commit applies.
* `projectedOrderTotals`: The projected merchandise, delivery, discount, tax, and total amounts.
* `warnings`: Existing-data or compatibility problems that don't prevent the calculation from succeeding.

A successful calculation is immutable. If you need to change the input, then start a new calculation and commit only the result that you want to make active.

**Review warnings:**

A successful calculation can contain warnings. Review them before committing because they can identify disabled currencies, delivery configuration problems, or other existing contract data that Shopify preserved instead of blocking the update.

### Use webhooks instead of polling

For event-driven processing, subscribe to the following webhook topics:

| Topic | Description |
| - | - |
| `subscription_contract_calculations/succeed` | The calculation succeeded and is ready to review and commit. |
| `subscription_contract_calculations/fail` | The calculation failed or Shopify voided it. A failed calculation has `errors` to query; a voided one has empty `errors`, so retry it. |

Create a webhook subscription with [`webhookSubscriptionCreate`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/webhookSubscriptionCreate):

## POST https://{shop}.myshopify.com/admin/api/2026-10/graphql.json

## GraphQL mutation

```graphql
mutation CreateCalculationWebhook {
  webhookSubscriptionCreate(
    topic: SUBSCRIPTION_CONTRACT_CALCULATIONS_SUCCEED
    webhookSubscription: {
      uri: "https://example.com/webhooks/calculation-success"
      format: JSON
    }
  ) {
    webhookSubscription {
      id
    }
    userErrors {
      field
      message
    }
  }
}
```

## Webhook payload

```json
{
  "id": 789,
  "admin_graphql_api_id": "gid://shopify/SubscriptionContractCalculation/789",
  "state": "succeeded"
}
```

Use `admin_graphql_api_id` to query the completed calculation. Webhook delivery doesn't commit the result automatically.

***

## Step 4: Commit the calculation

After the calculation succeeds and you've reviewed the result, call [`subscriptionContractCalculationCommit`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractCalculationCommit). The commit makes the calculated contract active for billing:

## POST https://{shop}.myshopify.com/admin/api/2026-10/graphql.json

## GraphQL mutation

```graphql
mutation CommitSubscriptionContractCalculation {
  subscriptionContractCalculationCommit(
    id: "gid://shopify/SubscriptionContractCalculation/789"
  ) {
    contract {
      ... on SubscriptionContract {
        id
        status
      }
    }
    userErrors {
      field
      message
      code
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "subscriptionContractCalculationCommit": {
      "contract": {
        "id": "gid://shopify/SubscriptionContract/999",
        "status": "ACTIVE"
      },
      "userErrors": []
    }
  }
}
```

Handle commit user errors by `code`:

| Code | Meaning | Action |
| - | - | - |
| `NOT_READY_TO_COMMIT` | The calculation hasn't succeeded yet. | Continue polling before retrying the commit. |
| `CALCULATION_NOT_FOUND` | The calculation ID doesn't exist or isn't available to the app. | Verify the ID and app access. |
| `STALE_CONTRACT` | The contract changed after this calculation started. | Query the latest contract state and calculate again. |
| `INVALID` | Shopify can't commit the calculation. | Use `field` and `message` to correct the request. |

You can safely retry polling and commit requests. Retrying a calculate mutation creates a new calculation, so retain the ID for the result that you intend to commit. Retrying a commit for an already committed calculation returns the committed result.

***

## Update common contract attributes

Use the following update semantics when preparing `contractUpdateInput`:

| Input | Behavior |
| - | - |
| `withMerchandiseCustomizations` | Required. Set to `true` to run merchandise transformations, both fixed bundle expansion from a stored definition and Shopify Functions that customize merchandise, such as cart transforms. Set to `false` to bypass all merchandise transformations. |
| `lines` | Replaces all lines. Include an `id` to keep or update an existing line. Omit `id` to add a line. Omit the field to leave all lines unchanged. |
| `manualDiscounts` | Replaces all manual order and delivery discounts. Pass `[]` to remove all manual discounts. |
| `discountCodes` | Applies codes during this calculation. Shopify resolves them into manual discounts on the contract instead of storing the codes. |
| `billingPolicy` | Replaces the billing cadence and anchors. |
| `deliveryPolicy` | Replaces the delivery anchors and optional multi-fulfillment configuration. |
| `deliveryMethod` | Replaces the delivery method. Pass `none` to remove it, or `fetchAvailableDeliveryOptions` to discover options without changing the committed method. The resulting discovery calculation can't be committed. |
| `paymentMethod` | Replaces the payment method. Pass `none` to remove it. |
| `note` | Replaces the note. Pass `null` to clear it. |
| `customAttributes` | Replaces all contract attributes. Pass `[]` to remove them. |
| `appManagedBillingConfig` | Replaces app-managed `minCycles` and `maxCycles` metadata. These are stored on the contract but don't affect calculated billing cycle dates. |

Manage contract status separately with [`subscriptionContractActivate`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractActivate), [`subscriptionContractPause`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractPause), [`subscriptionContractCancel`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractCancel), [`subscriptionContractExpire`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractExpire), or [`subscriptionContractFail`](https://shopify.dev/docs/api/admin-graphql/2026-10/mutations/subscriptionContractFail).

### Discover delivery options

To retrieve shipping options for a new address without changing the committed delivery method, provide `fetchAvailableDeliveryOptions`:

```graphql
deliveryMethod: {
  fetchAvailableDeliveryOptions: {
    address: {
      lastName: "Ishida"
      address1: "123 Main St"
      city: "Toronto"
      provinceCode: "ON"
      countryCode: CA
      zip: "M5V 1A1"
    }
    withDeliveryCustomizations: true
  }
}
```

When you poll the calculation in [Step 3](#step-3-poll-for-the-calculation-result), read the results from the `deliveryOptions` field on `SubscriptionContractCalculationSuccess`, a union of shipping, local delivery, and pickup options. A calculation that provides `fetchAvailableDeliveryOptions` is a discovery calculation and can't be committed. Read the options, then create a new calculation with the chosen delivery method, and commit it. Delivery customization Functions run only when `withDeliveryCustomizations` is `true`.

***

## Next steps

* Learn how to [edit contracts that contain bundles](https://shopify.dev/docs/apps/build/purchase-options/subscriptions/contracts/edit-contracts-with-bundles).
* Learn how to [manage and edit billing cycles](https://shopify.dev/docs/apps/build/purchase-options/subscriptions/billing-cycles).
* Refer to the [`SubscriptionContractCalculationContractUpdateInput`](https://shopify.dev/docs/api/admin-graphql/2026-10/input-objects/SubscriptionContractCalculationContractUpdateInput) fields.
* Use the [legacy `SubscriptionDraft` update guide](https://shopify.dev/docs/apps/build/purchase-options/subscriptions/contracts/update-a-subscription-contract-with-subscription-draft) while maintaining an older integration.

***
