Skip to main content
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
Use the theme pages to understand the workflow. Use the API reference when you already know the workflow and need the exact request shape.

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
An internal sync worker uses 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:
Keep your API keys secure. Never expose them in client-side code or public repositories.
API keys and OAuth2 access tokens can both be limited with scopes, for example 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 to GET /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 /orders for your operational order queue
  • use POST /orders when your backend wants to create a pending order from catalog line items
  • use POST /orders/:id/fulfill for a server-side fulfillment trigger on a pending order
  • use POST /orders/:id/refund for merchant-driven refunds without the dashboard
  • use GET /disputes for chargebacks and payment-risk review

Invoices & payments

  • use GET /invoices when you need the lower-level invoice resource directly
  • use POST /payment-links or POST /payment-links/:id/toggle for sales-link workflows
  • use GET /coupons/code/:code for server-side coupon validation by code

Subscriptions & customers

  • use POST /subscriptions/:id/pause or PATCH /subscriptions/:id/custom-fields for recurring billing workflows
  • use POST /customers/:id/wallet/credit or POST /customers/:id/wallet/debit for store-credit adjustments
  • use GET /customers/:id?include_affiliate=true when you need customer detail plus affiliate summary in one response

Products & variants

  • use GET /products/:id/variants/fields, POST /products/:id/variants/fields, or POST /products/:id/variants/fields/:fieldId/options for variant configuration tooling
  • use GET /products?include_variants=true when you need product reads with inline variant prices

Analytics & affiliates

  • use GET /analytics/reports or POST /analytics/reports/:id/generate for scheduled exports
  • use GET /affiliates/customers or POST /affiliates/applications/:id/approve for affiliate program automation

Store & themes

  • use GET /store/branding, POST /store/domains/additional, GET /store/domains/additional/:id/verification, or PUT /store/layout for storefront automation
  • use GET /themes/:id/control/document/draft, PUT /themes/:id/control/document, and POST /themes/:id/control/publish for revision-gated ThemeDocument automation
  • use GET/POST /themes/:id/control/settings for settings-derived themes and POST /themes/:id/control/preview for previews

Licenses & webhooks

  • use GET /licenses, POST /licenses, PATCH /licenses/:id, or DELETE /licenses/:id for license support workflows
  • use POST /webhooks/logs/:id/retry or POST /webhooks/:id/rotate-secret for webhook ops workflows

Security

  • use GET /security/audit-trail or GET /security/session-trail/:session_id for security and support workflows
Coupon note:
  • the public storefront flow already had POST /v1/storefront/coupons/check
  • GET /coupons/code/:code is the server-to-server Dev API equivalent
The reliable pattern: use Shoppex invoice/payment responses, listen to Shoppex webhooks, and make your fulfillment logic idempotent. Do not assume one invoice always maps to exactly one provider-side payment session.

Theme automation shortcut

If you came here for themes, the usual endpoint family is:
  • GET /themes
  • POST /themes/control/create
  • GET /themes/{id}/control/settings
  • POST /themes/{id}/control/settings
  • GET /themes/{id}/control/document
  • GET /themes/{id}/control/document/draft
  • PUT /themes/{id}/control/document
  • GET /themes/{id}/control/document/schema
  • POST /themes/{id}/control/preview
  • DELETE /themes/{id}/control/preview/{sessionId}
  • POST /themes/{id}/control/publish
These endpoints inspect, revision-save, preview, and publish validated ThemeDocuments and their settings. For document-authoritative themes, read the draft first. Send its 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:
  1. Editing with AI
  2. Visual themes
  3. this API reference for the exact endpoint details

Response format

All responses return JSON with a consistent structure:

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

Use X-Request-Id to correlate your logs with Shoppex responses. Shoppex returns the same header on both success and error responses.
If you send 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 a 429 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-Key
  • X-Idempotency-Key
Responses include an Idempotency-Status header:
  • created when the request created a new payment
  • cached when Shoppex replayed the stored response
  • processing when the same key is still in flight
  • bypassed when idempotency was intentionally skipped
Important behavior:
  • 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
  • 5xx responses are not cached for replay
In other words:
  • 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.
Use cursor-based pagination for feed-style lists like products, invoices, orders, customers, and subscriptions.

Parameters

Basic usage

First request

Response:

Next page

Use the next_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.
Always include the same filters and sorts when you follow a cursor. Changing them invalidates the cursor.

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:
Supported list endpoints for this shared contract:
  • GET /customers
  • GET /orders
  • GET /invoices
filters uses field:value pairs separated by commas.
Legacy single-field query params like status or customerEmail still work where already documented, but new integrations use filters and sorts instead.