API ReferenceGoing live

Going live

The pre-flight checklist before flipping from brainerce_test_* to brainerce_* in production. Skip steps at your own risk.

Before your first real customer transaction, walk this list end-to-end. Each item exists because someone shipped without it and paid for it later.

1. Keys and secrets

  • Rotated the test key. Make sure no brainerce_test_* key is hard-coded anywhere — search your repo and CI/CD secret stores for brainerce_test_ before deploy.
  • Issued a live key with minimum scopes. Don't ship a wildcard. Pick exactly the scopes the integration needs (Resource locations).
  • Stored the live key in a secret manager, not in .env checked into git. Doppler / 1Password / AWS SM / Vault — pick one.
  • Set an IP allowlist on the key (if your integration runs from fixed IPs). Lock down at the key, not the firewall — keys leak more than networks do.

2. Webhooks

  • Subscribed to the events you actually need — not all of them. Each event adds a delivery your endpoint has to handle correctly. See the webhook event catalogue.
  • Verification is in place — signature + replay window + constant-time compare. See Verify a webhook signature.
  • Idempotency on your side — store envelope.id and reject duplicates. Any non-2xx response is retried, not just 5xx: a 400, 404 or 409 from your handler is retried the full 5 attempts and counts toward the circuit breaker exactly like a timeout. If you want a delivery dropped rather than retried, answer 2xx and discard it yourself.
  • Reply fast. The dispatcher has a 10-second timeout. Reply with 2xx in under 2 seconds and queue the actual work async.
  • You have a plan for the circuit breaker, because self-healing is slow and nothing resends for you. Ten failed delivery attempts — roughly two consecutive failing events, since each event is tried 5 times — open the circuit. It half-opens an hour later and lets the next event (plus any arriving in the few seconds before its result lands) through as a probe: a 2xx resumes delivery, anything else re-opens it for another hour, and because failureCount is not reset, one failed probe is enough. Events that happen meanwhile are recorded as Not sent in the endpoint's event log (up to 500 per endpoint, kept 7 days) but are never replayed automatically: once the endpoint works, resend them from the event log or with POST /stores/{storeId}/webhook-subscriptions/{id}/replay-missed (25 per call, oldest first). A failed event (five attempts used) is not queued again either; resend it with POST .../redeliver. Alert on circuitOpen: true and on missedCount (see §9) and treat it as a page, not a warning — waiting for the probe costs you an hour with nothing sent, and a Not sent backlog older than 7 days is no longer counted or replayed in bulk (the rows stay in the event log, where you can still resend them with POST .../redeliver). See Resending events.
  • Resends are safe on your side. A resent event keeps its original event id and carries X-Brainerce-Redelivery: true; dedupe on the id in your own inbox, and on a business key (the order id) wherever a third-party API is called, because event-id dedupe protects only your own inbox.
  • Tested with the dashboard "Send test event" button — confirmed signature verification passes against a real Brainerce-signed payload. Note this sends a fresh test event; to re-send a past delivery, use Resend on that row of the event log (test events themselves cannot be resent). On an endpoint with custom headers or a body template a test is a real call to the destination, so the API answers 400 unless you send confirmLiveCall: true; use POST .../preview to see the body without sending.

