Skip to Content
Your subscription data now goes where you need it: the Curobi Developer API feeds your warehouse, ERP or CRM, and webhooks tell you the moment something changes — read the API docs →
Developer APIHeadless & Custom Storefronts

Headless & custom storefronts

This page is the integration contract for a storefront that isn’t a Liquid theme — Hydrogen, a custom React front end, a native app, or anything else talking to Shopify’s Storefront API. It says what your cart must send, and what happens when it sends it wrong.

A headless storefront can create Curobi subscriptions today

With no code from Curobi, and no Curobi credential.

Subscriptions on Shopify are selling plans. A cart line carrying a sellingPlanId produces a checkout that Shopify turns into a SubscriptionContract, which fires orders/create, which Curobi mirrors on exactly the same path a Liquid theme uses. Nothing in that webhook knows or cares whether a theme app extension was involved.

Everything below is about the extras — build-a-box, Mix & Match, gifts. Those ride on cart-line properties, and they’re the part that fails silently.

Get a property key wrong and nothing errors. The add-to-cart succeeds, checkout succeeds, the subscription is created, the order ships — and the feature simply never happens. No box selection is recorded. No gift is captured. No discount group is formed.

There is no runtime validation that can catch this for you: by the time the order reaches us, a wrong key is indistinguishable from the merchant not having used the feature. Copy the keys below exactly, then place a real test order and confirm it appears in the Curobi admin.

Cart-line properties

Written at add-to-cart, carried through checkout onto the order line’s customAttributes, and read there by Curobi.

KeyFeatureValueVisible to buyer
_rc_box_pickBuild-a-boxvariantGid=qty segments, comma-joinedno
Your picksBuild-a-boxHuman-readable companion, e.g. Coffee ×2, Teayes
_rc_mm_groupMix & MatchThe plan’s cart_group_idno
_rc_giftGiftAny truthy value — 1, true, yesno
_rc_gift_emailGiftRecipient email — requiredno
_rc_gift_startGiftOptional YYYY-MM-DD first deliveryno
Gift recipientGiftRecipient nameyes
Gift messageGiftThe gifter’s noteyes

The leading _ is Shopify’s convention for hiding a property from the buyer-facing order summary. The unprefixed keys are visible on purpose: who the gift is for, the gift note and the box contents are exactly what a merchant wants on the order and the packing slip. Preserve that split — it’s part of the contract, not an accident of naming. A headless build that hides all eight produces orders the fulfilment team can’t read.

These keys are add-only. Renaming one would orphan in-flight carts, so they won’t change.

Build-a-box

_rc_box_pick carries the picks:

gid://shopify/ProductVariant/44000000000001=2,gid://shopify/ProductVariant/44000000000002=1
  • Segments joined by ,; variant and quantity separated by =. Neither character appears in a variant GID, so the split is unambiguous.
  • A bare GID with no =qty is treated as quantity 1 — a legacy format, still accepted.
  • Quantity is clamped server-side to 1–10 per item, and a repeated variant is summed then re-clamped.
  • Use the GID, not the numeric id. The menu metafield gives you GIDs; pass them through unchanged.

The server re-validates every pick against the published menu and the plan’s min/max bounds, so a crafted payload can’t smuggle in an item that isn’t on the menu. Invalid picks are dropped, not rejected — another reason to test with a real order.

Mix & Match

_rc_mm_group is what groups the lines of one selection.

  • Every line of one selection must carry the same value.
  • The value is the plan’s cart_group_id — available from the mix_match product metafield as groupId, or from GET /api/admin/v1/mix-match/{id} as cart_group_id.
  • Set selling_plan on each line only when the shopper chose to subscribe. Omit it for one-time purchases.
  • Don’t put the group id on lines that aren’t part of the selection.

The tier discount is applied by a Shopify Product Discount Function that runs server-side at checkout and re-derives the tier from real cart quantities. So the discount lands whether the picker was Liquid, React or curl, and a crafted cart can’t claim a richer tier than it bought.

Gifts

_rc_gift_email is mandatory. A gift line without a plausible recipient email is rejected outright rather than stored as a half-gift — there would be nobody to send the claim link to.

Bounds enforced server-side: recipient name ≤ 100 characters, message ≤ 500 characters. _rc_gift_start must be YYYY-MM-DD and a real calendar date.

A gift is only captured if the selling plan itself is marked giftable by the merchant. A cart carrying the gift properties against a non-giftable plan produces an ordinary subscription, silently and by design — check giftable on GET /plans.

Configuration you can already read

Every Curobi config metafield is declared access.storefront = "public_read", so the Shopify Storefront API returns them to a headless front end today, with no Curobi credential involved.

