Migrate to the Subscription Contract Calculation API
This guide walks you through migrating from the SubscriptionDraft API to the new SubscriptionContractCalculation API for managing subscription contracts.
This API is in early access. Send feedback to subscriptions-calculate-api-early-access@shopify.com.
This API is in early access. Send feedback to subscriptions-calculate-api-early-access@shopify.com.
Anchor to RequirementsRequirements
- Access scope:
write_own_subscription_contracts. - API version:
2026-10.
Anchor to What's changing and whyWhat's changing and why
The new API integrates with Shopify's unified checkout and provides the following benefits:
- Simplified API surface: Contract mutations are consolidated into three calculate operations plus a shared commit.
- Consistent pricing: Tax and discount calculations align with checkout.
- Function support: Cart transforms and delivery customizations. Other Shopify Functions will be supported in later releases.
- Accurate cost breakdown: Totals for lines, delivery, taxes, duties, and discounts are available through the API.
The following table summarizes the main differences between SubscriptionDraft and the new SubscriptionContractCalculation API.
| Aspect | SubscriptionDraft | SubscriptionContractCalculation |
|---|---|---|
| Approach | Multi-step draft mutations | Single contract calculation |
| Processing | Synchronous | Asynchronous (requires polling) |
| State management | Server maintains draft state | Client provides desired state; omitted fields are preserved on update |
| Webhooks | None | Webhooks on success or failure |
Anchor to Understand the new API modelUnderstand the new API model
Anchor to From drafts to contract calculationsFrom drafts to contract calculations
With the current SubscriptionDraft API, you create a draft, apply changes through multiple mutations, and then commit the draft. With the new SubscriptionContractCalculation API, you submit the desired state in a single request. The server calculates pricing, applies taxes, executes functions, and returns an immutable result snapshot. On contract updates, any field you omit is left unchanged — you don't need to resend the entire contract.
Anchor to Asynchronous processingAsynchronous processing
Calculations run asynchronously. You can poll for results or subscribe to webhooks. Here's the flow:
- Call the calculate mutation.
- Receive a
SubscriptionContractCalculationPendingresponse. - Poll the
subscriptionContractCalculationquery, or wait for a webhook. - Receive a
SubscriptionContractCalculationSuccessorSubscriptionContractCalculationFailureresponse when complete. - Call
subscriptionContractCalculationCommitto apply the changes.
Anchor to Feature flagsFeature flags
The following Boolean fields control contract calculation behavior:
| Field | Location | Description |
|---|---|---|
withMerchandiseCustomizations | Top-level create or update input | When set to true, enables cart transforms and other function-based merchandise modifications. |
withDeliveryCustomizations | deliveryMethod.fetchAvailableDeliveryOptions | When true (the default), runs delivery customization functions while fetching delivery options, so the returned options reflect the merchant's configured customizations. Set to false to bypass these functions and return the raw options. |
Anchor to API migration referenceAPI migration reference
Anchor to Mutation mappingMutation mapping
| Old mutation | New approach |
|---|---|
subscriptionContractCreate | Use subscriptionContractCreateCalculate |
subscriptionContractUpdate | Use subscriptionContractUpdateCalculate |
subscriptionContractAtomicCreate | Use subscriptionContractCreateCalculate |
subscriptionContractProductChange | Use subscriptionContractUpdateCalculate |
subscriptionDraftLineAdd | Include in lines[] input |
subscriptionDraftLineUpdate | Include updated line with the same id in lines[] |
subscriptionDraftLineRemove | Omit line from lines[] |
subscriptionDraftDiscountAdd | Include orderDiscount in manualDiscounts[] input |
subscriptionDraftDiscountUpdate | Include updated orderDiscount with the same id in manualDiscounts[] |
subscriptionDraftDiscountRemove | Omit discount from manualDiscounts[] |
subscriptionDraftDiscountCodeApply | Include in discountCodes[] input |
subscriptionDraftFreeShippingDiscountAdd | Include deliveryDiscount in manualDiscounts[] |
subscriptionDraftFreeShippingDiscountUpdate | Include updated deliveryDiscount with the same id in manualDiscounts[] |
subscriptionDraftUpdate | Recalculate with additional input |
subscriptionDraftCommit | Use subscriptionContractCalculationCommit |
subscriptionBillingCycleContractEdit | Use subscriptionBillingCycleContractEditCalculate |
subscriptionBillingCycleContractDraftConcatenate | No calculate API equivalent yet; continue using the draft API |
subscriptionBillingCycleContractDraftCommit | Use subscriptionContractCalculationCommit |
Anchor to Deprecated and renamed fieldsDeprecated and renamed fields
The SubscriptionContractCalculation API introduces changes to how certain contract attributes are managed. The following fields have been deprecated, renamed, or moved.
Anchor to Contract statusContract status
Contract status is now managed using dedicated mutations instead of being set through the draft or contract calculation input. This change decouples contract status from the contract versioning model.
| Old approach | New approach |
|---|---|
Set status field in SubscriptionDraftInput during commit. | Use dedicated status mutations: subscriptionContractActivate, subscriptionContractPause, subscriptionContractCancel, subscriptionContractExpire, subscriptionContractFail. |
Anchor to Line pricing policyLine pricing policy
The pricingPolicy field on subscription lines has been renamed to appManagedPricingPolicy. This name change clarifies that the field contains app-managed pricing data that isn't used during contract calculation. The SubscriptionContractCalculation API calculates prices based on the current catalog and any applied discounts.
| Old field | New field |
|---|---|
pricingPolicy | appManagedPricingPolicy |
Anchor to Billing configurationBilling configuration
The nextBillingDate, minCycles, and maxCycles fields have been moved into a new AppManagedBillingConfig object. This consolidates app-managed billing metadata that isn't used during contract calculation into a single location.
| Old fields | New field |
|---|---|
nextBillingDate, minCycles, maxCycles | appManagedBillingConfig.nextBillingDate, appManagedBillingConfig.minCycles, appManagedBillingConfig.maxCycles |
Anchor to Multiple fulfillment configurationMultiple fulfillment configuration
Passing multiFulfillment is only required for pre-paid subscriptions that have multiple deliveries per billing period. Omit the multi fulfillment input to use the billing cadence as the delivery cadence.
Passing multiFulfillment is only required for pre-paid subscriptions that have multiple deliveries per billing period. Omit the multi fulfillment input to use the billing cadence as the delivery cadence.
The SubscriptionContractCalculation API introduces explicit control over the number of fulfillments per billing cycle through a new multiFulfillment field. Previously, the number of deliveries per billing cycle was implicitly derived from the relationship between billing and delivery policy intervals. The new approach makes this configuration explicit.
The multiFulfillment field accepts a SubscriptionMultipleFulfillmentConfigInput with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
cadence | Object | Yes | The cadence for calculating fulfillment dates. |
numberOfFulfillments | Integer | Yes | The number of fulfillments per billing cycle. Must be at least 2. |
Anchor to Explore the APIExplore the API
Use the GraphQL Admin API docs to explore the new API mutations, input types, and return types.
Anchor to Create a subscription contractCreate a subscription contract
Anchor to Old approach (SubscriptionDraft)Old approach (Subscription Draft)
With the SubscriptionDraft API, creating a contract requires multiple sequential mutations:
- Create a draft for the new contract using
subscriptionContractCreate. - Add lines to the draft using
subscriptionDraftLineAdd. - Commit the draft to create the contract using
subscriptionDraftCommit.
Anchor to New approach (SubscriptionContractCalculation)New approach (Subscription Contract Calculation)
With the SubscriptionContractCalculation API, you submit the complete desired state for the new contract in a single mutation. The server calculates pricing, applies taxes, and returns an immutable result snapshot.
-
Submit the new contract's desired state to create a contract calculation:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Create contract calculation
mutation CreateCalculation {subscriptionContractCreateCalculate(contractCreateInput: {# Control contract calculation behaviorwithMerchandiseCustomizations: true# Customer to create the contract for (must belong to the current shop)customerId: "gid://shopify/Customer/123"# Currency for the contractcurrencyCode: USD# Define all lines (at least one required)lines: [{productVariantLine: {productVariantId: "gid://shopify/ProductVariant/111"quantity: 1customAttributes: []discounts: [] # Line-scoped discounts; pass [] for none}}]# Define billing policybillingPolicy: {cadence: {unit: MONTHcount: 1}}# Define delivery policydeliveryPolicy: {multiFulfillment: {cadence: { unit: WEEK, count: 2 }numberOfFulfillments: 2}}# Define delivery method (use { none: true } for digital-only subscriptions)deliveryMethod: {shipping: {address: {firstName: "Quinn"lastName: "Ishida"address1: "123 Main St"city: "Toronto"provinceCode: "ON"countryCode: CAzip: "M5V 1A1"}deliveryPrice: {amount: "5.00"currencyCode: USD}}}# Define payment method (use { none: true } for no payment method)paymentMethod: {customerPaymentMethod: {id: "gid://shopify/CustomerPaymentMethod/456"}}# Define discount codes to apply (pass an empty array for none)discountCodes: []# Define manual discounts (pass an empty array for no manual discounts)# Supports two types: orderDiscount, deliveryDiscountmanualDiscounts: []# Define custom attributes (pass an empty array for none)customAttributes: []}) {subscriptionContractCalculation {... on SubscriptionContractCalculationPending {id}}userErrors {fieldmessagecode}}}JSON response
{"data": {"subscriptionContractCreateCalculate": {"subscriptionContractCalculation": {"id": "gid://shopify/SubscriptionContractCalculation/789"},"userErrors": []}}} -
Poll for the contract calculation result:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Poll for result
query PollCalculation {subscriptionContractCalculation(id: "gid://shopify/SubscriptionContractCalculation/789") {__typename... on SubscriptionContractCalculationPending {id}... on SubscriptionContractCalculationSuccess {idcalculatedContract {lines(first: 10) {edges {node {idvariantIdquantity}}}}}... on SubscriptionContractCalculationFailure {iderrors { code }}}}JSON response (pending)
{"data": {"subscriptionContractCalculation": {"__typename": "SubscriptionContractCalculationPending","id": "gid://shopify/SubscriptionContractCalculation/789"}}} -
Commit the contract calculation to create the contract:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Commit contract calculation
mutation CommitCalculation {subscriptionContractCalculationCommit(id: "gid://shopify/SubscriptionContractCalculation/789") {# The return type is a union: SubscriptionContract (create/update)# or SubscriptionBillingCycleEditedContract (billing cycle edit).contract {... on SubscriptionContract {idstatus}}userErrors {fieldmessage}}}JSON response
{"data": {"subscriptionContractCalculationCommit": {"contract": {"id": "gid://shopify/SubscriptionContract/999","status": "ACTIVE"},"userErrors": []}}}
Anchor to Update a subscription contractUpdate a subscription contract
Anchor to Old approach (SubscriptionDraft)Old approach (Subscription Draft)
With the SubscriptionDraft API, updating a contract requires multiple sequential mutations:
- Create a draft from the existing contract using
subscriptionContractUpdate. - Add, update, or remove lines using
subscriptionDraftLineAdd,subscriptionDraftLineUpdate, orsubscriptionDraftLineRemove. - Apply discounts using
subscriptionDraftDiscountCodeApply,subscriptionDraftDiscountAdd, orsubscriptionDraftFreeShippingDiscountAdd. - Commit the draft to apply changes using
subscriptionDraftCommit.
Anchor to New approach (SubscriptionContractCalculation)New approach (Subscription Contract Calculation)
With the SubscriptionContractCalculation API, you submit only the fields you want to change in a single mutation. Omitted fields are left unchanged.
Fetch the existing SubscriptionContract state using the subscriptionContract query, then apply any changes before submitting an updated version of the SubscriptionContract attributes for contract calculation.
-
Submit the desired state to create a contract calculation. On an update, only
withMerchandiseCustomizationsmust be provided; all other fields are optional and omitted fields are left unchanged:POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Create contract calculation
mutation UpdateCalculation {subscriptionContractUpdateCalculate(contractId: "gid://shopify/SubscriptionContract/123"contractUpdateInput: {# Control contract calculation behaviorwithMerchandiseCustomizations: true# Define all lines (replaces existing lines when provided)lines: [{productVariantLine: {# Include id to keep existing lineid: "gid://shopify/SubscriptionLine/existing-line-uuid"productVariantId: "gid://shopify/ProductVariant/111"quantity: 1customAttributes: []discounts: [] # Line-scoped discounts; pass [] for none}}{productVariantLine: {# Omit id to add new lineproductVariantId: "gid://shopify/ProductVariant/789"quantity: 2customAttributes: []discounts: []}}]# Define discount codes to apply (resolved into manual discounts; not persisted as codes)discountCodes: [{ redeemCode: "SAVE10" }]# Define all manual discounts (replaces existing manual discounts)# Supports two types: orderDiscount, deliveryDiscountmanualDiscounts: [{orderDiscount: {# Include id to keep an existing order discountid: "gid://shopify/SubscriptionDiscount/existing-discount-uuid"title: "10% off subscription"value: {percentage: 10}recurringCycleLimit: 0 # 0 = no limit}}{deliveryDiscount: {# Omit id for a new delivery discounttitle: "Free shipping for 3 months"value: {percentage: 100}recurringCycleLimit: 3 # Apply for 3 billing cycles}}]# Define billing policy (replaces existing billing policy)billingPolicy: {anchors: [{monthday: {dayOfMonth: 7}}]cadence: {unit: MONTHcount: 2}}# Define delivery policy (replaces existing delivery policy)deliveryPolicy: {anchors: {weekday: {dayOfWeek: TUESDAY}}multiFulfillment: {cadence: { unit: WEEK, count: 1 }numberOfFulfillments: 8}}# Define delivery method (replaces existing delivery method when provided)deliveryMethod:{pickup: {title: "Test",locationId: "gid://shopify/Location/1234",deliveryPrice: {amount: "0",currencyCode: GBP}}}# Define payment method (replaces existing payment method when provided)paymentMethod: {customerPaymentMethod: {id: "gid://shopify/CustomerPaymentMethod/1234"}}# Define note (replaces existing note)note: "Test"}) {subscriptionContractCalculation {... on SubscriptionContractCalculationPending {id}}userErrors {fieldmessage}}}JSON response
{"data": {"subscriptionContractUpdateCalculate": {"subscriptionContractCalculation": {"id": "gid://shopify/SubscriptionContractCalculation/789"},"userErrors": []}}} -
Poll for the contract calculation result:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Poll for result
query PollCalculation {subscriptionContractCalculation(id: "gid://shopify/SubscriptionContractCalculation/789") {__typename... on SubscriptionContractCalculationPending {id}... on SubscriptionContractCalculationSuccess {idcalculatedContract {lines(first: 10) {edges {node {idvariantIdquantity}}}}warnings {codemessage}projectedOrderTotals {subtotal { amount currencyCode }totalDelivery { amount currencyCode }totalTax { amount currencyCode }totalMerchandiseDiscounts { amount currencyCode }totalDeliveryDiscounts { amount currencyCode }total { amount currencyCode }}}... on SubscriptionContractCalculationFailure {iderrors { code }}}}JSON response (pending)
{"data": {"subscriptionContractCalculation": {"__typename": "SubscriptionContractCalculationPending","id": "gid://shopify/SubscriptionContractCalculation/789"}}} -
Commit the contract calculation to apply changes:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Commit contract calculation
mutation CommitCalculation {subscriptionContractCalculationCommit(id: "gid://shopify/SubscriptionContractCalculation/789") {# The return type is a union: SubscriptionContract (create/update)# or SubscriptionBillingCycleEditedContract (billing cycle edit).contract {... on SubscriptionContract {idstatus}}userErrors {fieldmessage}}}JSON response
{"data": {"subscriptionContractCalculationCommit": {"contract": {"id": "gid://shopify/SubscriptionContract/123","status": "ACTIVE"},"userErrors": []}}}
Anchor to Edit a single billing cycleEdit a single billing cycle
Apply a one-time change to a single billing cycle of a subscription without changing the recurring contract. Later cycles continue to bill from the underlying contract. A billing cycle can contain more than one delivery (for example, prepaid or multi-fulfillment subscriptions), and the edit applies to the whole cycle.
Anchor to Old approach (SubscriptionDraft)Old approach (Subscription Draft)
With the SubscriptionDraft object, editing one billing cycle requires multiple sequential mutations:
- Create a draft scoped to the target cycle using
subscriptionBillingCycleContractEdit. - Add, update, or remove lines and discounts on the draft using the
subscriptionDraft*mutations. - Commit the draft using
subscriptionBillingCycleContractDraftCommit.
Anchor to New approach (SubscriptionContractCalculation)New approach (Subscription Contract Calculation)
With the SubscriptionContractCalculation API, you submit the complete desired state for the cycle in a single mutation and identify the cycle with a selector, then poll and commit exactly as you do for a contract update.
-
Submit the desired state for the target cycle to create a contract calculation, identifying the cycle with a
billingCycleSelector. Select the cycle either:-
By its
index:mutation {subscriptionBillingCycleContractEditCalculate(contractId: "gid://shopify/SubscriptionContract/1"billingCycleSelector: { index: 2 }billingCycleEditInput: {withMerchandiseCustomizations: true,# The complete desired state for this cycle (lines, discounts, delivery, and so on).lines: [{productVariantLine: {productVariantId: "gid://shopify/ProductVariant/1"quantity: 2customAttributes: []discounts: [] # Line-scoped discounts; pass [] for none}}]}) {subscriptionContractCalculation {... on SubscriptionContractCalculationPending {id}}userErrors {fieldmessage}}} -
Or by a
datethat falls within the cycle:billingCycleSelector: { date: "2025-06-01T00:00:00Z" }
-
-
Poll the calculation and commit it as described in Update a subscription contract. You wait for the calculation to succeed, then commit it with
subscriptionContractCalculationCommit.
Committing creates and commits a new contract version scoped to that single billing cycle, leaving the recurring contract unchanged.
Anchor to Manage discountsManage discounts
The SubscriptionContractCalculation API splits discounts into two separate input fields that replace the multiple draft-based discount mutations:
discountCodes[]: Discount codes to apply during the calculation. Applied codes are resolved into manual discounts on the resulting contract; codes aren't persisted as codes, so there's nothing to preserve or replace across calculations. Defaults to an empty array.manualDiscounts[]: Manual order and delivery discounts. This is a@oneOfinput — each entry is either anorderDiscountor adeliveryDiscount.
For line-scoped discounts (discounts that apply to a single line), use the discounts[] field on each line input. See Line-scoped discounts.
The manualDiscounts array replaces all existing manual discounts on the contract. To keep an existing discount, include it in the array with its id. To remove a discount, omit it from the array. To add a new discount, include it without an id. To remove all manual discounts, pass an empty array ([]).
The manualDiscounts array replaces all existing manual discounts on the contract. To keep an existing discount, include it in the array with its id. To remove a discount, omit it from the array. To add a new discount, include it without an id. To remove all manual discounts, pass an empty array ([]).
Anchor to Discount typesDiscount types
| Discount type | Old mutation(s) | New input field | Description |
|---|---|---|---|
| Code discount | subscriptionDraftDiscountCodeApply | discountCodes[] | Apply a discount code by its redeem code. |
| Order discount | subscriptionDraftDiscountAdd, subscriptionDraftDiscountUpdate, subscriptionDraftDiscountRemove | manualDiscounts[].orderDiscount | Apply a fixed or percentage discount to all lines. |
| Delivery discount | subscriptionDraftFreeShippingDiscountAdd, subscriptionDraftFreeShippingDiscountUpdate | manualDiscounts[].deliveryDiscount | Apply a discount to shipping or delivery charges. |
| Line-scoped discount | subscriptionDraftDiscountAdd (line-scoped) | lines[].productVariantLine.discounts[] | Apply a fixed or percentage discount to a single line. |
Anchor to Apply a discount codeApply a discount code
To apply a discount code to an existing contract, include an entry in the discountCodes array:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Apply code discount
Anchor to Apply an order discountApply an order discount
Order discounts apply to all lines on the contract. They support percentage or fixed amount values:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Percentage order discount
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Fixed amount order discount
Anchor to Apply a delivery discountApply a delivery discount
Delivery discounts apply to shipping or delivery charges. For example, to offer free shipping:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Free shipping discount
Anchor to Combine multiple discountsCombine multiple discounts
You can apply multiple discounts of different types in a single calculation. Include discount codes in discountCodes[] and manual discounts in manualDiscounts[]:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Multiple discount types
Anchor to Line-scoped discountsLine-scoped discounts
To apply a discount to a single line, include it in the discounts[] field on that line's input. Each line-scoped discount takes title, value, and recurringCycleLimit fields, and optionally an id to update an existing line discount:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Line-scoped discount
Anchor to Remove all discountsRemove all discounts
To remove all manual discounts from a contract, pass an empty manualDiscounts array and an empty discountCodes array:
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Remove all discounts
Anchor to Fetch delivery optionsFetch delivery options
The deliveryOptions field on SubscriptionContractCalculationSuccess returns the available delivery options that the calculation computed. This field replaces the SubscriptionDraft.deliveryOptions field from the SubscriptionDraft API, and returns all available options.
The SubscriptionContractCalculationSuccess type also exposes:
warnings: Non-fatal warnings produced during the calculation. The calculation still succeeded; warnings inform the merchant about issues that should be reviewed before committing.projectedOrderTotals: Projected order totals (subtotal, total delivery, estimated tax, merchandise discounts, delivery discounts, and grand total) for the calculated contract. Null when the calculation only discovered delivery options for an address rather than calculating committed totals.
Each option is a SubscriptionContractCalculationDeliveryOption, which resolves to one of the following types:
SubscriptionContractCalculationShippingOptionSubscriptionContractCalculationLocalDeliveryOptionSubscriptionContractCalculationPickupOption
Fetching delivery options is opt-in. A calculation that sets or keeps a committed delivery method uses a faster path that doesn't look up the full set of rates, so it doesn't return the complete set of options. To fetch all available options for an address, provide fetchAvailableDeliveryOptions in the deliveryMethod input instead of a method. This fetches the options but leaves the contract's committed delivery method unchanged:
- On a create, the calculation commits no method.
- On an update, the calculation inherits the existing method.
deliveryMethod is a @oneOf input. Provide exactly one of shipping, localDelivery, pickup, none, or fetchAvailableDeliveryOptions. Providing a committed method and fetchAvailableDeliveryOptions in the same calculation is rejected.
deliveryMethod is a @oneOf input. Provide exactly one of shipping, localDelivery, pickup, none, or fetchAvailableDeliveryOptions. Providing a committed method and fetchAvailableDeliveryOptions in the same calculation is rejected.
Anchor to Old approach (SubscriptionDraft)Old approach (Subscription Draft)
With the SubscriptionDraft API, fetching and setting a delivery option requires multiple sequential mutations and a separate asynchronous query:
- Create a draft using
subscriptionContractUpdate(orsubscriptionContractCreate). - Query the
deliveryOptionsfield on the SubscriptionDraft with adeliveryAddress, polling until it returns a non-null result. Delivery option lookup is asynchronous and returnsnullwhile pending. - Set the selected method using
subscriptionDraftUpdatewith adeliveryMethod. - Commit the draft using
subscriptionDraftCommit.
Anchor to New approach (SubscriptionContractCalculation)New approach (Subscription Contract Calculation)
With the SubscriptionContractCalculation API, you fetch delivery options with a calculation, read them off the result, then submit a second calculation that commits the selected method.
-
Submit a contract calculation that fetches the delivery options by providing
fetchAvailableDeliveryOptionsin thedeliveryMethodinput:POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Fetch delivery options
mutation FetchDeliveryOptions {subscriptionContractUpdateCalculate(contractId: "gid://shopify/SubscriptionContract/123"contractUpdateInput: {withMerchandiseCustomizations: true# Fetch delivery options for this address without committing a methoddeliveryMethod: {fetchAvailableDeliveryOptions: {# Run delivery customization functions while discovering options (default)withDeliveryCustomizations: trueaddress: {firstName: "Quinn"lastName: "Ishida"address1: "123 Main St"city: "Toronto"provinceCode: "ON"countryCode: CAzip: "M5V 1A1"}}}}) {subscriptionContractCalculation {... on SubscriptionContractCalculationPending {id}}userErrors {fieldmessage}}}JSON response
{"data": {"subscriptionContractUpdateCalculate": {"subscriptionContractCalculation": {"id": "gid://shopify/SubscriptionContractCalculation/789"},"userErrors": []}}} -
Poll for the contract calculation result (see the recommended polling strategy) and read the
deliveryOptions. Each option type is part of a union, so use inline fragments to select its fields:POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Read delivery options
query PollDeliveryOptions {subscriptionContractCalculation(id: "gid://shopify/SubscriptionContractCalculation/789") {__typename... on SubscriptionContractCalculationPending {id}... on SubscriptionContractCalculationSuccess {iddeliveryOptions {__typename... on SubscriptionContractCalculationShippingOption {titlecodeprice { amount currencyCode }}... on SubscriptionContractCalculationLocalDeliveryOption {titlecodephoneRequiredprice { amount currencyCode }}... on SubscriptionContractCalculationPickupOption {titlecodepickupTimeprice { amount currencyCode }location { id name }}}}... on SubscriptionContractCalculationFailure {iderrors { code }}}}JSON response (success)
{"data": {"subscriptionContractCalculation": {"__typename": "SubscriptionContractCalculationSuccess","id": "gid://shopify/SubscriptionContractCalculation/789","deliveryOptions": [{"__typename": "SubscriptionContractCalculationShippingOption","title": "Standard","code": "Standard","price": { "amount": "5.00", "currencyCode": "CAD" }},{"__typename": "SubscriptionContractCalculationShippingOption","title": "Express","code": "Express","price": { "amount": "15.00", "currencyCode": "CAD" }}]}}} -
Submit a second contract calculation that commits the selected option as the
deliveryMethod. Map the chosen option'stitleandcodedirectly, and map itspriceto the method'sdeliveryPricefield:POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Select a delivery option
mutation SelectDeliveryOption {subscriptionContractUpdateCalculate(contractId: "gid://shopify/SubscriptionContract/123"contractUpdateInput: {withMerchandiseCustomizations: true# Commit the option chosen from deliveryOptionsdeliveryMethod: {shipping: {address: {firstName: "Quinn"lastName: "Ishida"address1: "123 Main St"city: "Toronto"provinceCode: "ON"countryCode: CAzip: "M5V 1A1"}title: "Standard"code: "Standard"deliveryPrice: {amount: "5.00"currencyCode: CAD}}}}) {subscriptionContractCalculation {... on SubscriptionContractCalculationPending {id}}userErrors {fieldmessage}}} -
Poll for the second contract calculation result, then commit it with
subscriptionContractCalculationCommitto apply the selected delivery method to the contract. This follows the same poll-then-commit pattern as the create and update flows above.
Anchor to Delivery customization functionsDelivery customization functions
When a calculation fetches delivery options, the withDeliveryCustomizations field on fetchAvailableDeliveryOptions controls whether delivery customization functions run:
true(the default): Functions run, so the returneddeliveryOptionsreflect any options the merchant's customizations hide, rename, or re-order.false: Functions are bypassed, and the raw delivery options are returned.
The SubscriptionDraft API doesn't run delivery customization functions when computing deliveryOptions. For shops with active delivery customizations, the set of options returned by the new API can therefore differ from the old field.
withDeliveryCustomizations applies only to calculations that include fetchAvailableDeliveryOptions. Calculations that set a committed delivery method and recurring billing attempts always bypass delivery customization functions, because the contract's committed delivery method is fixed and re-running functions can remove it.
withDeliveryCustomizations applies only to calculations that include fetchAvailableDeliveryOptions. Calculations that set a committed delivery method and recurring billing attempts always bypass delivery customization functions, because the contract's committed delivery method is fixed and re-running functions can remove it.
Anchor to Input types referenceInput types reference
Explore the API to understand the full input schema, starting from the top-level input types.
Explore the API to understand the full input schema, starting from the top-level input types.
Anchor to Top-level input typesTop-level input types
| Input type | Description |
|---|---|
SubscriptionContractCalculationContractCreateInput | Inputs for creating a new subscription contract. |
SubscriptionContractCalculationContractUpdateInput | Input for updating an existing subscription contract. |
SubscriptionContractCalculationBillingCycleEditInput | Input for editing a single billing cycle of an existing contract. |
Anchor to Union input typesUnion input types
The inputs for lines, manualDiscounts, and deliveryMethod fields use the GraphQL @oneOf directive. You must provide exactly one of the available input options for each entry. discountCodes is a separate non-union array. Refer to the following sections for the specific options available for each field.
The inputs for lines, manualDiscounts, and deliveryMethod fields use the GraphQL @oneOf directive. You must provide exactly one of the available input options for each entry. discountCodes is a separate non-union array. Refer to the following sections for the specific options available for each field.
Anchor to [object Object]SubscriptionContractCalculationLineInput
SubscriptionContractCalculationLineInputLines use a @oneOf input pattern. Provide exactly one of the following options:
productVariantLine: For product variant lines.customLine: For custom lines without a variant.productVariantParentLine: For product variant bundle parent lines (available when bundle support is enabled).
The lines array replaces all existing lines. Include all lines that you want to keep and omit any that you want to remove. Use line IDs to identify existing lines for updates.
The lines array replaces all existing lines. Include all lines that you want to keep and omit any that you want to remove. Use line IDs to identify existing lines for updates.
Anchor to [object Object]SubscriptionContractCalculationProductVariantLineInput
SubscriptionContractCalculationProductVariantLineInputProduct variant lines accept the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | ID | No | The ID of an existing line to update. Omit for new lines. |
productVariantId | ID | Yes | The ID of the product variant for this line. |
quantity | Integer | Yes | The quantity of the product variant. Must be at least 1. |
priceOverride | MoneyInput | No | Override price for this line item. |
customAttributes | [AttributeInput] | Yes (default []) | Custom attributes for this subscription line. |
appManagedPricingPolicy | SubscriptionContractCalculationAppManagedPricingPolicyInput | No | App-managed pricing policy metadata for this line. |
originSellingPlanId | ID | No | The ID of the selling plan this line was originally created with. Used to find the delivery profile. |
sellingPlanName | String | No | The selling plan name for this line. Defaults to the origin selling plan's current name. |
discounts | [SubscriptionContractCalculationLineDiscountInput] | Yes | Line-scoped manual discounts. Pass [] for none. |
Anchor to [object Object]SubscriptionContractCalculationManualDiscountInput
SubscriptionContractCalculationManualDiscountInputManual discounts use a @oneOf input pattern. Provide exactly one of the following options:
orderDiscount: For order discounts that apply to all lines on the contract. Takestitle,value, andrecurringCycleLimitfields. Optionally takes anidto update an existing discount.deliveryDiscount: For delivery or shipping discounts. Takestitle,value, andrecurringCycleLimitfields. Optionally takes anidto update an existing discount.
The manualDiscounts array replaces all existing manual discounts. Include all discounts that you want to keep and omit any that you want to remove. Use discount IDs to identify existing discounts for updates.
The manualDiscounts array replaces all existing manual discounts. Include all discounts that you want to keep and omit any that you want to remove. Use discount IDs to identify existing discounts for updates.
Anchor to [object Object]SubscriptionContractCalculationDiscountCodeInput
SubscriptionContractCalculationDiscountCodeInputDiscount codes are provided in the separate discountCodes[] array (not part of manualDiscounts). Each entry takes:
redeemCode: The discount code string to apply.
Applied codes are resolved into manual discounts on the resulting contract; codes aren't persisted as codes, so there's nothing to preserve or replace across calculations.
Anchor to [object Object]SubscriptionContractCalculationDiscountValueInput
SubscriptionContractCalculationDiscountValueInputDiscount values use a @oneOf input pattern. Provide exactly one of the following options:
percentage: An integer percentage value (0–100).fixedAmount: A fixed money amount withappliesOnEachItem(boolean) andamount(MoneyInput).
Anchor to [object Object]SubscriptionContractCalculationDeliveryMethodInput
SubscriptionContractCalculationDeliveryMethodInputDelivery methods use a @oneOf input pattern. Provide exactly one of the following options:
shipping: For shipping deliveries.localDelivery: For local delivery.pickup: For pickup.none: Explicitly set no delivery method (digital-only subscriptions). Must betrue.fetchAvailableDeliveryOptions: To fetch the available delivery options for an address without committing a delivery method. See Fetch delivery options.
Anchor to [object Object]SubscriptionContractCalculationPaymentMethodInput
SubscriptionContractCalculationPaymentMethodInputPayment methods use a @oneOf input pattern. Provide exactly one of the following options:
customerPaymentMethod: A vaulted customer payment method. Takes anidfield (ID) for the customer payment method.none: Explicitly set no payment method. Must betrue.
Anchor to [object Object]SubscriptionContractCalculationFetchDeliveryOptionsInput
SubscriptionContractCalculationFetchDeliveryOptionsInputUse this input under deliveryMethod.fetchAvailableDeliveryOptions to fetch available delivery options without setting a delivery method.
address: The delivery address to fetch options for.withDeliveryCustomizations: Optional Boolean. Whentrue(the default), delivery customization functions run while fetching options. Whenfalse, delivery customization functions are bypassed and the raw options are returned.
Anchor to Handle asynchronous resultsHandle asynchronous results
The SubscriptionContractCalculation API uses asynchronous processing because it aligns contract editing with all other checkout surfaces across Shopify, including subscription billing attempts, checkout, and draft orders.
Most contract calculations complete in less than one second, but the system needs to account for network latency and errors in third-party services. The asynchronous model handles these situations without exposing transient errors or imposing strict API request timeouts.
You can handle contract calculation results by polling the contract calculation query or subscribing to webhooks for event-driven processing.
Anchor to Contract calculation statesContract calculation states
A contract calculation progresses through several states from creation to completion. Understanding these states helps you build integrations that handle all possible outcomes.
| State | Description | Webhook |
|---|---|---|
| Initiated | The contract calculation is created and waiting to be processed. | — |
| Processing | The contract calculation is running with functions and external services. | — |
| Succeeded | The contract calculation is ready for review and commit. | subscription_contract_calculations/succeed |
| Failed | The contract calculation failed. Errors are available on the result. | subscription_contract_calculations/fail |
| Voided | The contract calculation wasn't processed due to infrastructure issues. | — |
| Committed | The contract calculation has been committed and is now active. | subscription_contracts/create or subscription_contracts/update |
The subscriptionContractCalculation query returns a union with three possible GraphQL types. The internal states map to these types as follows:
| GraphQL type | Internal states |
|---|---|
SubscriptionContractCalculationPending | Initiated, Processing |
SubscriptionContractCalculationSuccess | Succeeded, Committed |
SubscriptionContractCalculationFailure | Failed, Voided |
Anchor to Polling patternPolling pattern
After creating a contract calculation, poll the subscriptionContractCalculation query until processing completes.
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
GraphQL query
Anchor to Recommended polling strategyRecommended polling strategy
- Initial wait: Wait 1 second after the mutation before the first poll.
- Poll interval: Poll every 500ms.
- Timeout: Stop polling after 30 seconds, or when the contract calculation result is in a failed state.
Anchor to Retry safetyRetry safety
All contract calculation operations are safe to retry:
- Calculate: Safe to retry. Creates a new contract calculation each time.
- Commit: Safe to retry. If already committed, it returns success.
- Polling: Safe to call as many times as needed.
Anchor to Webhook integrationWebhook integration
Subscribe to webhooks for event-driven processing instead of polling.
| Webhook topic | Description |
|---|---|
subscription_contract_calculations/succeed | Fires when a contract calculation succeeds and the contract calculation is ready to commit. |
subscription_contract_calculations/fail | Fires when a contract calculation fails and errors are available on the result. |
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
Create webhook subscription
JSON response
Webhook payload
Anchor to Backwards compatibilityBackwards compatibility
The SubscriptionDraft API will remain available but will not support the new capabilities of the SubscriptionContractCalculation API.
Anchor to Cross-API compatibilityCross-API compatibility
Compatibility between APIs is still being evaluated. This may work in one of the following ways:
- Allow both the
SubscriptionDraftandSubscriptionContractCalculationAPIs to be used on the same contracts. - Prevent contracts with changes committed through the
SubscriptionContractCalculationAPI from being edited using theSubscriptionDraftAPI.
Compatibility between APIs is still being evaluated. This may work in one of the following ways:
- Allow both the
SubscriptionDraftandSubscriptionContractCalculationAPIs to be used on the same contracts. - Prevent contracts with changes committed through the
SubscriptionContractCalculationAPI from being edited using theSubscriptionDraftAPI.