This one started with a community thread I couldn't stop thinking about: a pure-B2B merchant — no D2C checkout at all, ~200k SKUs, selling maintenance bundles — wanted to sell those bundles as quarterly subscriptions. The only answer the thread produced was "make a draft order every month by hand." I've seen that advice given out for years, so I went to check whether it even works at the API level. It doesn't — and the reason is written down in Shopify's own docs. Here is the wall, and the three doors around it.
Root cause: a one-sentence wall
Shopify's B2B developer documentation states it in the Limitations section:
"B2B doesn't support purchase options, such as subscriptions, pre-orders, and try before you buy."
"Purchase options" is the umbrella term, and subscriptions are implemented through selling plans: a SellingPlan defines how a product can be sold and purchased through recurring billing — when to bill, when to fulfill, what pricing adjustments to apply — and a SellingPlanGroup attaches those plans to products and variants. No purchase options in B2B means no selling plans in the B2B checkout, which means no SubscriptionContract — the object that defines recurring purchases for a customer and tracks billing attempts, payment status, and generated orders — is ever born there.
Why "use a draft order" fails at the API level
Draft orders are the standard B2B instrument: the API lets you create draft orders for company contacts to review and approve, attach paymentTerms, send an invoiceUrl. So the community reflex is "sell the subscription through a draft order."
Open the Admin GraphQL reference for DraftOrder and DraftOrderLineItem (latest stable, 2026-07) and read the full field list: there is no selling-plan field anywhere — not on the draft order, not on its line items. You cannot smuggle a selling plan through a draft order, so a draft order cannot create a subscription contract either. The wall has no cracks on this side.
Minimal reproduction
You don't need a Plus sandbox to confirm the wall; you need the schema. Run an introspection query against any store's Admin GraphQL API:
{
draftLineItem: __type(name: "DraftOrderLineItem") {
fields { name }
}
draftOrder: __type(name: "DraftOrder") {
fields { name }
}
}
Grep the result for sellingPlan: zero hits on either type, matching the published object reference. What you will find on DraftOrder are the B2B-native fields: paymentTerms, purchasingEntity, invoiceUrl, and the deposit-aware totals (amountDueNowSet / amountDueLaterSet — when payment terms exist, "due now" is the deposit and "due later" is the remainder).
Two checks, five minutes, wall confirmed from primary sources.
Door 1 — the D2C lane (no app required)
The B2B object model has a built-in escape hatch: a company contact is associated with a retail customer record. That customer record can buy through the normal online-store checkout, where purchase options are supported. So the subscription is sold on the D2C lane to the contact's customer account, while the rest of the relationship stays B2B.
The trade-offs are real: that subscription order won't carry B2B catalog pricing, won't inherit payment terms, and won't roll up into company-level reporting. For a pure-B2B merchant it's a compromise lane, not a native feature.
Door 2 — the API door: build the contract without checkout
Here's the part almost nobody mentions: a subscription contract does not have to be born at checkout. The Admin GraphQL API exposes subscriptionContractCreate, which — per the reference — "creates a subscription contract draft, which is an intention to create a new subscription." You supply the customer, a customer payment method, and billing/delivery policies; you then finalize with subscriptionDraftCommit. No storefront involved:
# Adapted from the official mutation example (API version 2026-07)
mutation createSubscriptionContract($input: SubscriptionContractCreateInput!) {
subscriptionContractCreate(input: $input) {
draft { id }
userErrors { field message }
}
}
{
"input": {
"customerId": "gid://shopify/Customer/544365967",
"currencyCode": "USD",
"nextBillingDate": "2026-09-01T09:00:00Z",
"contract": {
"status": "ACTIVE",
"paymentMethodId": "gid://shopify/CustomerPaymentMethod/b7cc6e3267aace169e516ed48be72dff",
"billingPolicy": { "minCycles": 3, "maxCycles": 12, "intervalCount": 1, "interval": "MONTH" },
"deliveryPolicy": { "intervalCount": 1, "interval": "MONTH" }
}
}
}
Once the draft commits, the contract exists. Billing then runs through subscriptionBillingAttemptCreate, which charges the contract for the current (or a selected) billing cycle and creates an Order on success; failed attempts expose a processingError, and the idempotencyKey argument exists specifically to prevent duplicate charges on retries.
The fine print, all verifiable in the reference:
- Both mutations require the
write_own_subscription_contractsaccess scope, and the acting user needs themanage_orders_informationpermission. - The contract's
customerPaymentMethodmust already exist — vaulting a card is a separate problem the API doesn't solve for you. - The contract belongs to the customer, not the company. B2B pricing, terms, and reporting still don't apply.
- Selling plans and their associated records are automatically deleted 48 hours after the merchant uninstalls the app that created them — if you build on this, back those records up.
Door 3 — emulate recurrence with native B2B primitives
If "real subscriptions" are impossible but "recurring charges on a schedule" is the actual business need, the native building blocks are draft orders with paymentTerms: deposit semantics via amountDueNowSet/amountDueLaterSet, an invoiceUrl to collect payment, and company association via purchasingEntity. You generate the recurring draft orders on a schedule (your own automation) instead of a billing policy doing it.
A community contributor on the same thread described one concrete assembly of this — vaulting a B2B customer's card and using Shopify Flow's payment-schedule trigger with a "charge vaulted payment" action to automate the collection. I flag that specific assembly as a field report: it's practitioner testimony from the thread, not something re-verified line-by-line in the merchant documentation, so treat the exact trigger/action names as "confirm in your own admin" rather than gospel.
Checklist
- Accept the wall: B2B doesn't support purchase options (subscriptions, pre-orders, try before you buy) — it's documented, not a bug.
- Stop trying to attach selling plans to draft orders: neither
DraftOrdernorDraftOrderLineItemhas a selling-plan field in the Admin GraphQL API. - Decide what the business actually needs: contract semantics (Door 2), or just recurring collection (Door 3 / Door 1).
- Door 1: route the subscription through the contact's retail customer record on the D2C checkout; document that B2B pricing/terms/reporting don't apply.
- Door 2: use
subscriptionContractCreate→subscriptionDraftCommit→subscriptionBillingAttemptCreate; confirmwrite_own_subscription_contractsscope +manage_orders_informationpermission; solve card vaulting first; back up selling-plan data against the 48-hour uninstall deletion rule. - Door 3: model recurrence with draft orders +
paymentTerms+ invoice collection; treat community Flow recipes as field reports to verify in your own admin. - Whatever you build: a subscription order created outside B2B will not appear in company-level B2B reporting — set stakeholder expectations in writing.
Originally published at https://clawmama.run/blog/shopify-b2b-subscription-gap/
Top comments (0)