MetafieldContains
product.metafields.app.bundle_planThe bundle’s selling-plan id
product.metafields.app.bundle_contentsThis period’s locked curated box
product.metafields.app.bundle_menuThis period’s build-a-box menu — variant GIDs, titles, images, prices
product.metafields.app.mix_matchTier config: groupId, sellingPlanId, mode, tiers, min, max
shop.metafields.app.gift_plansThe shop’s giftable selling-plan ids
shop.metafields.app.plan_schedulesFixed-calendar plans only, keyed by numeric selling-plan id: { "<id>": { "d": charge day, "m": [months], "c": cutoff days, "s": ship day or null } }

A curobi namespace exists as a legacy fallback for each; read app first and fall back only if you’re supporting an older install.

So a headless build already has, with zero integration work: which products are subscribable, on what plans, at what cadence and discount, what’s in this period’s box, what the Mix & Match tiers are, which plans accept gifts, and which plans bill on a fixed calendar — with the charge day, months, cutoff and ship day to render an “Order by … · ships …” line.

What the Curobi browser APIs add

The public metafields cover the configuration that fits in Shopify’s storefront data. The remaining interactive pieces use two browser-safe Curobi surfaces, both behind a publishable key and an origin allowlist — never an Admin API key in the browser.

The Mix & Match pickable catalog and live price preview. The Storefront API returns the paged catalog that cannot fit in a metafield, plus the server’s own tier calculation for the selection currently in the picker. Use the returned selling_plan_id and cart_group_id when you turn that selection into cart lines.

The gift disclosure. GET /gifts/terms/{selling_plan_id} on the Storefront API returns the plan’s finished disclosure sentence, so a shopper sees the billing terms before adding a gift subscription. If that request fails, keep the conservative fallback wording: a recurring gift continues billing the buyer until cancelled. Do not render no disclosure at all.

The customer portal. The Customer API lets a storefront exchange its Shopify Customer Account API token for a short-lived Curobi session, then run the Curobi-owned portal features on its own pages: box picks, rescheduling, in-plan swaps, payment-method update emails, notification preferences, and the cancellation save flow. Shopify’s own Customer Account API remains the right surface for pause, resume, cancel, skip, and address edits.

For a ready-to-fork implementation of the purchase path and portal, see Reference React components.

Two limits that will not be lifted

Contracts created by another app are invisible. Curobi holds Shopify’s read_own_subscription_contracts / write_own_subscription_contracts scopes. It can’t read, manage or report on a subscription another app created, even on a store where Curobi is installed. Migration is via CSV import, which creates real Curobi-owned contracts. There is no read-only overlay mode, and this is a Shopify scope boundary rather than a Curobi decision.

Gift recipients can never have a login. Shopify has no third-party-payer concept — the contract belongs to whoever holds the card, and a contract can’t be transferred between customers. Recipients get a tokenized claim page instead of portal access — and you don’t have to build it. Curobi serves it through the Shopify App Proxy, so the emailed link reaches the recipient whatever your storefront is written in.

The catch for a headless store: it renders as Liquid inside the shop’s online-store theme, not your front end. That needs a published theme to exist, and it will inherit however that theme looks. Keep it presentable — to a recipient who has never heard of Curobi, a page in the merchant’s own branding on the merchant’s own domain is the difference between a gift and a phishing email.

Capability matrix

CapabilityShopify Storefront API onlyWith Curobi browser APIs
Read plans, cadence, discounts✅ metafields✅
Fixed-calendar schedule (charge day, months, cutoff, ship day)✅ plan_schedules metafield✅ schedule on Storefront API GET /plans
Create a subscription at checkout✅ sellingPlanId✅
Curated box contents / build-a-box menu✅ metafields✅
Build-a-box pick capture✅ cart-line property✅
Mix & Match tier config✅ metafield✅
Mix & Match pickable catalog❌✅ Storefront API
Mix & Match live price preview⚠️ port the math, or none✅ Storefront API
Mix & Match checkout discount✅ function runs server-side✅
Gift capture at checkout✅ cart-line properties✅
Gift disclosure before add-to-cart❌ use conservative copy✅ Storefront API
Customer portal❌ Shopify-hosted account pages only✅ Customer API + Shopify Customer Account API
Gift recipient claim page✅ App Proxy, needs a published theme✅

Checklist before you ship

Cart lines carry sellingPlanId for subscriptions, and omit it for one-time purchases

Build-a-box lines carry _rc_box_pick with GIDs, plus a readable Your picks

Every line of one Mix & Match selection carries the same _rc_mm_group

Gift lines carry _rc_gift and _rc_gift_email, plus the two visible gift properties

The gift disclosure renders conservative copy when it can’t be fetched

You placed a real test order

And confirmed the box selection, gift or bundle actually appears in the Curobi admin. Nothing above throws when it’s wrong, so this step is the test.

Last updated on