A selling plan defines the offer. It does not bill anyone. The Shopify selling plans API lets your app attach a recurring purchase option to a product, so a shopper can check out on “every month, 15% off”, and Shopify turns that first checkout into a subscription contract. Every charge after it has to be raised by your app.
That division is the whole shape of the work. Shopify records what each contract owes and when, then waits for your app to ask for the money. Miss the date and nothing happens.
What the Shopify selling plans API actually creates
Three nested objects. A SellingPlanGroup is the offer as a merchant thinks of it, with a customer-facing name, a merchantCode only staff see, and an options list that becomes the storefront dropdown. Inside it sit one or more SellingPlan records, each holding the actual terms. The group is then associated with products or variants.
Each plan carries four policies, and Shopify’s selling plans overview defines them plainly. The billing policy is “the billing frequency associated with the subscription. For example, bill every week, or bill every three months.” The delivery policy is the same idea for fulfilment. The inventory policy decides whether “inventory can be committed when the order is created, or when the order is fulfilled”. The pricing policy applies the discount, either fixed for the life of the subscription or stepped across cycles.
Billing and delivery are deliberately separate. Charge quarterly and ship monthly, and the two policies carry different intervals on the same plan.
Which apps are allowed to use it
Run this check before you write any code, because it can end the project. Custom apps created in the Shopify admin cannot do subscriptions at all. The wording on the same page is blunt: “Custom apps created in the Shopify admin can’t use subscriptions, pre-order or TBYB because these apps can’t use extensions or request access to protected scopes.” If you are building for one store, build it as a custom app in the Partner Dashboard instead.
Then you request access. The subscriptions scopes are protected, so you apply through the app’s API access page, and Shopify’s getting started guide warns that “the request for some scopes can take up to 7 business days to be approved”. Approval is what lets you add read_customer_payment_methods and write_own_subscription_contracts to the app. A working subscriptions app also needs read_own_subscription_contracts and write_products for the plans themselves, read_all_orders for order history, and the protected customer data fields set to App Functionality.
Put that week in the plan before anyone commits to a launch date.
How to build a subscribe and save plan
One mutation creates the group, the plans and the product association together. This is written against Admin GraphQL 2026-07, the latest stable version as of September 2026, and schema-validated against it.
mutation createSubscribeAndSave {
sellingPlanGroupCreate(
input: {
name: "Subscribe and save"
merchantCode: "subscribe-and-save"
options: ["Delivery every"]
position: 1
sellingPlansToCreate: [
{
name: "Every month"
options: ["1 month"]
category: SUBSCRIPTION
billingPolicy: { recurring: { interval: MONTH, intervalCount: 1 } }
deliveryPolicy: { recurring: { interval: MONTH, intervalCount: 1 } }
pricingPolicies: [
{
fixed: {
adjustmentType: PERCENTAGE
adjustmentValue: { percentage: 15.0 }
}
}
]
inventoryPolicy: { reserve: ON_SALE }
}
]
}
resources: { productIds: ["gid://shopify/Product/108828309"] }
) {
sellingPlanGroup {
id
sellingPlans(first: 5) {
nodes { id name }
}
}
userErrors { field message }
}
}
Add more entries to sellingPlansToCreate for weekly and fortnightly variants of the same offer. Later attachments do not need the group rebuilt; sellingPlanGroupAddProducts and sellingPlanGroupAddProductVariants extend an existing group in place.
On the storefront, a plan is just an extra ID on the cart line. CartLineInput takes an optional sellingPlanId, and the line comes back with a sellingPlanAllocation describing what the shopper actually agreed to.
mutation addSubscriptionLine($cartId: ID!, $variantId: ID!, $sellingPlanId: ID!) {
cartLinesAdd(
cartId: $cartId
lines: [{ merchandiseId: $variantId, quantity: 1, sellingPlanId: $sellingPlanId }]
) {
cart {
id
lines(first: 10) {
nodes {
id
sellingPlanAllocation {
sellingPlan { id name }
}
}
}
}
userErrors { field message }
}
}
If you are new to the cart mutations themselves, our walkthrough of the Storefront API cart covers create, mutate and the checkout handoff.
Who charges the customer every month
Shopify handles the first order. After checkout it generates the subscription contract and tells your app through the subscription_contracts/create webhook, which the contracts documentation notes “is deferred until after the order is created”. The same page warns that contracts “might not be immediately available when an order is created”, so read the webhook instead of polling straight after an order lands.
Everything after that is your scheduler. Each contract has billing cycles, indexed from 1, and the index does not reset when the contract is edited. Every cycle exposes a billingAttemptExpectedDate, described in Shopify’s billing cycles guide as “the single date in the cycle on which the app is expected to bill the customer”. Find the cycles due today, charge them.
mutation billSubscriptionCycle($contractId: ID!, $index: Int!, $key: String!) {
subscriptionBillingAttemptCreate(
subscriptionContractId: $contractId
subscriptionBillingAttemptInput: {
billingCycleSelector: { index: $index }
idempotencyKey: $key
}
) {
subscriptionBillingAttempt {
id
idempotencyKey
state {
... on SubscriptionBillingAttemptPendingState { processing }
... on SubscriptionBillingAttemptSuccessState { order { id name } }
... on SubscriptionBillingAttemptFailedState {
error {
... on SubscriptionBillingAttemptPaymentError { code }
}
}
}
}
userErrors { field message }
}
}
Two details there are easy to get wrong. The idempotencyKey is what stops a retried job double-charging a customer, so derive it from the contract ID and the cycle index and the same cycle will always produce the same key. The second is where you read the result. On 2026-07 the schema marks ready, errorMessage, order and processingError as deprecated in favour of state, while Shopify’s own reference example for the mutation still selects ready. The state union splits into pending, success, failed and action-required, each carrying its own payload.
Failures are yours to handle too. Shopify’s guide to building a subscription contract is explicit that “it’s up to apps to attempt re-billing for failed payment attempts”, so a dunning schedule, a cap on retries and a path to pausing the contract all have to be built. Subscribe to subscription_billing_attempts/success and subscription_billing_attempts/failure to drive it, and verify those payloads properly; our guide to Shopify webhook HMAC verification covers the signature check and deduplication.
What happens when the merchant uninstalls
The plans disappear. Shopify’s SellingPlan reference carries a caution that “selling plans and associated records are automatically deleted 48 hours after a merchant uninstalls” the app that created them. The build a selling plan guide spells out the scope. Groups, plans, policies and the associations to products and variants all go, while “products and product variants aren’t deleted”.
Forty-eight hours is enough to recover from an accidental uninstall, and only if you already hold a copy. Keep your own record of every group and plan you create, keyed to the shop, so a reinstall can rebuild them.
If you need subscriptions on one store and nothing more exotic than a monthly box, install a vetted app from the App Store and spend the saved fortnight elsewhere. Build your own when the billing logic is the product: usage-based pricing, bundles that change per cycle, contracts that have to reconcile against a system outside Shopify. Whichever way you go, start the access request first, because seven business days of waiting is the one part of the timeline you cannot compress.
Whoooop builds and maintains Shopify storefronts and private apps for UK merchants, including subscription work of this shape. If a recurring offer is on your roadmap and you want the billing side reviewed before it goes near real cards, our Shopify development team can take a look.