> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shoppex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# API Introduction

> Getting started with the Shoppex Developer API

<Info>
  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](/api-reference/quickstart)
  * if you want to **build or customize a theme with AI**, start with [Build and Customize a Theme with AI](/themes/build-and-customize-with-ai)
  * if you want **theme automation endpoints** specifically, read [Editing Themes](/themes/editing) first and use this API section as reference after that
</Info>

## 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:

* JavaScript / TypeScript backend SDK: [`@shoppexio/sdk`](/api-reference/sdks)
* Python backend SDK: [`shoppexio`](/api-reference/sdks)
* PHP backend SDK: [`shoppexio/shoppex-php`](/api-reference/sdks)

For example, your Node backend creating orders uses `@shoppexio/sdk`, a Python cron job reading customers uses `shoppexio`, and a Laravel admin tool rotating webhooks uses `shoppexio/shoppex-php`.

<Card title="SDKs & Libraries" icon="box-open" href="/api-reference/sdks">
  Official Shoppex SDKs, install commands, public repos, and language examples.
</Card>

## Base URL

All API requests should be made to:

```
https://api.shoppex.io/dev/v1
```

## Authentication

The Shoppex API uses Bearer token authentication.

You can authenticate with:

* an API key like `shx_...`
* an OAuth2 access token like `shpat_...`

An internal sync worker would use `Authorization: Bearer shx_your_api_key_here`, while an installable ERP app would use `Authorization: Bearer shpat_your_access_token_here`.

Include the token in the `Authorization` header:

```bash theme={"system"}
Authorization: Bearer shx_your_api_key_here
```

<Warning>
  Keep your API keys secure. Never expose them in client-side code or public repositories.
</Warning>

API keys can be limited with scopes.

<AccordionGroup>
  <Accordion title="Commerce Scopes">
    * `orders.read`
    * `disputes.read`
    * `subscriptions.write`
    * `payment_links.write`
    * `products.read`
  </Accordion>

  <Accordion title="Customer & Analytics">
    * `customers.write`
    * `analytics.read`
    * `analytics.write`
    * `affiliates.read`
    * `affiliates.write`
    * `reseller_programs.read`
    * `reseller_programs.write`
    * `resellers.read`
    * `resellers.write`
    * `reseller_sales.read`
    * `reseller_payouts.read`
    * `reseller_payouts.write`
    * `reseller_stock.read`
    * `reseller_stock.write`
  </Accordion>

  <Accordion title="Platform & Security">
    * `webhooks.write`
    * `themes.read`
    * `themes.write`
    * `licenses.read`
    * `licenses.write`
    * `security.read`
    * `security.write`
    * `store.read`
    * `store.write`
    * `*` for full access
  </Accordion>
</AccordionGroup>

OAuth2 access tokens use the same Dev API scopes.

For instance, your app requests `orders.read customers.read`, the merchant approves, and the exchanged `shpat_...` token can then only read orders and customers.

## Getting Your API Key

