GraphQL Account API: Unified Billing
Below are examples of queries and mutations. They demonstrate how to use the Unified Billing features available through the BigCommerce GraphQL Account API.
Use the API by submitting POST requests to https://api.bigcommerce.com/accounts/{{your_partner_account_uuid}}/graphql
.
We have prepared a Postman collection you can import into your Postman workspace to interact with the Unified Billing API. It includes example requests for common operations such as creating checkouts, querying checkouts, querying subscriptions, and canceling subscriptions: Unified Billing Postman Collection (opens in a new tab)
Possible Implementation Strategies
Using the information gathered from the prerequisites section (opens in a new tab), you can begin integrating with the Unified Billing API. The following examples are meant to be a helpful guide for implementation. When considering how to utilize the API, keep in mind the specific needs of your app.
Creating Subscriptions
When a merchant wants to purchase a subscription after installing your app, use the Create Checkout Mutation to generate a checkout URL for them to complete the purchase.
Implementation Options:
Option A: Polling Checkout Status
- Cache the checkout URL for each merchant store
- Periodically poll the Checkout Query (opens in a new tab) until the status changes to
COMPLETE
- Once complete, retrieve the
subscriptionId
from the checkout response
Option B: Polling Subscription Status (Recommended)
- Instead of monitoring individual checkouts, gate access to premium features until the Subscriptions Query (opens in a new tab) returns an active subscription
- This approach is more efficient and provides better reliability for verifying completed purchases
Managing Access Control
Use the Subscriptions Query (opens in a new tab) to verify which merchants have active subscriptions. Filter by scopeId
(store hash) to find the subscription for a specific merchant.
Caching Strategy:
- Cache subscription results to reduce API calls
- Invalidate the cache when processing cancellations or when subscriptions expire
- Consider periodic cache refresh to handle external subscription changes
Handling Subscription Cancellations
When merchants request to cancel their subscription, use the CancelSubscription Mutation (opens in a new tab). The response includes a cancelledAt
date indicating when access ends:
- Regular subscriptions: Access continues until the end of the current billing cycle
- Trial subscriptions: Cancellation takes effect immediately
Implementation Note: Invalidate any cached subscription data immediately after processing a cancellation to ensure accurate access control.
Updating Subscription Plans
To handle plan changes (upgrades, downgrades, or billing frequency modifications), create a new checkout using the UpdateSubscription Mutation (opens in a new tab). This allows you to modify subscription details while preserving the merchant's billing relationship.
Use Cases:
- Plan tier changes (Basic → Premium)
- Billing frequency updates (Monthly → Annual)
- Price adjustments for existing subscribers
Example queries and mutations
Create a checkout
The first step in charging a merchant for a product is to create a checkout. After creating a checkout through the API, the checkout will be in a pending status
, and a checkoutUrl
will be included in the response. The checkoutUrl
routes to the BigCommerce checkout UI where a merchant must complete the checkout in their store's control panel. After checkout completion, a subscription is created and billed to the merchant, and the checkout status moves to complete. When the merchant exits the BigCommerce checkout UI, they will be redirected to the redirectUrl
provided in the request. Use this field to route the merchant back to your app.
When creating a checkout with a product that has a trial period, set the trialDays
field to the number of days the trial should last.
The merchant will not be charged during this period. A subscription only generates an invoice once the trial period ends. No charges are made during the trial.
Checkout links are valid for 24 hours and expire automatically to prevent misuse.
To create a checkout, run the createCheckout
mutation:
mutation ($checkout: CreateCheckoutInput!) {
checkout {
createCheckout(input: $checkout) {
checkout {
id
accountId
status
checkoutUrl
items(first: 1) {
edges {
node {
subscriptionId
status
product {
id
type
productLevel
}
scope {
id
type
}
pricingPlan {
interval
price {
value
currencyCode
}
trialDays
}
redirectUrl
description
}
}
}
}
}
}
}
Some common errors returned when creating a checkout:
Product is not supported for your account.
- The application being purchased is not owned by the partner account (opens in a new tab) (in the request URL) making the request.Scope does not belong to account.
- The store hash provided does not belong to the merchant account provided.You do not have permission to do this operation.
- This failure has multiple possible causes:- The account-level API token being used does not have the required auth scope (opens in a new tab) for the given operation.
- The account-level API token does not match the account UUID being used in the request URL.
Fetch a checkout
Once the checkout is created, you can periodically poll the status of the checkout by calling this endpoint. To fetch an existing checkout, copy and run the following query:
Replace the checkoutId
with the following variable:
{
"checkoutId": "bc/account/checkout/ab0a8354-3caf-423b-a3be-42a59c97fcf5"
}
query Account($checkoutId: ID!) {
account {
checkout(id: $checkoutId) {
id
accountId
checkoutUrl
status
items {
edges {
node {
description
status
product {
id
productLevel
type
}
scope {
id
type
}
pricingPlan {
price {
value
currencyCode
}
interval
trialDays
}
subscriptionId
redirectUrl
}
}
}
}
}
}
Update a subscription
The createCheckout
mutation can also be used to update an existing subscription's pricing plan information and product level. A subscriptionId
must be passed in the request for this to be processed as an update.
mutation ($checkout: CreateCheckoutInput!) {
checkout {
createCheckout(input: $checkout) {
checkout {
accountId
status
checkoutUrl
items(first: 1) {
edges {
node {
subscriptionId
status
product {
id
type
productLevel
}
scope {
id
type
}
pricingPlan {
interval
price {
value
currencyCode
}
trialDays
}
redirectUrl
description
}
}
}
}
}
}
}
Cancel a subscription
This mutation cancels the subscription at the end of the merchant's current billing cycle. The cancelledAt
value will be the last day of the merchant's billing cycle (i.e., the day through which they have already paid). For trials and subscriptions that have not yet been invoiced, the cancellation takes effect immediately and the cancelledAt
value will reflect this immediate time.
mutation ($subscription: CancelSubscriptionInput!) {
subscription {
cancelSubscription(input: $subscription) {
subscriptionId
cancelledAt
}
}
}
How Cancellations Work During a Free Trial
-
No Billing During Trial
- A subscription only generates an invoice once the trial period ends. No charges are made during the trial.
-
Immediate Cancellation During Trial
- If a subscription is canceled while it's still in the trial period, the cancellation takes effect immediately. The trial ends, and no invoice is ever generated.
-
Post Trial Cancellation Behavior
- Once the trial ends, the subscription begins billing.
- Any cancellation initiated before the next billing cycle date will be prorated for the period from trial end until cancellation date.
- Charges for prorated fees will appear on the merchant's next invoice.
Query subscriptions
Subscriptions can be queried using the subscriptions
query endpoint.
Fields that can be retrieved are:
id
- subscription idaccountId
- account id belonging to the merchantactivationDate
- date billing beganbillingInterval
- billing frequencycreatedAt
- date and time the subscription was createdcurrentPeriodEnd
- date of the end of the current billing periodupdatedAt
- date and time the subscription was last updatedpricePerInterval
- amount being billed with the following fields:value
- the scalar amountcurrencyCode
- the currency of the amount
product
- product object with the following fieldsproductLevel
- the level of the product if it was providedid
- product idtype
- product type
status
- subscription statusscope
- scope object with the following fieldstype
- scope typeid
- scope id
In this example, no filters are passed, so the response will include all subscriptions of the partner. The default number of subscriptions returned per page is 10.
query {
account {
subscriptions {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
accountId
activationDate
pricePerInterval {
value
currencyCode
}
billingInterval
status
scope {
type
id
}
product {
productLevel
id
type
}
createdAt
currentPeriodEnd
updatedAt
}
}
}
}
}
Pagination info is returned with cursors on every subscription node. The pageInfo
provides information on the next page along with the cursors to use in traversing the graph.
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "WzE3MjQ0MzgzODk0MzY0NzYsODhd",
"endCursor": "WzE3MTk3NDYxNzUyNjg2NDUsNzdd"
}
Use the endCursor
to begin the next query. first
can be used to limit how many subscriptions are returned. The max limit is 50 subscriptions at a time.
query {
account {
subscriptions(first: 10, after: "WzE3MTk3NDYxNzUyNjg2NDUsNzdd") {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
accountId
activationDate
pricePerInterval {
value
currencyCode
}
billingInterval
status
scope {
type
id
}
product {
productLevel
id
type
}
createdAt
currentPeriodEnd
updatedAt
}
}
}
}
}
Filters can also be used to query subscriptions.
The supported filters are
productId
productType
scopeId
scopeType
updatedAfter
status
ids
query ($filters: SubscriptionFiltersInput) {
account {
subscriptions(filters: $filters) {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
accountId
activationDate
pricePerInterval {
value
currencyCode
}
billingInterval
status
scope {
type
id
}
product {
productLevel
id
type
}
createdAt
currentPeriodEnd
updatedAt
}
}
}
}
}