Skip to main content
A fixed-price Build-a-Box sells one parent product at one price, and the customer chooses what goes inside it, such as the scents, flavors or blends. Appstle’s own box page does all of this for you. This guide shows how to do the same from your own UI: add the box to the cart, then let the customer change its contents later without creating a second subscription.
Steps 1, 2 and 4 run in the customer’s browser. Steps 1 and 4 call Appstle’s Storefront API through Shopify’s App Proxy, so they need no API key; it isn’t Shopify’s Storefront GraphQL API. Step 3 and Save from your server instead use the Admin API from your server, with an API key. If the store changed its App Proxy path, replace /apps/subscriptions with it. Read Build your own frontend first if you haven’t used the App Proxy before.

What you’ll build

  • A cart add that puts a fixed-price box in the cart with a selling plan, and carries the customer’s choices as line item properties.
  • An account-page call that saves a changed selection on the customer’s existing subscription line.

Before you start

  • A fixed-price box (a Fixed Pricing Bundle), enabled in the Build-A-Box section of the Appstle Subscriptions app. Note its reference: the box list shows it as Ref, and the API calls it uniqueRef.
  • A selling plan attached to the box’s parent product.
  • A signed-in customer for Step 4. Steps 1 and 2 also work for guests.

How a fixed-price box is stored

A fixed-price box is one ordinary line: the box’s parent product with a selling plan. The customer’s choices travel on that line as five line item properties, and they stay on the subscription line after checkout. That is why you can change the choices later by updating that line’s properties, without adding a line or a subscription. Shopify doesn’t show properties whose names start with an underscore to the customer at checkout, so only products appears on the order summary.

Step 1: Read the box

Get the box’s rules and its choices. The call goes through the App Proxy, so it needs no API key and works for guests as well as signed-in customers:
The reference lists the same call under /apps/subscriptions/cp/api/, which needs a signed-in customer. The call answers 204 with an empty body when no box has this reference or the box is disabled. bundle.singleProductSettings is a JSON string. After parsing, it holds one entry per parent product, so a box with several parents has several entries. Use the entry whose sourceProduct is the parent you sell:
Read the rest from Shopify’s product JSON (/products/<handle>.js). The parent product’s selling_plan_groups[].selling_plans[].id is the selling plan. For a PRODUCT choice, its variants[].id and variants[].sku go into __appstle-bb-variants and _appstle-bb-product-sku. For a VARIANT choice, read the product at its productHandle and use the variant whose id matches. Offer only these choices: Appstle’s box page refuses anything else, and your UI must too.
The same box is available to your server as GET /api/external/v2/build-a-box/{handle} with an API key, where {handle} is the uniqueRef. It answers 404 while the box is disabled. See Retrieve Build-A-Box bundle details by handle.

Step 2: Add the box to the cart

Add the parent variant with the selling plan and the five properties. This is Shopify’s Cart API, so no Appstle call is involved:
Keep the total of the picks between minQuantity and maxQuantity. Shopify’s cart add doesn’t check it, but the save in Step 4 does.
Shopify answers 422 with Cannot apply selling plan to variant when the selling plan isn’t attached to the parent variant. Take the plan ID from the parent product’s selling_plan_groups.

Cart clearing

The Cart Clearing Behavior setting in the store’s Build-A-Box settings applies only to Appstle’s own box page. Your /cart/add.js call leaves the cart as it is. To start the customer with an empty cart, call Shopify’s POST /cart/clear.js before you add the box.

Step 3: Check the subscription after checkout

When the customer pays, Shopify creates the subscription, and the five properties are copied onto its line unchanged. Read the contract with the Admin API on your server to confirm:
On the storefront, the signed-in customer reads the same data from GET /apps/subscriptions/cp/api/subscription-customers-detail/valid/{customerId} (reference), where {customerId} is the customer’s numeric Shopify ID ({{ customer.id }} in Liquid). Each contract has a subscriptionContractId, and its contractDetailsJSON is a JSON string with one entry per line. Each entry has a lineId and the line’s customAttributes. The box line is the one whose customAttributes include _appstle-bb-id.

Step 4: Save a changed selection

To change what is in the box, send the new properties for the existing line. Don’t add a second line or a new subscription:
On success the call returns 200 with the updated contract, in the same shape as the Step 3 response, and the line carries the new properties. The line ID doesn’t change and the customer still has one subscription. See Edit custom attributes on a single line item.

What the save checks

The app runs these checks before it saves a customer’s change. A failed check answers 400 with application/problem+json, and its title holds the message. The time-window and frozen-subscription messages are written for the customer: show them without the UserGeneratedError: prefix. The others point to a problem in your request or the customer’s picks, so show your own text for them, such as “Choose between 1 and 4 items.” A customer can change only products, __appstle-bb-variants and _appstle-bb-product-sku. Every other Appstle property keeps its old value, whether you send it or not. Properties that aren’t Appstle’s, such as a gift message from another app, work differently: the line keeps exactly the ones you send. Send each of them back unchanged. Adding, changing or leaving one out needs the store’s Allow customers to edit attribute values setting, and a property you leave out is then removed. A call without a signed-in customer answers 401. Storefront calls are rate limited per customer: a call over the limit answers 429 with a Retry-After header, so wait that many seconds before you retry. Save once, when the customer confirms the new box, not on every change.
The time-window, frozen-subscription and edit-permission checks depend on the store’s customer portal settings. The other checks apply to every store.

Save from your server instead

The Admin API has an equivalent call for your own back office. It uses an API key and acts as the merchant, so none of the customer checks above run, including the box’s minimum and maximum. It replaces the line’s properties with exactly the list you send: send all five box properties and every other property the line should keep.
See Update line item attributes for a subscription contract.

Next steps

Build your own frontend

Selling plans, the cart and the customer portal from your own UI.

JavaScript hooks

Events the widget fires that your code can listen to.

Authentication

Admin API keys and how storefront calls are authenticated.

Integration guide

Server-side calls for subscriptions and contracts.