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
Without a resolvable tenant, tenant-scoped endpoints return 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.

ScopeGrants
read_ordersPacking slips, pick lists, label batch status, rental contracts & claims (read), payment plan status
write_ordersBuying shipping & return labels, refunds, sending contracts, taking payment-plan payments, booking operators
read_analyticsBundle 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.

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);
}
Compare with a constant-time function (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.