Skip to main content

Pre-Launch Readiness Checklist

A self-service guide for implementation partners building on the Storefront API. Use this checklist before taking a client live to catch the issues we most commonly see in the wild — caching mistakes, over-fetching, and client-side state drift in checkout.

This is written for the agency-side engineers who own the frontend integration. As a headless SaaS platform, we give you the building blocks; how you use them is ultimately your call. This checklist is the accumulated wisdom of what we've seen work — and what we've seen go wrong.

How to use this checklist

The checklist is split into seven sections:

  1. Caching strategy — how you store and invalidate our data
  2. Security — authorization modes, input validation, and bot protection
  3. Query efficiency — what you fetch, where, and how much
  4. Checkout integrity — ordering, client-side state, and mutation hygiene
  5. Tracing & observability — propagating trace context for debugging
  6. Functional coverage — features that need explicit implementation
  7. Plugin & AMS configuration — settings worth double-checking

For each item, ask yourself "can I confidently answer this for our implementation?" If not, it's worth a closer look before launch.


1. Caching strategy

Caching is where we see the biggest variance in implementation quality. The questions below help you document and pressure-test your approach.

Document your caching approach

  • Which type of caching are you using? (CDN edge cache, server-side cache, client-side React Query / SWR, a combination?) Have this written down somewhere your team can reference.
  • How is the cache invalidated? Event-driven via webhooks? TTL-based? Manual purge? Be explicit about which strategy applies to which data type — product catalog, pricing, inventory, and content typically need different approaches.
  • Are there periodic sync jobs that do a full rebuild? If yes, when do they run, how long do they take, and what's the impact on origin load? Schedule these outside peak traffic windows.
  • Are the flows documented? Cache population, invalidation triggers, and known edge cases should be captured somewhere — a runbook, an architecture doc, anything your on-call engineer can find at 2am.

Avoid duplicate storage

A common pattern we see: storing a full product object both as a standalone item and nested under every related-items relationship. This bloats your cache, makes invalidation harder, and means a single product update requires touching many keys.

  • Are you storing related items by reference (ID) and resolving them at read time, or duplicating the full payload?
  • If you're duplicating, do you have a clear invalidation strategy that updates every copy when the source changes?

2. Security

NO_SESSION and SHARED_SECRET integrations

If your integration routes Storefront API calls through a backend for frontend (BFF) using NO_SESSION or SHARED_SECRET mode, input validation becomes your responsibility — the API trusts the server making the call. See Authorization & sessions for how each mode works.

  • Are user-supplied identifiers validated server-side before being passed to the API? Never trust an order ID, line ID, or customer ID from the client without confirming it belongs to the current authenticated session.
  • Do your BFF endpoints enforce ownership? The authenticated session must have the right to perform the requested operation on the referenced resource.

Bot protection

Checkout flows are a common target for automated abuse: credential stuffing, voucher brute-forcing, and carding attacks.

  • Have you evaluated bot protection for your checkout? Consider a CAPTCHA or challenge at sensitive steps — login, checkout initiation, voucher application. Centra supports Google reCAPTCHA as a pre-authorization check option.
  • Are you using any post-authorization fraud screening? See Fraud prevention for how to integrate external fraud solutions via the Fraud Prevention plugin.

3. Query efficiency

The Storefront API gives you fine-grained control over what you fetch. The default of "request everything" is the most common cause of slow page loads.

Right-size your queries

  • Do your catalog queries match what's actually rendered on the page? If your PLP shows name, price, thumbnail, and a "new" badge, your query should fetch those fields and not much else.
  • Are you filtering attributes between PLP and PDP? Product list pages should pull a slim projection; product detail pages can fetch the full set. Reusing a single "product" fragment everywhere is convenient but expensive.
  • Are related items fetching only a small set of fields? "You might also like" widgets rarely need full descriptions or every attribute — a thumbnail, name, price, and link are usually enough.

The checkout field

The checkout field on cart operations is one of the heaviest things you can ask us for. It triggers payment provider calls, shipping calculations, and validation logic.

  • Is checkout queried on every cart operation, or only when the user is actually in the checkout flow? It should be the latter. Add-to-cart, quantity changes outside checkout, and similar operations should not include it.

4. Checkout integrity

Checkout is where bugs become revenue loss. A note on how our API behaves: we use distributed session locking for mutations, so the server-side state of a cart or checkout will remain consistent even under concurrent requests. Mutations are serialized for you on our side.

What this means in practice: if your checkout ends up in a weird state, it's almost always a client-side state-sync issue, not a server-side correctness issue. Specifically, if you fire-and-forget requests without awaiting their responses, your client cache will drift away from the server's actual state. The questions below are about keeping your client in sync.

Mutation ordering

Updates inside checkout often have implicit dependencies — paymentInstructions, for example, depends on totals being up to date, which depend on shipping being resolved.

The diagram below contrasts the correct serial pattern with a fire-and-forget pattern that lets the client drift out of sync:

