API Reference
Last updated: August 2026
Every Qwikr store is headless by default. The same HTTP API that powers the hosted storefront is available to your own frontend, mobile app or back-office system — you can replace the storefront entirely and keep the rest of the platform.
Base URL
https://api.{your-store-domain}
Your tenant is resolved from the hostname. If you are calling from somewhere that isn't your store domain — a static site on a CDN, a local dev server — send your store slug instead:
X-Tenant-Slug: your-store-slug
404. That is deliberate: an unknown slug must not be
distinguishable from a store with no matching record.Authentication
There are two credential types. They are not interchangeable.
1. Server keys (esk_…)
For your own backend: fulfilment, reporting, admin automation. Create one in Settings → API Keys. The plaintext key is shown once — store it immediately. Never ship one to a browser or mobile binary.
Authorization: Bearer esk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2. Customer session tokens
For logged-in shopper actions (order history, wishlists, saved addresses).
Obtained from POST /api/customer/login and passed the same way.
These are scoped to one customer and carry no admin rights.
POST /api/customer/login
Body: { "email": "...", "password": "..." }
Returns: { "token": "..." }
Scopes
Every key-protected endpoint requires one specific scope. A key holding none of them can call nothing — there is no "any valid key" tier.
| Scope | Grants |
|---|---|
read_orders | Packing slips, pick lists, label batch status, rental contracts & claims (read), payment plan status |
write_orders | Buying shipping & return labels, refunds, sending contracts, taking payment-plan payments, booking operators |
read_analytics | Bundle analytics, rental analytics (revenue, assets, customers) |
Grant the narrowest set that works. A reporting integration should hold
read_analytics alone — a key that can read revenue should not also
be able to spend money on labels.
Errors
401 { "error": "API key required" } – missing or malformed
401 { "error": "Invalid API key" } – no matching key
401 { "error": "API key has expired" } – past expires_at
403 { "error": "Missing required scope: write_orders" }
Storefront endpoints
These are public — no key required — because they serve the same data a shopper's browser already sees. They are rate limited per IP.
Catalog
GET /api/products – paginated list
GET /api/products/featured
GET /api/products/{slug}
GET /api/products/{slug}/variants
GET /api/products/{slug}/reviews
GET /api/products/{id}/social-proof
GET /api/categories
GET /api/search?q=…
GET /api/search/autocomplete
GET /api/currencies
GET /api/flash-sales
Cart
Carts are keyed by an X-Cart-Session header you generate and
persist client-side (localStorage or equivalent). No cookies, so CORS works
from any origin without credentials.
GET /api/cart
POST /api/cart/items
PATCH /api/cart/items/{item}
DELETE /api/cart/items/{item}
PATCH /api/cart/options
DELETE /api/cart
POST /api/cart/coupon
POST /api/cart/gift-card
Checkout
Guest-friendly: pass a customer token to attach the order to an account, or just an email address to check out as a guest.
POST /api/checkout
GET /api/tracking/{reference}
POST /api/orders/{reference}/return
Content
GET /api/posts GET /api/storefront/sections POST /api/newsletter/subscribe POST /api/contact
Fulfilment endpoints
Server keys only — the scope each one needs is shown.
GET /api/orders/{reference}/packing-slip read_orders
GET /api/orders/batch/pick-list read_orders
GET /api/orders/batch-labels/{batchId}/status read_orders
POST /api/orders/{reference}/shipping-label write_orders
POST /api/orders/{reference}/shipping-labels/multi write_orders
POST /api/orders/{reference}/return-label write_orders
POST /api/orders/batch-labels write_orders
POST /api/orders/{reference}/refund write_orders
GET /api/bundles/analytics read_analytics
Rate limits
Per IP, per minute. Exceeding a limit returns 429 with a
Retry-After header.
- Catalog and cart reads — 120/min
- Checkout, refunds, label purchases — 30/min
- Batch label jobs — 10/min
- Contact form — 5/min
Webhooks
Register endpoints in Settings → Webhooks, optionally with an IP allowlist. Failed deliveries are retried automatically and every attempt is recorded in the delivery log.
Verifying the signature
Each request carries an HMAC-SHA256 of the raw body, computed with your webhook's secret:
X-Webhook-Signature: sha256=<hex digest>
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
if (! hash_equals($expected, $request->header('X-Webhook-Signature'))) {
abort(401);
}
hash_equals, crypto.timingSafeEqual). A plain
=== leaks the digest one byte at a time.Events
order.created product.created subscription.renewed
order.paid product.updated subscription.cancelled
order.shipped product.low_stock subscription.past_due
order.delivered product.out_of_stock customer.created
order.refunded product.back_in_stock return.submitted
return.approved
review.created
CORS
All /api/* routes accept any origin. Credentials are not
supported — carts and sessions travel in headers, not cookies. Allowed
request headers are Content-Type, Accept,
Authorization, X-Cart-Session,
X-Session-Id, X-Tenant-Slug and
X-Requested-With.