RetailLink

Merchant API

REST API for server-to-server integrations. Base path /api/v1/{shopId}. Each endpoint below includes language tabs and sample responses.

OpenAPI

Machine-readable contract (same routes as this page). Local: GET http://localhost:3003/api/v1/openapi.json.

curl -s "http://localhost:3003/api/v1/openapi.json" \
  -H "Accept: application/json"

Base URL

Localtext
http://localhost:3003/api/v1/{shopId}

Production uses your deployed API host. Always send Authorization: Bearer sk_live_… (or X-RetailLink-Access-Token).

Scopes & plan limits

Growth (limited): read scopes only — read_products, read_orders, read_customers, read_inventory. Keys: 2 · webhooks: 3.

Enterprise / active trial (full): adds write_* counterparts. Keys: 10 · webhooks: 25.

Inventory scopes

read_inventory / write_inventory exist on keys for plan gating. There is no public /inventory Merchant API route yet — stock fields appear nested on products/variants where returned.

Resource map (v1 today)

Routeshttp
GET    /products
GET    /products/{id}
POST   /products          # write_products · Enterprise / trial
GET    /orders
GET    /orders/{id}
GET    /customers
GET    /customers/{id}

No other Merchant API resources are published in v1. Domain models (gift cards, POs, locations, …) are documented under Objects as Admin / POS.

Products

GET/api/v1/{shopId}/products

List products with optional search, status, and pagination (page, limit).

# List products
curl -s "http://localhost:3003/api/v1/{shopId}/products?limit=5" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Accept: application/json"
{ } Response200 OK
{
  "products": [
    {
      "id": "prod_01HXYZ...",
      "title": "Hiking backpack",
      "handle": "hiking-backpack",
      "status": "ACTIVE",
      "variants": [
        {
          "id": "var_01HXYZ...",
          "title": "Default Title",
          "sku": "BP-001",
          "barcode": "5000123456789",
          "price": 49.99,
          "inventoryQuantity": 12
        }
      ]
    }
  ],
  "pagination": { "page": 1, "limit": 5, "total": 42, "pages": 9 }
}
GET/api/v1/{shopId}/products/{productId}

Retrieve a single product and its variants.

curl -s "http://localhost:3003/api/v1/{shopId}/products/{productId}" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Accept: application/json"
{ } Response200 OK
{
  "product": {
    "id": "prod_01HXYZ...",
    "title": "Hiking backpack",
    "handle": "hiking-backpack",
    "status": "ACTIVE",
    "description": null,
    "variants": [
      {
        "id": "var_01HXYZ...",
        "title": "Default Title",
        "sku": "BP-001",
        "price": 49.99,
        "inventoryQuantity": 12
      }
    ]
  }
}
POST/api/v1/{shopId}/products

Create a product. Requires write_products (Enterprise or active trial).

# Requires write_products · Enterprise / trial
curl -s -X POST "http://localhost:3003/api/v1/{shopId}/products" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Hiking backpack",
    "status": "ACTIVE",
    "price": 49.99,
    "sku": "BP-001",
    "inventoryQuantity": 12
  }'
{ } Response201 Created
{
  "product": {
    "id": "prod_01HXYZ...",
    "title": "Hiking backpack",
    "handle": "hiking-backpack",
    "status": "ACTIVE"
  }
}

Orders

GET/api/v1/{shopId}/orders

List omnichannel orders (POS + storefront). Managed in Admin → Orders.

curl -s "http://localhost:3003/api/v1/{shopId}/orders?limit=10" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Accept: application/json"
{ } Response200 OK
{
  "orders": [
    {
      "id": "ord_01HXYZ...",
      "orderNumber": "10042",
      "status": "CONFIRMED",
      "channel": "POS",
      "total": 49.99,
      "currency": "GBP",
      "createdAt": "2026-10-08T12:00:00.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 128, "pages": 13 }
}
GET/api/v1/{shopId}/orders/{orderId}

Retrieve a single order with items, customer, and payments when included.

curl -s "http://localhost:3003/api/v1/{shopId}/orders/ord_01HXYZ..." \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Accept: application/json"
{ } Response200 OK
{
  "order": {
    "id": "ord_01HXYZ...",
    "orderNumber": "10042",
    "receiptNumber": "R-10042",
    "status": "CONFIRMED",
    "totalAmount": 49.99,
    "currency": "GBP",
    "customer": { "id": "cus_01HXYZ...", "email": "alex@example.com" },
    "items": [],
    "payments": []
  }
}

Customers

GET/api/v1/{shopId}/customers

List customers from the shop directory (Admin → Customers).

curl -s "http://localhost:3003/api/v1/{shopId}/customers?limit=10" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Accept: application/json"
{ } Response200 OK
{
  "customers": [
    {
      "id": "cus_01HXYZ...",
      "email": "alex@example.com",
      "firstName": "Alex",
      "lastName": "Ng",
      "phone": null,
      "createdAt": "2026-10-01T09:00:00.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 10, "total": 56, "pages": 6 }
}
GET/api/v1/{shopId}/customers/{customerId}

Retrieve a single customer.

curl -s "http://localhost:3003/api/v1/{shopId}/customers/cus_01HXYZ..." \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Accept: application/json"
{ } Response200 OK
{
  "customer": {
    "id": "cus_01HXYZ...",
    "email": "alex@example.com",
    "firstName": "Alex",
    "lastName": "Ng",
    "phone": null,
    "storeCreditBalance": 0,
    "_count": { "orders": 3 },
    "createdAt": "2026-10-01T09:00:00.000Z",
    "updatedAt": "2026-10-08T12:00:00.000Z"
  }
}

Errors

Error bodies are JSON with an error string. Some responses include hint or details.

Status codestext
401  Missing or invalid API key
403  Plan does not allow this scope / write requires full API access
404  Resource not found (wrong shop or id)
400  Validation error (body or query)
429  Reserved — treat as retryable if you see it from a proxy
Examplesjson
// 401
{ "error": "Unauthorized" }

// 403
{ "error": "Plan does not allow this scope" }

// 404
{ "error": "Product not found" }

// 400
{ "error": "Validation error", "details": [/* … */] }