Storefront APIClientStorefront APIClientPreferred: serial, awaitedAcceptable, but paymentInstructions must be last...await settled state...updateShippingresponse (updated state)applyVoucherresponse (updated state)paymentInstructionsresponse (final payment state)updateShippingapplyVoucherpaymentInstructionsresponse (final payment state)

Each of these mutations returns the updated state, so as long as you use the latest response, the risk of client drift is minimal even if calls overlap. The one rule that matters: paymentInstructions must be last, after totals and shipping have settled.

  • Do your mutations run in a sensible order? paymentInstructions should be last, after totals and shipping have settled.
  • Do calls "wait for each other" where they need to? If you're using React Query (or similar), check that dependent mutations aren't firing in parallel when they should be sequential. Look for proper await chains and use of onSuccess callbacks.
  • Are you using the response from each mutation to update your client state? Don't fire and forget. Our session lock guarantees the server processes mutations safely, but your client only learns the new state from the response.

Reload-on-change

  • Is payment reloaded when relevant fields change? Adding a voucher, changing shipping method, or updating quantities should trigger a payment refresh — otherwise the customer pays the wrong amount.
  • Do totals update in checkout when a voucher is added? Test this on every release — it's a common regression.
  • Are you handling UnavailableItem userErrors? Changing the shipping address country can trigger a market switch that makes some cart items unavailable. The API returns UnavailableItem errors describing what was removed — the frontend must surface this to the customer and reconcile the cart accordingly. See Checkout for details.

Preventing updates during payment processing

After a customer submits payment and is redirected to the success page, any update to the selection — country, currency, shipping method — risks creating a mismatch between Centra's state and the PSP's state. This can result in amount discrepancies or, in the worst case, a charge without a corresponding order in Centra.

  • Is your frontend blocking selection updates while paymentResult is being processed? Default site behaviours like redirecting to a different market on page load can silently mutate an open selection during this window. See Best practices for the timing diagram and full explanation.

Race-condition hygiene

Even though our session lock prevents server-side races, your UI can still present inconsistent views if you don't manage in-flight requests carefully.

  • Are you making checkout as race-free as possible with async triggers? Multiple in-flight mutations against the same cart can produce a UI that lags behind reality. Cancel superseded requests, use mutation queues, or serialize where it matters.
  • Are calls toward us debounced when mutating the cart inside checkout? Quantity steppers, address fields, and similar inputs should debounce — typically 300–500ms — rather than firing a mutation per keystroke or click.

5. Tracing & observability

Every response from our API includes a traceId in the GraphQL extensions field. This is the single most useful thing you can capture when debugging an issue with us — pulling logs by trace ID is dramatically faster than searching by timestamp or user.

Propagate trace context

We follow the W3C Trace Context standard. If you send a traceparent header on your request, we'll use it and you'll get the same trace ID back in extensions.traceId. This lets you correlate a single request end-to-end across your frontend, your BFF (if any), and our platform.

  • Are you generating and sending a traceparent header on requests to our API? If you're already using OpenTelemetry, your instrumentation library probably does this automatically — verify it actually reaches us.
  • Are you logging the traceId from extensions on the client side? Capture it in your error tracking (Sentry, Datadog, etc.) so support tickets can include it.
  • If you don't send a traceparent, we generate one for you. It's still returned in extensions.traceId — capture it either way.

When you contact support

Providing a trace ID with bug reports will help us help you better. Make it part of your standard support workflow.


6. Functional coverage

These are features that need explicit implementation work. They're not on by default just because you've integrated the API.

  • Campaign sites — is the campaign-sites functionality implemented?
  • Affiliates — is affiliate functionality implemented?
  • Voucher → totals → payment reload chain — when a voucher is added in checkout, totals must update and payment must reload. Test this end-to-end before launch.

User adds voucher

applyVoucher mutation

Totals recalculated server-side

Update totals in UI

Reload payment

Payment reflects
new total

  • EU Omnibus directive — is lowestPrice displayed on discounted items? If you sell in the EU and run price campaigns, displaying the lowest price from the preceding 30 days alongside any promotional price is a legal requirement. Use DisplayItem.lowestPrice (session mode) or DisplayItem.lowestPriceByPricelist (no-session mode). See Lowest price for implementation details.

  • Country changes update market, pricelist, and cart state — when a customer changes country, the active market and pricelist change with it. This affects currency, prices, available products, shipping options, and tax. Make sure the site reflects this consistently after the change. If the customer has an active cart or is mid-checkout, we recommend showing a confirmation popup ("You're about to switch to Italy. Your cart will be updated to EUR pricing and some items may no longer be available. Continue?") before applying the change.


7. Plugin & AMS configuration

Settings inside the AMS (admin) and plugin configuration are easy to over-configure. Each enabled item has a cost in payload size and query time.

  • Are only the needed attributes selected in the plugin configuration? Selecting everything "just in case" is the single biggest cause of bloated payloads.
  • Filter fields — are only the filters your storefront actually exposes configured?
  • Product attributes — same principle: only enable what your storefront renders or uses for logic.

Questions?

If anything in this checklist is unclear, reach out to your integration contact. We'd rather answer questions early than have something surface after launch.