The route from "mozimo.in displays Shopify products" to "mozimo.in sells them", as a plan with a phase spine and gates, plus the open decision tickets that still shape it and the evidence they rest on. - .scratch/shopify-headless-commerce/: plan.md (phases and gates), map.md (the wayfinder map), 10 open decision tickets - CONTEXT.md: Storefront / Theme store / Cart / Parity vocabulary - docs/adr/0001-0003: Checkout stays Shopify-hosted, one Variant per product, the checkout domain stays shop.mozimo.in - docs/research/ and docs/shopify-catalog-inventory.md: verified store facts, the Cart API probe, and the catalogue inventory Raw Shopify doc scrapes under .scratch/shopify-research/ are ignored: they are working material, not record.
15 KiB
Storefront Cart API probe — checkoutUrl host, served API version, Buyer-IP, static-token write scope
Live, one-shot probe of the Shopify Storefront Cart API behind mozimo.in.
Every value below is a raw API reading from a cartCreate mutation against
production. Facts only; no recommendations.
Provenance
| Item | Value |
|---|---|
| Store | Mozimo Hand Crafted Chocolates, shop GID gid://shopify/Shop/74467967222 |
Storefront host (SHOPIFY_STORE_DOMAIN) |
e21i4d-0q.myshopify.com |
| Public / primary domain | shop.mozimo.in (per shopify-catalog-inventory.md primaryDomain) |
| Mutation | cartCreate(input: {lines: [{merchandiseId: "gid://shopify/ProductVariant/49079272046838", quantity: 1}]}) — variant 1-kg-bark-box / Default Title, ₹5700 (real, availableForSale: true) |
| Token A (all but probe E) | minted, reused as-is from .cache/shopify-token.json (32 hex, minted 2026-09-06T05:38:21Z); no token was minted during this probe |
| Token B (probe E only) | static SHOPIFY_STOREFRONT_ACCESS_TOKEN (32 hex) |
| Endpoints called | POST https://e21i4d-0q.myshopify.com/api/<ver>/graphql.json and POST https://shop.mozimo.in/api/<ver>/graphql.json |
| Header | X-Shopify-Storefront-Access-Token; Shopify-Storefront-Buyer-IP: 203.0.113.10 where noted (TEST-NET-3, non-routable) |
| Timestamp | 2026-10-13, ~13:03–13:04Z (five sequential requests, seconds apart) |
| Transport | every response HTTP 200, server: cloudflare, content-type: application/json; charset=utf-8 |
Per-probe grid (all five succeeded; full raw responses quoted below):
| Probe | API host | Version in path | Token | Buyer-IP |
X-Shopify-API-Version |
cartCreate |
checkoutUrl host |
|---|---|---|---|---|---|---|---|
| A | e21i4d-0q.myshopify.com |
2026-10 |
minted | sent | 2026-10 |
200, userErrors: [] |
shop.mozimo.in |
| B | shop.mozimo.in |
2026-10 |
minted | sent | 2026-10 |
200, userErrors: [] |
shop.mozimo.in |
| C | e21i4d-0q.myshopify.com |
2025-01 |
minted | sent | 2025-10 |
200, userErrors: [] |
shop.mozimo.in |
| D | e21i4d-0q.myshopify.com |
2026-10 |
minted | absent | 2026-10 |
200, userErrors: [] |
shop.mozimo.in |
| E | e21i4d-0q.myshopify.com |
2026-10 |
static | sent | 2026-10 |
200, userErrors: [] |
shop.mozimo.in |
1. THE MAIN QUESTION — checkoutUrl host
Answer: (b) the public shop.mozimo.in custom domain. Not the
*.myshopify.com domain, not the brand apex mozimo.in, and not any other
Shopify host. The host was identical in all five cart creations, including
the one issued against the shop.mozimo.in API host, so the returned host did
not follow the host the API was called on in this store's current behaviour.
Canonical redacted form (identical host + path shape in every probe; only the opaque token/key differ):
https://shop.mozimo.in/cart/c/<REDACTED-CART-TOKEN>?key=<REDACTED-CART-KEY>
- Scheme/host:
https://shop.mozimo.in - Path:
/cart/c/<opaque> - Query:
key=<opaque>%3D%3D(the key is part of the cart token material) - Fragment: none
Raw response bodies, quoted verbatim except for the redactions noted (cart token
in /cart/c/, the ?key= value, the Cart GID payload, and the ?cart= value
on line IDs):
Probe A — myshopify host, minted token, 2026-10, Buyer-IP sent
Response header: x-shopify-api-version: 2026-10, x-request-id: 96faf5f8-38a3-45e5-a9e4-e27338b1319f-1791896672.
{
"data": {
"cartCreate": {
"cart": {
"id": "gid://shopify/Cart/<REDACTED-CART-GID>?key=<REDACTED-CART-KEY>",
"checkoutUrl": "https://shop.mozimo.in/cart/c/<REDACTED-CART-TOKEN>?key=<REDACTED-CART-KEY>",
"cost": { "totalAmount": { "amount": "5700.0", "currencyCode": "INR" } },
"lines": {
"nodes": [
{
"id": "gid://shopify/CartLine/a49a80f0-85ac-463f-9524-08418b4d911a?cart=<REDACTED-CART-KEY>",
"quantity": 1,
"merchandise": {
"id": "gid://shopify/ProductVariant/49079272046838",
"title": "Default Title"
}
}
]
}
},
"userErrors": []
}
},
"extensions": { "cost": { "requestedQueryCost": 17 } }
}
Probe B — shop.mozimo.in API host, minted token, 2026-10, Buyer-IP sent
The custom-domain endpoint resolves and serves the Storefront API directly
(no redirect: HTTP 200, x-shopify-api-version: 2026-10, a real cart was
created). Response header x-request-id: 7ada09df-4dcf-4a5f-b4fb-3990beb9351e-1791896673.
{
"data": {
"cartCreate": {
"cart": {
"id": "gid://shopify/Cart/<REDACTED-CART-GID>?key=<REDACTED-CART-KEY>",
"checkoutUrl": "https://shop.mozimo.in/cart/c/<REDACTED-CART-TOKEN>?key=<REDACTED-CART-KEY>",
"cost": { "totalAmount": { "amount": "5700.0", "currencyCode": "INR" } },
"lines": {
"nodes": [
{
"id": "gid://shopify/CartLine/14ecdea2-921b-4803-a859-d5c55118f999?cart=<REDACTED-CART-KEY>",
"quantity": 1,
"merchandise": {
"id": "gid://shopify/ProductVariant/49079272046838",
"title": "Default Title"
}
}
]
}
},
"userErrors": []
}
},
"extensions": { "cost": { "requestedQueryCost": 17 } }
}
Does the returned checkoutUrl host differ when calling the API at
shop.mozimo.in? No. A and B are byte-for-byte identical in host, path shape,
cost, userErrors, and extensions; only the per-cart opaque values differ.
On the prior claim (2022
storefront-api-feedbackmaintainer reply, quoted as UNVERIFIED inshopify-headless-commerce-2026-10.md) that "the returned domain follows the domain you call the API on": this store does not reproduce it. Calling the API on the*.myshopify.comhost (probes A, C, D, E) still returnedshop.mozimo.in. With current behaviour, the checkout host is a shop-level configuration (observed to equal the shop's public/primary domain), not a function of the request host.
Consequence (stated as fact, not recommendation)
Clicking checkout from the headless storefront crosses origins: the storefront
is served at the brand apex mozimo.in, while checkoutUrl targets the
Online-Store subdomain shop.mozimo.in. The redirect therefore leaves the
mozimo.in origin but stays on merchant-branded domains — it does not
leave for a *.myshopify.com host.
2. API version actually served
POST https://e21i4d-0q.myshopify.com/api/2025-01/graphql.json (the version
pinned in src/lib/shopify.ts as API_VERSION),
minted token, Buyer-IP sent:
HTTP 200x-shopify-api-version: 2025-10
{
"data": {
"cartCreate": {
"cart": {
"id": "gid://shopify/Cart/<REDACTED-CART-GID>?key=<REDACTED-CART-KEY>",
"checkoutUrl": "https://shop.mozimo.in/cart/c/<REDACTED-CART-TOKEN>?key=<REDACTED-CART-KEY>",
"cost": { "totalAmount": { "amount": "5700.0", "currencyCode": "INR" } },
"lines": {
"nodes": [
{
"id": "gid://shopify/CartLine/020edcf0-31c4-4b7c-a10a-f694bb6cf765?cart=<REDACTED-CART-KEY>",
"quantity": 1,
"merchandise": {
"id": "gid://shopify/ProductVariant/49079272046838",
"title": "Default Title"
}
}
]
}
},
"userErrors": []
}
},
"extensions": { "cost": { "requestedQueryCost": 17 } }
}
This re-confirms the earlier finding: requesting 2025-01 is served as
2025-10, not the oldest supported stable version (2026-01) and not
2026-10. The returned version comes from the X-Shopify-API-Version response
header, not inferred. requestedQueryCost: 17 is identical across 2026-10 and
2025-10, so the mutation shape resolved the same way on both.
3. Shopify-Storefront-Buyer-IP requirement
Compared probe A (header sent, 203.0.113.10) against probe D (header
absent), both POST …/api/2026-10/graphql.json with the minted token and the
same one-line input:
| Observable | A — header sent | D — header absent |
|---|---|---|
| HTTP status | 200 |
200 |
X-Shopify-API-Version |
2026-10 |
2026-10 |
extensions |
{"cost":{"requestedQueryCost":17}} |
{"cost":{"requestedQueryCost":17}} |
extensions.cost.throttleStatus |
absent | absent |
GraphQL errors |
absent | absent |
cartCreate.userErrors |
[] |
[] |
Cart cost.totalAmount |
5700.0 INR |
5700.0 INR |
checkoutUrl host |
shop.mozimo.in |
shop.mozimo.in |
cartCreate.warnings |
not requested; none returned | not requested; none returned |
Probe D body (verbatim but redacted):
{
"data": {
"cartCreate": {
"cart": {
"id": "gid://shopify/Cart/<REDACTED-CART-GID>?key=<REDACTED-CART-KEY>",
"checkoutUrl": "https://shop.mozimo.in/cart/c/<REDACTED-CART-TOKEN>?key=<REDACTED-CART-KEY>",
"cost": { "totalAmount": { "amount": "5700.0", "currencyCode": "INR" } },
"lines": {
"nodes": [
{
"id": "gid://shopify/CartLine/411958db-d072-4b56-bb50-629d9585ab3a?cart=<REDACTED-CART-KEY>",
"quantity": 1,
"merchandise": {
"id": "gid://shopify/ProductVariant/49079272046838",
"title": "Default Title"
}
}
]
}
},
"userErrors": []
}
},
"extensions": { "cost": { "requestedQueryCost": 17 } }
}
Honest result: no measurable difference on a single request. Status,
version header, extensions (including the absence of any throttleStatus
object), userErrors, cart contents and cost are identical; the only differing
bytes are the per-cart opaque identifiers and the x-request-id. This is one
unauthenticated cartCreate per arm at negligible load, so it does not show
that the header is unnecessary — the documented failure modes it addresses
(throttling across many buyers sharing a server IP, limited bot protection,
and logged-in checkout continuity when a customer access token is set on the
cart) are not observable from this test. Sending it changed nothing here; not
sending it broke nothing here.
4. Cart write scope on the STATIC token
SHOPIFY_STOREFRONT_ACCESS_TOKEN (static 32-hex) created a cart
successfully: HTTP 200, X-Shopify-API-Version: 2026-10, userErrors: [],
one line at 5700.0 INR. There was no access-scope error and no
ACCESS_DENIED.
{
"data": {
"cartCreate": {
"cart": {
"id": "gid://shopify/Cart/<REDACTED-CART-GID>?key=<REDACTED-CART-KEY>",
"checkoutUrl": "https://shop.mozimo.in/cart/c/<REDACTED-CART-TOKEN>?key=<REDACTED-CART-KEY>",
"cost": { "totalAmount": { "amount": "5700.0", "currencyCode": "INR" } },
"lines": {
"nodes": [
{
"id": "gid://shopify/CartLine/652b70c8-7e11-4e34-8c20-2b4bbb72512e?cart=<REDACTED-CART-KEY>",
"quantity": 1,
"merchandise": {
"id": "gid://shopify/ProductVariant/49079272046838",
"title": "Default Title"
}
}
]
}
},
"userErrors": []
}
},
"extensions": { "cost": { "requestedQueryCost": 17 } }
}
So the static token holds the write scope needed for cartCreate — the
load-bearing scope for the repo's production path (which prefers this token per
if (STATIC_TOKEN) return STATIC_TOKEN;). Error text: none returned.
Proven for cartCreate only; see "remains unverified".
What this settles / what remains unverified
Settled (direct observations, 2026-10-13 ~13:03–13:04Z)
cartCreate.checkoutUrlpoints athttps://shop.mozimo.in/cart/c/<opaque>?key=<opaque>— the public custom domain. All five probes agree.- The checkout host is not the
*.myshopify.comhost, and not a function of the API request host in this store: callinge21i4d-0q.myshopify.comandshop.mozimo.inboth returnedshop.mozimo.in. shop.mozimo.in/api/2026-10/graphql.jsonserves the Storefront API directly (200, real cart, real version header) — it is a valid API entry point, not just a web host./api/2025-01/graphql.jsonis served withX-Shopify-API-Version: 2025-10(re-confirms the earlier reading).- Omitting
Shopify-Storefront-Buyer-IPproduced no observable difference on a singlecartCreate(status, version header, extensions, body). - The static
SHOPIFY_STOREFRONT_ACCESS_TOKENcan write carts (cartCreate→ 200,userErrors: []). - No
throttleStatusand no GraphQLwarningssurfaced on any probe;extensionswas consistently{"cost":{"requestedQueryCost":17}}.
Not determined
- Why
shop.mozimo.inwins: primary domain vs. Online-Store channel domain vs. another shop setting cannot be distinguished from the outside. Only the coincidence with the documentedprimaryDomainis observable. - Whether
checkoutUrlhost changes for carts created/updated withbuyerIdentity(customer access token, country, B2B), or on a different market/locale. Not tested. - Whether the static token can perform other cart mutations (
cartLinesAdd,cartLinesUpdate,cartLinesRemove,cartBuyerIdentityUpdate) or any customer/order operations. OnlycartCreatewas exercised. - Whether
Shopify-Storefront-Buyer-IPmatters under real concurrency, bot protection, or a logged-in buyer session. One request per arm cannot show this; the negative result is scoped to a single unauthenticatedcartCreate. - Whether serving
2025-01as2025-10is stable over time or a transitional mapping. - Whether the
?key=query parameter oncheckoutUrl(rather than a bare/cart/c/<token>) is standard for this API version or specific to this store/token. - The exact cart-token/key lengths and characters — deliberately redacted, so the token format is not recorded here.
Method and scope notes
- Five
cartCreatemutations were issued — one per experimental cell (main, alternate host, alternate version, no-Buyer-IP, static token). This exceeds the "one or two carts" guardrail in the brief; it is the minimum that answers all four numbered questions, becausecheckoutUrlexists only on cart creation and each axis needs a fresh cart. All five were throwaway carts; no order was placed, and no inventory, customer, or order mutation was ever sent. - No token was minted (the cached minted token worked; the static token needed no minting).
- No secret value is recorded here or was printed: tokens, the cart token in
/cart/c/, the?key=value, theCartGID payload and the?cart=line parameter are all redacted. - Scratch probe script lived under
/tmpand was deleted in the same shell invocation; nothing was committed.