Skip to main content
The Shopify Subscriptions Reference App provides comprehensive contract management capabilities, allowing merchants to control subscription lifecycles and update contract details.

Core Operations

Pause Subscriptions

Temporarily stop a subscription while retaining customer information

Resume Subscriptions

Reactivate paused subscriptions and continue billing cycles

Cancel Subscriptions

Permanently end a subscription contract

Update Details

Modify subscription address, payment methods, and line items

Pause a Subscription

Merchants can pause active subscriptions to temporarily stop billing without canceling the contract.
1

Navigate to Contract Details

Access the subscription contract from the contracts list page
2

Click Pause Action

Select the pause action from the contract details page
3

Confirm Pause

The subscription status changes to PAUSED and billing cycles are suspended

Implementation

The pause functionality is implemented in app/routes/app.contracts.$id.pause/route.ts:12-37:
export async function action({
  request,
  params,
}: ActionFunctionArgs): Promise<TypedResponse<WithToast>> {
  const {admin, session} = await authenticate.admin(request);
  const contractId = composeGid('SubscriptionContract', params.id || '');

  try {
    await new SubscriptionContractPauseService(
      admin.graphql,
      session.shop,
      contractId,
    ).run();

    return json(toast(t('actions.pause.success')));
  } catch (e) {
    return json(toast(t('actions.pause.error'), {isError: true}));
  }
}
Paused subscriptions retain all contract details including customer information, line items, and billing schedules.

Resume a Subscription

Paused subscriptions can be reactivated to continue regular billing cycles.
1

Access Paused Contract

Navigate to a contract with PAUSED status
2

Click Resume Action

Select the resume action to reactivate billing
3

Billing Resumes

The subscription status changes to ACTIVE and the next billing cycle is scheduled

Implementation

The resume functionality is implemented in app/routes/app.contracts.$id.resume/route.ts:15-40:
export async function action({
  request,
  params,
}: ActionFunctionArgs): Promise<TypedResponse<WithToast>> {
  const {admin, session} = await authenticate.admin(request);
  const contractId = composeGid('SubscriptionContract', params.id || '');

  try {
    await new SubscriptionContractResumeService(
      admin.graphql,
      session.shop,
      contractId,
    ).run();

    return json(toast(t('actions.resume.success')));
  } catch (e) {
    return json(toast(t('actions.resume.error'), {isError: true}));
  }
}

Cancel a Subscription

Canceling a subscription permanently ends the contract and stops all future billing.
1

Open Cancel Modal

Click the cancel action on the contract details page
2

Confirm Cancellation

Confirm the irreversible cancellation action
3

Contract Terminated

The subscription status changes to CANCELLED and no further billing occurs

Implementation

The cancel functionality is implemented in app/routes/app.contracts.$id.cancel/route.ts:12-34:
export async function action({
  request,
  params,
}: ActionFunctionArgs): Promise<TypedResponse<WithToast<{error?: boolean}>>> {
  const {admin, session} = await authenticate.admin(request);
  const contractId = composeGid('SubscriptionContract', params.id || '');

  try {
    await new SubscriptionContractCancelService(
      admin.graphql,
      session.shop,
      contractId,
    ).run();

    return json(toast(t('actions.cancel.success')));
  } catch (e) {
    return json({
      error: true,
      ...toast(t('actions.cancel.error'), {isError: true}),
    });
  }
}
Canceling a subscription is permanent and cannot be undone. Consider pausing instead if temporary suspension is needed.

Update Shipping Address

Merchants can update the shipping address for subscription deliveries.
1

Open Address Modal

Click the edit address action on the contract overview card
2

Enter New Address

Provide the updated shipping address details
3

Select Delivery Method

Choose an appropriate delivery method for the new address
4

Save Changes

The address update is committed to the subscription contract

Implementation

Address updates use the SubscriptionContractDraft model in app/routes/app.contracts.$id.address-update/route.ts:15-57:
export async function action({
  request,
  params,
}: ActionFunctionArgs): Promise<TypedResponse<WithToast<{error?: boolean}>>> {
  const body = await request.formData();
  const {admin, session} = await authenticate.admin(request);
  const contractId = composeGid('SubscriptionContract', params.id || '');
  const address = body.get('address') as string;
  const deliveryMethodName = body.get('deliveryMethodName') as string;

  const addressObject = JSON.parse(address);

  // Build draft from existing contract
  const draft = await buildDraftFromContract(
    session.shop,
    contractId,
    admin.graphql,
  );

  // Update address and delivery method
  const result = await draft.updateAddress(addressObject, deliveryMethodName);

  if (!result) {
    return json({
      error: true,
      ...toast(t('edit.actions.updateAddress.error'), {isError: true}),
    });
  }

  // Commit the draft to apply changes
  const draftCommitted = await draft.commit();

  if (!draftCommitted) {
    return json({
      error: true,
      ...toast(t('edit.actions.updateAddress.error'), {isError: true}),
    });
  }

  return json(toast(t('edit.actions.updateAddress.success')));
}
Address updates use the Shopify Subscription Contract Draft API to ensure atomic updates and maintain data consistency.

Update Payment Method

Merchants can trigger payment method update emails to customers.
1

Open Payment Method Card

Navigate to the customer payment method section
2

Send Update Email

Click to send a payment method update request to the customer
3

Customer Updates

The customer receives an email with a secure link to update their payment method

Implementation

Payment method updates are handled in app/routes/app.contracts.$id.payment-update/route.ts:16-72:
export async function action({
  request,
  params,
}: ActionFunctionArgs): Promise<TypedResponse<WithToast<{error?: boolean}>>> {
  const {admin} = await authenticate.admin(request);
  const contractId = composeGid('SubscriptionContract', params.id || '');

  // Fetch payment details
  const response = await admin.graphql(PaymentUpdateDetailsQuery, {
    variables: {contractId},
  });

  const {data} = await response.json();
  const {name: shopName, contactEmail: shopEmail} = data?.shop;
  const subscriptionContract = data?.subscriptionContract;
  const customerEmail = subscriptionContract?.customer?.email;
  const customerPaymentMethodId =
    subscriptionContract?.customerPaymentMethod?.id;

  // Send payment method update email
  const sendEmailResponse = await sendPaymentMethodUpdateEmail(
    admin.graphql,
    customerPaymentMethodId,
    {
      from: shopEmail,
      to: customerEmail,
      subject: t('paymentMethodDetails.emailModal.subjectMessage', {shopName}),
    },
  );

  const {customer, userErrors} = sendEmailResponse || {};

  if (!customer || userErrors?.length) {
    return json({
      error: true,
      ...toast(t('paymentMethodDetails.emailModal.errorMessage'), {
        isError: true,
      }),
    });
  }

  return json(toast(t('paymentMethodDetails.emailModal.successMessage')));
}
Payment method updates are initiated via email for security. Customers use a secure Shopify-hosted page to update their payment information.

Edit Subscription Details

Merchants can modify subscription line items, quantities, and pricing:
  • Edit line item quantities
  • Update product variants
  • Modify pricing (with custom pricing modal)
  • Add or remove line items
These operations use the SubscriptionContractDraft API to ensure consistent updates across the contract.

Contract Status Flow

Best Practices

Use Pause for Temporary Stops

Recommend pausing instead of canceling when customers need a break

Validate Address Changes

Ensure delivery methods are compatible with new shipping addresses

Communicate Changes

Send notifications to customers when contract details change

Handle Errors Gracefully

Provide clear error messages and retry options for failed operations

Build docs developers (and LLMs) love