This page is the Dev API overview, not the best first page for every workflow.Use this quick routing:
- if you want your first generic Dev API call, start with Quick start
- if you want to build or customize a theme with AI, start with Editing with AI
- if you want theme automation endpoints specifically, read Visual themes first and use this API section as reference after that
Guide vs reference
/themes/*explains the ThemeDocument workflow and when to use settings, document, preview, and publish operations/api-reference/*explains the Dev API shape, auth, scopes, and endpoint details
Official SDKs
If you do not want to hand-write requests, use an official SDK for your language.SDKs & libraries
Official Shoppex SDKs, install commands, public repos, and language examples.
Base URL
All API requests must be made to:Authentication
The Shoppex API uses Bearer token authentication. You can authenticate with:- an API key like
shx_..., for server-to-server integrations you manage directly - an OAuth2 access token like
shpat_..., for installable third-party apps
Authorization: Bearer shx_your_api_key_here. An installable ERP app uses Authorization: Bearer shpat_your_access_token_here.
Include the token in the Authorization header:
products.read or themes.write. See Authentication for the full scope catalog, how to create and rotate keys, and the OAuth2 authorization flow.
Use GET /me/capabilities to inspect an active key’s scopes, enabled payment methods, and (for OAuth2) issuing client at runtime. See Authentication for the full response shape.
Request format
All requests must:- Use
Content-Type: application/json - Send JSON-encoded request bodies
- Include the Authorization header
OAuth2 flow
For third-party apps, Shoppex also supports an OAuth2 authorization-code flow. The merchant browser is redirected toGET /dev/v1/oauth/authorize and approves on a Shoppex-hosted screen. Your backend then exchanges the returned shoa_... code at POST /dev/v1/oauth/token for a shpat_... access token.
The token endpoint returns standard OAuth2 JSON, not the normal Shoppex API envelope. Every other /dev/v1/* endpoint you call afterward still uses the standard Shoppex response format. See Authentication for the full flow with request and response examples.
Payment retries and gateway switches
Shoppex can create more than one internal payment attempt for the same invoice. A customer can start with PayPal, close the checkout, reopen the invoice, and finish with Stripe. This catches most people off guard. Treat the Shoppex invoice or payment ID as the source of truth, not a single provider session or order ID.Orders & disputes
- use
GET /ordersfor your operational order queue - use
POST /orderswhen your backend wants to create a pending order from catalog line items - use
POST /orders/:id/fulfillfor a server-side fulfillment trigger on a pending order - use
POST /orders/:id/refundfor merchant-driven refunds without the dashboard - use
GET /disputesfor chargebacks and payment-risk review
Invoices & payments
- use
GET /invoiceswhen you need the lower-level invoice resource directly - use
POST /payment-linksorPOST /payment-links/:id/togglefor sales-link workflows - use
GET /coupons/code/:codefor server-side coupon validation by code
Subscriptions & customers
- use
POST /subscriptions/:id/pauseorPATCH /subscriptions/:id/custom-fieldsfor recurring billing workflows - use
POST /customers/:id/wallet/creditorPOST /customers/:id/wallet/debitfor store-credit adjustments - use
GET /customers/:id?include_affiliate=truewhen you need customer detail plus affiliate summary in one response
Products & variants
- use
GET /products/:id/variants/fields,POST /products/:id/variants/fields, orPOST /products/:id/variants/fields/:fieldId/optionsfor variant configuration tooling - use
GET /products?include_variants=truewhen you need product reads with inline variant prices
Analytics & affiliates
- use
GET /analytics/reportsorPOST /analytics/reports/:id/generatefor scheduled exports - use
GET /affiliates/customersorPOST /affiliates/applications/:id/approvefor affiliate program automation
Store & themes
- use
GET /store/branding,POST /store/domains/additional,GET /store/domains/additional/:id/verification, orPUT /store/layoutfor storefront automation - use
GET /themes/:id/control/document/draft,PUT /themes/:id/control/document, andPOST /themes/:id/control/publishfor revision-gated ThemeDocument automation - use
GET/POST /themes/:id/control/settingsfor settings-derived themes andPOST /themes/:id/control/previewfor previews
Licenses & webhooks
- use
GET /licenses,POST /licenses,PATCH /licenses/:id, orDELETE /licenses/:idfor license support workflows - use
POST /webhooks/logs/:id/retryorPOST /webhooks/:id/rotate-secretfor webhook ops workflows
Security
- use
GET /security/audit-trailorGET /security/session-trail/:session_idfor security and support workflows
- the public storefront flow already had
POST /v1/storefront/coupons/check GET /coupons/code/:codeis the server-to-server Dev API equivalent
Theme automation shortcut
If you came here for themes, the usual endpoint family is:GET /themesPOST /themes/control/createGET /themes/{id}/control/settingsPOST /themes/{id}/control/settingsGET /themes/{id}/control/documentGET /themes/{id}/control/document/draftPUT /themes/{id}/control/documentGET /themes/{id}/control/document/schemaPOST /themes/{id}/control/previewDELETE /themes/{id}/control/preview/{sessionId}POST /themes/{id}/control/publish
revision back as expected_revision when saving, and
send the new revision returned by the save as expected_revision when
publishing. The MCP tools wrap this exact read → save → publish contract.
POST /themes/control/create accepts these document-native bases: default,
classic, pulse, starlight, and clean-minimal. Every accepted base maps
to a deployed ThemeDocument composition. Retired schemes (apex, nebula,
phantom, shadow, vault) are no longer valid bases. Existing themes on
them keep rendering and stay exportable and importable.
Best reading order:
- Editing with AI
- Visual themes
- this API reference for the exact endpoint details
Response format
All responses return JSON with a consistent structure:- Success Response
- List Response
- Error Response
HTTP status codes
All error responses follow the standard error envelope format. See the Error handling guide for the full status-code table and detailed error codes.
Request tracing
UseX-Request-Id to correlate your logs with Shoppex responses. Shoppex returns the same header on both success and error responses.
X-Request-Id: sync-run-42, Shoppex echoes that same id back. If you do not send one, Shoppex generates one for you.
Rate limiting
API requests are limited to protect the service. If you go over the limit, Shoppex returns a429 Too Many Requests response.
Authenticated Dev API requests return X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and (when blocked) Retry-After headers. If you are rate limited, wait until X-RateLimit-Reset or Retry-After before retrying. See the Error handling guide for a header example and the RATE_LIMITED error shape.
Idempotency
All current state-changing/dev/v1 endpoints support idempotent retries.
This covers product, category, group, coupon, variant, customer, webhook, ticket, review, blacklist, license, escrow, invoice, and payment writes. Retrying POST /customers after a timeout will not create the same customer twice, and retrying POST /webhooks/:id/test will not spam duplicate test deliveries.
Use one of these headers:
Idempotency-KeyX-Idempotency-Key
Idempotency-Status header:
createdwhen the request created a new paymentcachedwhen Shoppex replayed the stored responseprocessingwhen the same key is still in flightbypassedwhen idempotency was intentionally skipped
- the replay key is scoped to your authenticated key, the route, and the idempotency key
- Shoppex also checks the request body for the same method and path
- the replay window is currently 24 hours
5xxresponses are not cached for replay
- same key + same body -> cached response
- same key + different body -> validation error
- same key while the first request is still in flight ->
processing
Pagination
The Dev API uses two pagination models. Cursor-based pagination covers most resource lists, including products, invoices, and orders. Page-based pagination covers operational lists, such as webhook delivery logs, where page navigation is clearer.- Cursor-based
- Page-based
Use cursor-based pagination for feed-style lists like products, invoices, orders, customers, and subscriptions.Response:
Parameters
Basic usage
First request
Next page
Use thenext_cursor value from the previous response.Final page
The response looks like this when no items remain.Code examples
Fetch all pages
Async generator (streaming)
TypeScript
Filtering with pagination
Filters work alongside pagination.Cursor-based endpoints
Pagination best practices
Use reasonable limits
For a UI, 20-25 items is plenty. For a background sync job, raise the limit to 100. Larger limits increase response time.
Do not store cursors
Cursors work for immediate sequential use. They expire after 24 hours. They also become invalid if the underlying data changes significantly.
Handle empty results
An empty
data array with has_more: false is valid. No items match your query.Respect rate limits
When you fetch all pages, add delays between requests to avoid rate limiting.
Filtering
Core list endpoints now support a shared filtering and sorting contract:GET /customersGET /ordersGET /invoices
filters uses field:value pairs separated by commas.
status or customerEmail still work where already documented, but new integrations use filters and sorts instead.