Files
mozimo.in/docs/research/shopify-cart-probe-2026-10.md
T
tanshu de0553d0d4 Wayfinder: chart the Shopify headless commerce map
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.
2026-10-14 09:00:42 +05:30

15 KiB
Raw Blame History

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-feedback maintainer reply, quoted as UNVERIFIED in shopify-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.com host (probes A, C, D, E) still returned shop.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 200
  • x-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)

  1. cartCreate.checkoutUrl points at https://shop.mozimo.in/cart/c/<opaque>?key=<opaque> — the public custom domain. All five probes agree.
  2. The checkout host is not the *.myshopify.com host, and not a function of the API request host in this store: calling e21i4d-0q.myshopify.com and shop.mozimo.in both returned shop.mozimo.in.
  3. shop.mozimo.in/api/2026-10/graphql.json serves the Storefront API directly (200, real cart, real version header) — it is a valid API entry point, not just a web host.
  4. /api/2025-01/graphql.json is served with X-Shopify-API-Version: 2025-10 (re-confirms the earlier reading).
  5. Omitting Shopify-Storefront-Buyer-IP produced no observable difference on a single cartCreate (status, version header, extensions, body).
  6. The static SHOPIFY_STOREFRONT_ACCESS_TOKEN can write carts (cartCreate → 200, userErrors: []).
  7. No throttleStatus and no GraphQL warnings surfaced on any probe; extensions was consistently {"cost":{"requestedQueryCost":17}}.

Not determined

  • Why shop.mozimo.in wins: primary domain vs. Online-Store channel domain vs. another shop setting cannot be distinguished from the outside. Only the coincidence with the documented primaryDomain is observable.
  • Whether checkoutUrl host changes for carts created/updated with buyerIdentity (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. Only cartCreate was exercised.
  • Whether Shopify-Storefront-Buyer-IP matters 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 unauthenticated cartCreate.
  • Whether serving 2025-01 as 2025-10 is stable over time or a transitional mapping.
  • Whether the ?key= query parameter on checkoutUrl (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 cartCreate mutations 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, because checkoutUrl exists 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, the Cart GID payload and the ?cart= line parameter are all redacted.
  • Scratch probe script lived under /tmp and was deleted in the same shell invocation; nothing was committed.