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
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)
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
/api/v1/{shopId}/productsList 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"{
"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 }
}/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"{
"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
}
]
}
}/api/v1/{shopId}/productsCreate 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
}'{
"product": {
"id": "prod_01HXYZ...",
"title": "Hiking backpack",
"handle": "hiking-backpack",
"status": "ACTIVE"
}
}Orders
/api/v1/{shopId}/ordersList 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"{
"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 }
}/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"{
"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
/api/v1/{shopId}/customersList 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"{
"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 }
}/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"{
"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.
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// 401
{ "error": "Unauthorized" }
// 403
{ "error": "Plan does not allow this scope" }
// 404
{ "error": "Product not found" }
// 400
{ "error": "Validation error", "details": [/* … */] }