npx @ordereazi/commerce-agent-toolkit init registers an MCP server in Claude Code,
Cursor or VS Code that knows this API's endpoints, schemas and exact per-language SDK method names,
and can scaffold a working React storefront against it. You still need a Store Access Key from
step 1. The steps below are the same either way - worth reading so you know what your agent built.
1Get a Store Access Key
Every request to /api/v1/store/... needs a key. Keys are created in Backoffice, not via any API call.
- In Backoffice, go to Settings → Application APIs (
/settings/api). - Click Create Application API.
- On the Basics tab, choose the store this key belongs to and set Application Type to Store.
- On the Access Control tab, choose a Key Type (see the table below), tick the scopes this integration actually needs, and optionally restrict Allowed Origins to your own domain(s).
- Save. The plaintext key is shown once, immediately after creation - copy it now. From then on only a masked preview (
sk_store_ab12…xyz9) is shown.
Secret vs. publishable keys
| Prefix | Where to use it | Notes |
|---|---|---|
sk_store_... |
Server-side only (your backend) | Rejected outright if the request carries a browser Origin header. |
pk_store_... |
Browser or mobile app code | Safe to ship in client-side JS - same idea as a Stripe publishable key. |
Scopes
Leaving every scope unchecked gives the key full access (anything an anonymous visitor or logged-in customer could do). Tick only what you need:
| Scope | Grants |
|---|---|
cart:write | Add, update, or remove items in the cart |
checkout:write | Complete checkout and place orders |
account:write | Manage saved addresses and B2B account members |
profile:write | Update the profile, change password, or delete the account |
wishlists:write | Create and modify wishlists |
gift-registries:write | Create and modify gift registries |
A request that touches a scope the key wasn't granted gets a 403 with code store_key_scope_denied. Read-only calls (browsing, viewing a cart) never require a scope.
2Send the key on every request
Base URL for every Store endpoint: /api/v1/store/...
curl https://your-store.example.com/api/v1/store/catalog/categories \
-H "X-Commerce-Key: pk_store_yourstore_<secret>"
X-Storefront-Application-Key is also accepted, as a legacy alias for the same header. A missing key returns 401 with code store_key_missing.
Origin is reflected automatically, no allow-list to configure. Only GET, POST, PATCH, DELETE, and OPTIONS are used (no PUT). If you read custom response headers (like X-Session-Ref or X-Total-Count) from JS, note that only those two plus X-Page-Number/X-Page-Size are exposed to browser fetch/XHR - anything else is invisible client-side even though it's on the response.
3Handle the anonymous cart session
A visitor doesn't need to log in to have a cart. The session is tracked with a header, not a cookie.
- The server generates the session reference on your first request and returns it in the
X-Session-Refresponse header - you don't invent one. - From then on, send that same value back as
X-Session-Refon every subsequent request. That's the entire contract. - When the visitor logs in, their anonymous cart carries over onto their account automatically - there's no separate "merge cart" call to make.
curl https://your-store.example.com/api/v1/store/cart \
-H "X-Commerce-Key: pk_store_yourstore_<secret>" \
-H "X-Session-Ref: 8f3c1a9d2b7e4f10"
4Register or log in a customer
Only needed for account-gated features - browsing, cart, and checkout as a guest all work without this step.
POST /api/v1/store/auth/login
{ "email": "customer@example.com", "password": "…" }
→ 200 OK
{
"token": "eyJ…",
"expiresIn": 3600,
"sessionReference": "8f3c1a9d2b7e4f10",
"refreshToken": "…",
"user": { "id": 1, "email": "customer@example.com", "personId": 42, "accountId": 7 }
}
Send token back as Authorization: Bearer <token> on every request that needs it. It expires after expiresIn seconds (60 minutes) - call POST /api/v1/store/auth/refresh-token with the refreshToken to get a new pair before then.
refreshToken you were given, never retry with an old one.
Cart, catalogue, search, and checkout all work with just X-Session-Ref - no Authorization header required. Orders, saved addresses, wishlists, gift registries, and account/member management require a logged-in customer's Bearer token.
5Make your first calls
A minimal browse → add to cart → check out flow.
# Search the catalogue
curl "https://your-store.example.com/api/v1/store/search/products?keywords=shoes&page=1&limit=18" \
-H "X-Commerce-Key: pk_store_yourstore_<secret>"
# Add an item to the cart
curl -X POST https://your-store.example.com/api/v1/store/cart/items \
-H "X-Commerce-Key: pk_store_yourstore_<secret>" \
-H "X-Session-Ref: 8f3c1a9d2b7e4f10" \
-H "Idempotency-Key: 3f7a-add-item-1" \
-H "Content-Type: application/json" \
-d '{ "productId": 501, "qty": 1 }'
# Place the order
curl -X POST https://your-store.example.com/api/v1/store/checkout/orders \
-H "X-Commerce-Key: pk_store_yourstore_<secret>" \
-H "X-Session-Ref: 8f3c1a9d2b7e4f10" \
-H "Idempotency-Key: 3f7a-place-order-1" \
-H "Content-Type: application/json" \
-d '{ }'
Every field, query parameter, and response shape for every endpoint - including the ones skipped here (shipping, payment, gift cards, wallet) - is documented and testable live at /docs/store.
6Handle errors correctly
Expected failures (bad input, not found, unauthorized) come back as RFC 9457 Problem Details:
{
"type": "https://…",
"title": "Bad Request",
"status": 400,
"code": "quantity_must_be_positive",
"detail": "Quantity must be greater than 0",
"errors": null,
"traceId": "00-abc123…"
}
success field, not just the status code.
Some checkout/cart rules (below minimum order quantity, out of stock, etc.) come back as
HTTP 200 with { "success": false, "message": "…" } in the body, since
they're not really an error, just an answer the caller needs to react to. Always check
success on cart/checkout responses in addition to the HTTP status.
Rate limits
Every key is limited to 200 requests per minute. Login, register, forgot-password, and reset-password are limited more tightly (10 requests per 5 minutes, by caller IP) since they're the credential-guessing surface. Either limit returns 429 with a Retry-After header (seconds to wait).
7Make retries safe with Idempotency-Key
Network retries happen. On any state-changing call (add to cart, place an order, cancel, reorder, and most others), send a client-generated Idempotency-Key header to make a retry safe:
- Same key + same request again → the original response is replayed, the action does not run twice.
- Same key + a different request →
422(you're reusing a key for something else). - Same key while the first call is still in flight →
409withRetry-After: 2. - No key sent → the call just runs normally, with no replay protection.
Generate a fresh key per logical action (a UUID is fine) and reuse the exact same value only when retrying that exact same action.
8Explore and test everything live
The full reference - every endpoint, every field, every response shape - is at /docs/store.
Click Authorize at the top of that page, paste in your key, and every "Test Request" button on the page fires a real call using it - no separate Postman setup needed.
total, page, pageSize, totalPages), while
order listing returns it as response headers (X-Page-Number, X-Page-Size,
X-Total-Count) with a plain array as the body. Don't assume one pattern applies
everywhere.