1. Log in to [dashboard.shoppex.io](https://dashboard.shoppex.io)
2. Go to **Settings** → **API Keys**
3. Click **Generate New Key**
4. Choose the scopes your integration needs
5. Copy and store your key securely (it starts with `shx_`)

<Note>
  API keys are shown only once when created. Store them in a secure location like a password manager or environment variables.
</Note>

If your goal is theme automation, the usual beginner setup is:

* API key auth
* `themes.read`
* `themes.write`

Claude Code or Codex editing your theme through the Shoppex theme workflow usually needs `themes.read themes.write`.

## Capabilities Endpoint

Use `GET /me/capabilities` to inspect the active key at runtime.

<CodeGroup>
  ```bash cURL theme={"system"}
  curl https://api.shoppex.io/dev/v1/me/capabilities \
    -H "Authorization: Bearer shx_your_api_key"
  ```
</CodeGroup>

This response tells you:

* which scopes are active
* whether the key has full access
* which resource groups are readable or writable
* which payment methods are enabled for the shop
* which gated Dev API features are enabled for the shop

Notably, `enabledPaymentMethods` tells you whether gateways like Stripe, PayPal, or your connected crypto provider (OxaPay, Cryptomus, NowPayments) are active, and `features.nativeCryptoCheckout` tells you which non-custodial native crypto gateways are available.

If you authenticated with OAuth2, the capabilities response also tells you which OAuth client issued the current access token.

## Request Format

All requests should:

* Use `Content-Type: application/json`
* Send JSON-encoded request bodies
* Include the Authorization header

<CodeGroup>
  ```bash cURL theme={"system"}
  curl https://api.shoppex.io/dev/v1/invoices \
    -H "Authorization: Bearer shx_your_api_key_or_shpat_access_token" \
    -H "Content-Type: application/json"
  ```
</CodeGroup>

## OAuth2 Flow

For third-party apps, Shoppex also supports an OAuth2 authorization-code flow:

<Steps>
  <Step title="Redirect to Authorize">
    Merchant is redirected to `GET /dev/v1/oauth/authorize`
  </Step>

  <Step title="Merchant Approval">
    Shoppex shows a hosted approval screen for the active merchant session
  </Step>

  <Step title="Receive Authorization Code">
    After approval, Shoppex redirects back with a short-lived `shoa_...` code
  </Step>

  <Step title="Exchange Code for Token">
    Your app exchanges the code at `POST /dev/v1/oauth/token`
  </Step>

  <Step title="Call API with Access Token">
    Your app calls normal `/dev/v1/*` endpoints with the returned `shpat_...` access token
  </Step>
</Steps>

In practice: the browser redirects to `/dev/v1/oauth/authorize?...`, the merchant approves on the Shoppex-hosted page, your backend exchanges the code via `POST /dev/v1/oauth/token`, then calls `GET /dev/v1/orders` with `Authorization: Bearer shpat_...`.

Important:

* `/dev/v1/oauth/token` returns standard OAuth2 JSON, not the normal Shoppex API envelope
* normal Dev API resource endpoints still use the standard Shoppex response format

## Payment Retries And Gateway Switches

Shoppex may create more than one internal payment attempt for the same invoice. A customer might 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.

<AccordionGroup>
  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="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
  </Accordion>

  <Accordion title="Security">
    * use `GET /security/audit-trail` or `GET /security/session-trail/:session_id` for security and support workflows
  </Accordion>
</AccordionGroup>

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/importable.

Best reading order:

1. [Build and Customize a Theme with AI](/themes/build-and-customize-with-ai)
2. [Editing Themes](/themes/editing)
3. this API reference for the exact endpoint details

## Response Format

All responses return JSON with a consistent structure:

Authenticated Dev API responses also include `X-Request-Id`. If you send `X-Request-Id: sync-run-42`, Shoppex echoes that same id back. If you don't send one, Shoppex generates one for you — useful for correlating logs when debugging.

<Tabs>
  <Tab title="Success Response">
    ```json theme={"system"}
    {
      "data": {
        "id": "abc123",
        "status": "COMPLETED"
      }
    }
    ```
  </Tab>

  <Tab title="List Response">
    ```json theme={"system"}
    {
      "data": [
        { "id": "abc123" },
        { "id": "def456" }
      ],
      "pagination": {
        "next_cursor": "eyJpZCI6MTIzfQ",
        "has_more": true
      }
    }
    ```
  </Tab>

  <Tab title="Error Response">
    ```json theme={"system"}
    {
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "Invalid email address",
        "details": [
          { "field": "email", "message": "must be a valid email" }
        ],
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Tab>
</Tabs>

## HTTP Status Codes

<Info>
  All error responses follow the standard error envelope format. See the [Error Handling guide](/api-reference/errors) for detailed error codes.
</Info>

| Code  | Description                                         |
| ----- | --------------------------------------------------- |
| `200` | Success                                             |
| `201` | Resource created                                    |
| `400` | Business rule or domain-specific request error      |
| `401` | Unauthorized (invalid or missing API key)           |
| `403` | Forbidden (missing scope or suspended shop)         |
| `404` | Resource not found                                  |
| `422` | Validation error for request body, params, or query |
| `429` | Rate limit exceeded                                 |
| `500` | Internal server error                               |

## Request Tracing

Use `X-Request-Id` to correlate your logs with Shoppex responses.

```http theme={"system"}
X-Request-Id: sync-run-42
```

Shoppex returns the same header on success and error responses.

## Rate Limiting

API requests are limited to protect the service. When you exceed the limit, you'll receive a `429 Too Many Requests` response.

Authenticated Dev API requests return these headers:

* `X-RateLimit-Limit`
* `X-RateLimit-Remaining`
* `X-RateLimit-Reset`
* `Retry-After` when the request is blocked

When you hit the limit, the headers look like this:

```http theme={"system"}
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 5
Retry-After: 5
```

<Note>
  Rate limits are plan-dependent. The values above are illustrative. Check the response headers from your actual requests for your current limits.
</Note>

If rate limited, wait until `X-RateLimit-Reset` or `Retry-After` before retrying.

## 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 won't create the same customer twice, and retrying `POST /webhooks/:id/test` won't spam duplicate test deliveries.

Use one of these headers:

* `Idempotency-Key`
* `X-Idempotency-Key`

<CodeGroup>
  ```bash cURL theme={"system"}
  curl https://api.shoppex.io/dev/v1/payments \
    -X POST \
    -H "Authorization: Bearer shx_your_api_key" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: payment-create-123" \
    -d '{"title":"Order #1001","email":"buyer@example.com","value":49.99,"currency":"USD"}'
  ```
</CodeGroup>

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 currently uses two pagination models: **cursor-based pagination** for feed-style entity lists (products, invoices, orders, customers, subscriptions) and **page-based pagination** for operational lists (webhook delivery logs, affiliate review queues). Most resource lists use cursor-based pagination with `cursor` and `limit` parameters.

<Info>
  For detailed pagination patterns, code examples, and best practices, see the [Pagination guide](/api-reference/pagination).
</Info>

## Filtering

Core list endpoints now support a shared filtering and sorting contract:

```bash theme={"system"}
# Filter by exact fields
GET /orders?filters=status:COMPLETED,customer_email:john@example.com

# Sort oldest first
GET /customers?sorts=created_at

# Sort newest first (default)
GET /invoices?sorts=-created_at
```

Supported list endpoints for this shared contract:

* `GET /customers`
* `GET /orders`
* `GET /invoices`

`filters` uses `field:value` pairs separated by commas.

```bash theme={"system"}
filters=status:COMPLETED,customer_email:buyer@example.com
sorts=created_at     # oldest first
sorts=-created_at    # newest first (default)
```

Legacy single-field query params like `status` or `customerEmail` still work where already documented, but new integrations should prefer `filters` and `sorts`.