3. Idempotency on mutations

  • Every POST/PATCH/DELETE retry includes an Idempotency-Key. Otherwise a network blip can double-charge, double-ship, or double-cancel. See Idempotency.
  • Idempotency keys are deterministic per logical operation — same key for same retry, different key for different orders.
  • POST /v1/checkout/{id}/complete always carries a key. It is required there — without one the call is a 400, before the handler runs.
  • You have checked the unsupported list. Sixteen mutating /v1/* routes ignore the header silently, including POST /v1/orders/{id}/shipments/app-label, which buys a real shipping label and bills your carrier account on every retry. Read Idempotency → Routes that do NOT support it and guard those calls yourself.

4. Rate limits

  • You know your key's tier. A brainerce_* key is on the per-key tiers — FREE 60, PRO 500, GROWTH 2000, ENTERPRISE 10000 req/min. (The 100 / 500 / 1000 numbers are marketplace-app installation tiers and do not apply to an API key.)
  • You have accounted for the global per-IP ceiling on top of that — 60 req/min and 1000 req/hour per IP apply to every request regardless of tier, and some storefront routes cap far lower. See Rate limits.
  • You handle 429 Too Many Requests with the Retry-After header — don't hot-loop. Note X-RateLimit-Reset is seconds remaining, not a Unix timestamp.
  • Burst protection on your side — if a CSV import wakes up 10,000 jobs, batch them so you stay under your tier. There is no burst grace; the bucket rejects at exactly the limit.

5. Inventory + stock

  • Verify the stock the live store really holds. A test key and a live key act on the same store data (a test key is not a sandbox), so there is no separate test inventory to carry over, but a product created without a stock quantity has none and cannot be bought until you set one.
  • Decided on trackingMode per product. TRACKED (default) blocks oversells; UNLIMITED accepts any order (digital products, print on demand); DISABLED makes the product impossible to purchase. Get this wrong and you either oversell or never sell.

6. Payments

  • Payment provider is in live mode — in the dashboard the provider's app (Apps → Installed) has Mode set to Live, not Test, and the sales channel itself is in LIVE mode.
  • At least one full live transaction succeeded end-to-end before launch — your test, with a real card, $1 (you can refund).
  • Refund flow tested in live mode — the provider-side refund settles and the refund on the order reaches CAPTURED.
  • Webhook on payment.failed writes to your CRM / alerts. Failed payments are the #1 silent failure mode in production.

7. Email

  • Email sending domain (DKIM/SPF) is verified in Brainerce email settings.
  • Transactional templates customized — order-confirmation, shipping-notification, password-reset have your branding (not the default).
  • From address is your domain, not [email protected].
  • Sent a test email to yourself for every event type your store will trigger.

8. Storefront

  • The sales channel's domain is your production domain. Set it under Channels → your channel → Settings. A LIVE channel matches the request Origin against this field (host equality or a subdomain) and against the channel's "Additional allowed origins" list (exact origins, at most 5: use it for a preview URL you keep building from), so until the domain is right, browsers will get a 403 on every storefront call. An allowed origin never replaces the domain: a LIVE channel with no domain refuses every request. Note a LIVE channel also requires an Origin header. Server code (SSR, Route Handlers, background jobs) sends none by default, so it is refused until you pass origin when you construct the client, derived from the incoming request so it matches the channel's domain; see Origin rules. Do not swap in an admin brainerce_* key to get around it.
  • CSP headers in your storefront allow Brainerce + your payment provider domains. A strict CSP blocks the payment iframe silently, so build it with buildStorefrontCsp({ nonce, apiOrigin }) from brainerce: its frame-src covers every payment host plus your store's API origin. If product descriptions use video embeds, also add https://www.youtube.com, https://www.youtube-nocookie.com, and https://player.vimeo.com to frame-src, or the embeds render blank.
  • Cookie domain is the production domain (not localhost or staging).
  • Custom domain SSL is provisioned and HSTS is set — Brainerce requires HTTPS on webhookUrl and OAuth callbacks.
  • On Vercel, the Vercel function region is pinned next to the Brainerce API. The default function region is iad1 (Washington, D.C.) and adds roughly 300 to 450 ms to every call a function makes to the API. For a store served from Europe set { "regions": ["fra1"] } in vercel.json or Project Settings → Functions → Function Region, then redeploy. (This is Vercel's setting, not a commerce region.) Public reads (products, categories) may call the API straight from the browser instead of through your own proxy.
  • cartToken is redacted from your logs. Guest cart writes carry the cart's session token as the cartToken query parameter, which is a credential for that one cart. Strip it in CDN, proxy and APM logs, including the Vercel function logs of a Backend-For-Frontend, and make sure any proxy of yours forwards the query string. See Cart ownership proof.
  • A 403 CART_ACCESS_DENIED does not log the shopper out. It only means the guest cart's proof was missing; the SDK sends it for you.
  • You ran the behaviour check on a phone viewport. node verify-storefront.mjs --url https://your-shop.example --viewport 390x844 --slow (from https://brainerce.com/verify-storefront.mjs; needs Playwright) drives the real storefront and checks header navigation without a page reload, no "empty cart" flash, the loading and error states, quick taps, checkout waiting for a pending update, floating widgets not covering cart or checkout controls, and autocomplete. It never submits an order. Fix every FAIL before launch.

9. Observability

  • Logs from your integration record the endpoint, and on errors the timestamp and path from the error body. There is no request-id response header — neither X-Request-Id nor X-Brainerce-Request-Id is set by the API, so generate your own correlation id client-side and log it alongside those two fields. That is what lets support find the request.
  • You have an alert on 5xx rate from Brainerce. If your integration starts seeing 5%+ 5xx, something is wrong (yours, ours, or upstream).
  • Webhook circuit-breaker state is monitored. If your endpoint opens the circuit, you stop receiving events — silently. Check GET /stores/{storeId}/webhook-subscriptions/{id} periodically for circuitOpen: true and for a growing missedCount (events waiting as Not sent, kept 7 days), and alert.
  • A dashboard chart of API key usage exists (your side) — sudden spikes/drops are the first sign of a compromised key or a broken cron.

10. Data + GDPR

  • Customer data export wired — for "deliver my data" requests under GDPR / CCPA. GET /api/v1/customers/{id}, GET /api/v1/customers/{id}/addresses and GET /api/v1/customers/{id}/orders cover the profile, addresses and order history.
  • Customer deletion has an owner. The /v1 API has no customer delete route. A customer is erased from the dashboard (Customers) or with the admin MCP tool delete_customer: the customer row, addresses, saved payment tokens, closed inquiries, assistant chats and similar are removed, and email logs and consent records lose the address. Orders are not anonymised yet, so it is refused with 409 for any customer who has an order, an active recurring payment schedule, a paid loyalty membership that is still billing, a recorded loyalty membership charge or an open inquiry. The response body lists every blocker in details.blockers. Check your own deletion procedure before live requests come in.
  • Marketing consent flag is respected — customer.acceptsMarketing: false should never receive a marketing email.
  • Privacy policy + terms-of-service URLs in store settings point at live, signed-off legal pages.

11. Versioning

  • You're calling /api/v1/* paths, not internal /api/{module} ones. Internal paths can change without notice; /api/v1/* carries the versioning guarantee.
  • Someone owns reading the Changelog before each SDK bump. There is no deprecation response header to automate against — X-Brainerce-Deprecation is not emitted — so the changelog and the dashboard banner are the whole early-warning system.

12. Rollback plan

  • You can roll your storefront back to the previous Brainerce SDK version within 5 minutes — pinned to a major in package.json, prior version cached.
  • You can disable a webhook subscription without redeploying — DELETE the subscription via the API, or toggle it in the dashboard.
  • You can revoke a leaked API key in under 60 seconds — practiced once before launch.

13. Day 1 verification

After the first hour of live traffic, confirm:

  • First real order.created arrived at your webhook and was processed
  • First real payment.succeeded settled in the payment-provider dashboard
  • New shoppers are reaching your CRM. customer.created fires for a merchant-created customer and for a storefront, email-verified or OAuth signup. It does not fire for a guest checkout — those shoppers arrive on order.created / checkout.completed, which carry the enriched customer (best-effort: when data.order is missing, fetch the order by orderId). Confirm your handler is an upsert keyed on customerId: a guest who later registers produces a customer.created for a customer you already have. See the event catalogue.
  • Your 5xx rate is < 1%
  • Your webhook delivery success rate (in Brainerce dashboard) is > 99%

If any of these are red, your launch is degraded — fix before pushing more traffic.