API Reference
The Shopvo v1 REST API gives headless storefronts, mobile apps, and third-party integrations full access to catalog, orders, customers, and more.
Base URL: https://<your-domain>/api/v1/<siteId>
Authentication
Every request must carry a Bearer API key in the Authorization header:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API keys are created in the Shopvo Admin under Settings → API Keys. Each key is scoped to a single site and carries an explicit set of permission scopes.
Key formats
| Prefix | Environment | Use |
|---|---|---|
sk_live_… | Production (live) | Real orders and customers. Keep secret. |
sk_test_… | Test / sandbox | Development and CI pipelines. |
Request Signing (HMAC-SHA256)
When a signing secret (sks_…) is configured on the API key, you should include an X-Shopvo-Signature header on every request. The server uses this header to verify authenticity and reject replay attacks.
401 Unauthorized.Header format
X-Shopvo-Signature: t=<unix-timestamp>,sha256=<hmac-hex>
The HMAC-SHA256 is computed over the UTF-8 string <timestamp>.<raw-request-body> using the signing secret as the key.
Node.js example
import crypto from "crypto";
function buildSignatureHeader(body: string, signingSecret: string): string {
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = `${timestamp}.${body}`;
const sig = crypto
.createHmac("sha256", signingSecret)
.update(payload, "utf8")
.digest("hex");
return `t=${timestamp},sha256=${sig}`;
}
// Usage
const body = JSON.stringify({ productId: "prd_abc" });
const header = buildSignatureHeader(body, "sks_xxxxxxxx…");
await fetch(`https://your-domain/api/v1/${siteId}/products`, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
"X-Shopvo-Signature": header,
},
body,
});TypeScript SDK (recommended)
The official @shopvo/sdk handles signing automatically:
import { createClient } from "@shopvo/sdk";
const client = createClient({
apiKey: "sk_live_…",
signingSecret: "sks_…", // optional — enables HMAC on every request
siteId: "site_…",
baseUrl: "https://your-domain",
});
// All requests are automatically signed
const products = await client.products.list({ limit: 20 });Verifying incoming webhook signatures
Shopvo also signs outgoing webhook payloads with the same scheme. Verify on your receiver endpoint:
function verifyWebhookSignature(
rawBody: string,
header: string,
secret: string,
toleranceSec = 300
): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = parts["t"];
const received = parts["sha256"];
if (!timestamp || !received) return false;
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (age > toleranceSec) return false; // replay protection
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`, "utf8")
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}Permissions & Scopes
Every API key carries an explicit list of permission scopes. Calling an endpoint that requires a scope not on the key returns 403 Forbidden.
| Scope | Grants access to |
|---|---|
read:catalog | List and get products, variants, categories, brands, reviews |
write:catalog | Create, update, delete products and variants; submit reviews |
read:content | Pages, menus, site configuration |
read:orders | Order list and detail |
read:customers | Customer list and detail |
write:customers | Update customer profiles |
read:cart | Fetch cart |
write:cart | Create, modify, delete cart |
read:checkout | Fetch checkout session |
write:checkout | Create and advance checkout |
read:inventory | List stock levels |
write:inventory | Adjust stock levels |
read:promotions | List promotions and price rules |
write:promotions | Create and delete promotions |
read:gift-cards | List and read gift cards |
write:gift-cards | Create, update, and void gift cards |
read:store-credit | Read customer store credit balances and transactions |
write:store-credit | Issue, adjust, and void store credit |
read:loyalty | Read loyalty accounts and point transactions |
write:loyalty | Award, deduct, and redeem loyalty points |
read:b2b | Read B2B accounts and pricing tiers |
write:b2b | Create and manage B2B accounts and custom pricing |
webhooks:read | List webhook subscriptions |
webhooks:write | Create and delete webhook subscriptions |
metafields:read | Read metafields on any resource |
metafields:write | Create, update, and delete metafields |
admin:* | Full admin access — all scopes above |
Pagination
List endpoints support two pagination modes. Cursor pagination is recommended for real-time feeds and large result sets because it remains stable as records are added.
Offset pagination (default)
GET /api/v1/{siteId}/products?page=2&limit=20{
"success": true,
"items": [...],
"pagination": { "page": 2, "limit": 20, "total": 84, "totalPages": 5 }
}Cursor pagination
Pass cursor=<id> instead of page. The response drops pagination and returns a cursor envelope.
GET /api/v1/{siteId}/products?cursor=prd_abc123&limit=20{
"success": true,
"items": [...],
"cursor": "prd_xyz789", // pass as ?cursor= to get the next page
"hasNextPage": true,
"limit": 20
}Field Selection
The /products endpoint supports sparse fieldsets via ?fields= and eager expansion of related resources via ?expand[]=.
// Only return id, name, and price GET /products?fields=id,name,price,slug // Eagerly load variants and brand alongside each product GET /products?expand[]=variants&expand[]=brand&expand[]=categories
Supported expand[] values: variants, brand, categories.
Rate Limits
| Route | Default limit |
|---|---|
| All routes | Plan-dependent (30–1 000 rpm) |
| /checkout | 10 rpm |
| /orders | 30 rpm |
| /products?search= | 60 rpm |
Rate limit headers are returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. When exceeded the API returns 429 Too Many Requests with a Retry-After header.
Products
/api/v1/{siteId}/productsread:catalogReturns a paginated list of products. Supports offset and cursor pagination, field selection, and resource expansion.
Query Parameters
| Name | Type | Description |
|---|---|---|
page | integer | Page number for offset pagination (default 1). Ignored when cursor is provided. |
limit | integer | Results per page (max 100, default 20). |
cursor | string | Opaque cursor from a previous response for cursor-based pagination. |
fields | string | Comma-separated list of fields to include (sparse fieldset). |
expand[] | string[] | Sub-resources to eager load: variants, brand, categories. |
search | string | Full-text search across name, SKU, and description. |
categoryId | string | Filter by category ID. |
brandId | string | Filter by brand ID. |
status | enum | ACTIVE | DRAFT | ARCHIVED (default ACTIVE). |
featured | boolean | Filter to featured products only. |
Example Response
{
"success": true,
"items": [
{
"id": "prd_abc123",
"name": "Classic White T-Shirt",
"slug": "classic-white-t-shirt",
"price": 2999,
"compareAtPrice": 3999,
"images": ["https://cdn.shopvo.eu/img/ts-wht.jpg"]
}
],
"pagination": { "page": 1, "limit": 20, "total": 84, "totalPages": 5 }
}/api/v1/{siteId}/products/{productId}read:catalogReturns a single product by ID (cuid) or slug, including all variants.
Query Parameters
| Name | Type | Description |
|---|---|---|
productId* | string | Product ID (cuid) or slug. |
Example Response
{
"success": true,
"data": {
"id": "prd_abc123",
"name": "Classic White T-Shirt",
"slug": "classic-white-t-shirt",
"price": 2999,
"variants": [
{ "id": "var_s", "name": "Small / White", "inventoryStatus": "IN_STOCK", "isDefault": true }
]
}
}Product Variants
/api/v1/{siteId}/products/{productId}/variantsread:catalogReturns all variants for a product ordered with the default variant first.
Query Parameters
| Name | Type | Description |
|---|---|---|
productId* | string | Parent product ID. |
Example Response
{
"success": true,
"data": [
{
"id": "var_s",
"name": "Small / White",
"sku": "TS-WHT-S",
"price": null,
"inventoryStatus": "IN_STOCK",
"inventoryCount": 42,
"options": { "Size": "Small", "Color": "White" },
"isDefault": true
}
]
}/api/v1/{siteId}/products/{productId}/variantswrite:catalogCreate a new variant. Setting isDefault: true automatically demotes the previous default variant. inventoryStatus is derived from inventoryCount when omitted.
Request Body
{
"name": "Large / Black",
"sku": "TS-BLK-L",
"inventoryCount": 10,
"options": { "Size": "Large", "Color": "Black" },
"isDefault": false
}Example Response
{
"success": true,
"data": { "id": "var_l", "name": "Large / Black", "inventoryStatus": "IN_STOCK", ... }
}/api/v1/{siteId}/products/{productId}/variants/{variantId}write:catalogUpdate any fields on an existing variant. All fields are optional. Setting isDefault: true atomically promotes this variant and demotes the previous default.
Request Body
{
"inventoryCount": 0,
"inventoryStatus": "OUT_OF_STOCK"
}Example Response
{ "success": true, "data": { "id": "var_s", "inventoryStatus": "OUT_OF_STOCK", ... } }/api/v1/{siteId}/products/{productId}/variants/{variantId}write:catalogPermanently delete a variant. If the deleted variant was the default, the next oldest variant is automatically promoted.
Example Response
{ "success": true, "deleted": true }Categories
/api/v1/{siteId}/categoriesread:catalogReturns all categories as a flat list, or filters to a single category by slug.
Query Parameters
| Name | Type | Description |
|---|---|---|
slug | string | Return a single category matching this slug. |
parentId | string | Filter to children of this category ID. |
Example Response
{ "success": true, "data": [{ "id": "cat_1", "name": "Apparel", "slug": "apparel" }] }Brands
/api/v1/{siteId}/brandsread:catalogReturns all brands, or filters to a single brand by slug.
Query Parameters
| Name | Type | Description |
|---|---|---|
slug | string | Return a single brand matching this slug. |
Example Response
{ "success": true, "data": [{ "id": "br_1", "name": "Essentials", "slug": "essentials" }] }Product Reviews
/api/v1/{siteId}/products/{productId}/reviewsread:catalogReturns paginated reviews for a product. Defaults to PUBLISHED status.
Query Parameters
| Name | Type | Description |
|---|---|---|
productId* | string | Parent product ID. |
page | integer | Offset page number (default 1). |
limit | integer | Results per page (max 100, default 20). |
status | enum | PENDING | PUBLISHED | ARCHIVED | SPAM (default PUBLISHED). |
Example Response
{
"success": true,
"items": [
{
"id": "rev_abc",
"rating": 5,
"title": "Great quality!",
"content": "Wore this for a week and it still looks brand new.",
"reviewerName": "Jane Smith",
"status": "PUBLISHED",
"isVerifiedPurchase": true,
"createdAt": "2026-01-10T12:00:00Z"
}
],
"pagination": { "page": 1, "limit": 20, "total": 14 }
}/api/v1/{siteId}/products/{productId}/reviewswrite:catalogSubmit a new review. Starts with PENDING status unless auto-approve is enabled on the site.
Parameters
| Name | Type | Description |
|---|---|---|
productId* | string | Parent product ID. |
Request Body
{
"rating": 5,
"title": "Great quality!",
"content": "Wore this for a week and it still looks brand new.",
"reviewerName": "Jane Smith",
"reviewerEmail": "jane@example.com"
}Example Response
{ "success": true, "data": { "id": "rev_abc", "status": "PENDING" } }Orders
/api/v1/{siteId}/ordersread:ordersReturns a paginated list of orders. Supports cursor and offset pagination.
Query Parameters
| Name | Type | Description |
|---|---|---|
page / cursor | integer / string | Offset page number or cursor for cursor-based pagination. |
limit | integer | Results per page (max 100, default 20). |
status | enum | PENDING | CONFIRMED | PROCESSING | SHIPPED | DELIVERED | CANCELLED | REFUNDED |
paymentStatus | enum | PENDING | PAID | FAILED | REFUNDED | PARTIALLY_REFUNDED |
customerId | string | Filter by customer ID. |
from / to | date-time | ISO 8601 timestamps to filter by creation date range. |
Example Response
{ "success": true, "items": [...], "pagination": { "page": 1, "limit": 20, "total": 42 } }/api/v1/{siteId}/orders/{orderId}read:ordersReturns a single order by ID (cuid) or order number (e.g. ORD-123456).
Query Parameters
| Name | Type | Description |
|---|---|---|
orderId* | string | Order cuid or order number like ORD-123456. |
Example Response
{
"success": true,
"data": {
"id": "order_xyz",
"number": "ORD-1042",
"status": "CONFIRMED",
"paymentStatus": "PAID",
"total": 7137,
"items": [...]
}
}Customers
/api/v1/{siteId}/customersread:customersReturns a paginated list of customers. Supports cursor pagination and search.
Query Parameters
| Name | Type | Description |
|---|---|---|
page / cursor | integer / string | Offset page or cursor for cursor-based pagination. |
search | string | Search by email, first name, or last name. |
group | string | Filter by customer group ID. |
Example Response
{ "success": true, "items": [...], "pagination": { "page": 1, "total": 320 } }/api/v1/{siteId}/customers/{customerId}read:customersReturns a single customer by ID.
Query Parameters
| Name | Type | Description |
|---|---|---|
customerId* | string | Customer cuid. |
Example Response
{
"success": true,
"data": {
"id": "cust_xyz",
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"totalOrders": 4,
"totalSpent": 28400
}
}/api/v1/{siteId}/customers/{customerId}write:customersUpdate a customer's profile fields.
Request Body
{ "firstName": "Jane", "lastName": "Smith", "phone": "+30 210 1234567" }Example Response
{ "success": true, "data": { ... } }Inventory
/api/v1/{siteId}/inventoryread:inventoryReturns stock levels for all products and variants. Optionally filter by SKU.
Query Parameters
| Name | Type | Description |
|---|---|---|
page | integer | Offset page number (default 1). |
limit | integer | Results per page (max 100, default 20). |
sku | string | Filter to a specific SKU. |
Example Response
{
"success": true,
"items": [
{
"id": "var_s",
"name": "Classic White T-Shirt — Small / White",
"sku": "TS-WHT-S",
"inventoryCount": 42,
"inventoryStatus": "IN_STOCK",
"variantId": "var_s"
}
],
"pagination": { "page": 1, "limit": 20, "total": 120 }
}/api/v1/{siteId}/inventory/{productId}write:inventoryAdjust the stock level of a product or variant using set, increment, or decrement operations.
Parameters
| Name | Type | Description |
|---|---|---|
productId* | string | Product ID (cuid). |
Request Body
{
"operation": "increment", // set | increment | decrement
"quantity": 10,
"variantId": "var_s", // optional — targets a specific variant
"reason": "Restock from supplier PO-1234" // optional audit note
}Example Response
{ "success": true, "data": { "id": "var_s", "inventoryCount": 52, "inventoryStatus": "IN_STOCK" } }Promotions
/api/v1/{siteId}/promotionsread:promotionsReturns a paginated list of discount promotions and coupon codes.
Query Parameters
| Name | Type | Description |
|---|---|---|
page | integer | Offset page number (default 1). |
limit | integer | Results per page (max 100, default 20). |
status | enum | ACTIVE | SCHEDULED | EXPIRED | EXHAUSTED |
Example Response
{
"success": true,
"items": [
{
"id": "promo_abc",
"code": "WELCOME20",
"type": "PERCENTAGE",
"value": 20,
"status": "ACTIVE",
"usedCount": 14,
"expiresAt": null
}
],
"pagination": { "page": 1, "limit": 20, "total": 8 }
}/api/v1/{siteId}/promotionswrite:promotionsCreate a new discount promotion or coupon code.
Request Body
{
"code": "WELCOME20",
"type": "PERCENTAGE", // PERCENTAGE | FIXED_AMOUNT | FREE_SHIPPING
"value": 20,
"minOrderAmount": 1000, // optional — minimum cart subtotal in cents
"maxUses": 500, // optional — null = unlimited
"expiresAt": "2026-12-31T23:59:59Z" // optional
}Example Response
{ "success": true, "data": { "id": "promo_abc", "code": "WELCOME20", "status": "ACTIVE" } }/api/v1/{siteId}/promotions/validateValidate a coupon code against a cart total and customer email. This endpoint does not require authentication — safe to call from storefront checkout flows.
Request Body
{
"code": "WELCOME20",
"cartTotal": 5000, // cart subtotal in cents
"email": "jane@example.com" // optional — used for per-customer limit checks
}Example Response
{
"success": true,
"valid": true,
"discount": 1000,
"promotion": { "id": "promo_abc", "code": "WELCOME20", "type": "PERCENTAGE", "value": 20 }
}Gift Cards
Gift cards are redeemable codes with a monetary balance. They can be issued manually from the admin, via automation triggers, or programmatically through the API. Partial redemptions are supported — the remaining balance stays on the card.
/api/v1/{siteId}/gift-cardsread:gift-cardsReturns a paginated list of gift cards. Filter by status, customer, or code.
Query Parameters
| Name | Type | Description |
|---|---|---|
status | enum | ACTIVE | REDEEMED | EXPIRED | VOID |
customerId | string | Filter cards issued to a specific customer. |
code | string | Exact gift card code to look up. |
Example Response
{
"success": true,
"items": [
{
"id": "gc_abc",
"code": "GIFT-XXXX-YYYY",
"initialBalance": 5000,
"remainingBalance": 2500,
"status": "ACTIVE",
"expiresAt": null,
"issuedAt": "2025-01-10T12:00:00Z"
}
],
"pagination": { "page": 1, "total": 18 }
}/api/v1/{siteId}/gift-cardswrite:gift-cardsIssue a new gift card. The code is auto-generated unless provided.
Request Body
{
"initialBalance": 5000,
"customerId": "cust_xyz", // optional — attach to a customer
"expiresAt": "2026-12-31T23:59:59Z", // optional
"note": "Birthday gift"
}Example Response
{ "success": true, "data": { "id": "gc_abc", "code": "GIFT-XXXX-YYYY", "remainingBalance": 5000 } }/api/v1/{siteId}/gift-cards/{giftCardId}/redeemwrite:gift-cardsApply a redemption against the card balance. Partial redemptions reduce remainingBalance. Returns an error if the card is void or expired.
Request Body
{ "amount": 2500, "orderId": "order_xyz" }Example Response
{ "success": true, "data": { "id": "gc_abc", "remainingBalance": 2500, "status": "ACTIVE" } }/api/v1/{siteId}/gift-cards/{giftCardId}write:gift-cardsVoid a gift card or update its expiry. Once voided the card cannot be reactivated.
Request Body
{ "status": "VOID" }Example Response
{ "success": true, "data": { "id": "gc_abc", "status": "VOID" } }Store Credit
Store credit is a per-customer balance attached to a specific store. It can be issued manually, awarded via automation, or granted as part of a refund. Customers can apply their available balance at checkout.
/api/v1/{siteId}/store-credit/{customerId}read:store-creditReturns the store credit account for a customer, including the current balance and a list of recent transactions.
Query Parameters
| Name | Type | Description |
|---|---|---|
customerId* | string | Customer cuid. |
Example Response
{
"success": true,
"data": {
"customerId": "cust_xyz",
"balance": 3500,
"transactions": [
{ "id": "sct_1", "type": "CREDIT", "amount": 5000, "note": "Welcome bonus", "createdAt": "..." },
{ "id": "sct_2", "type": "DEBIT", "amount": 1500, "orderId": "order_xyz", "createdAt": "..." }
]
}
}/api/v1/{siteId}/store-credit/{customerId}write:store-creditIssue (type: CREDIT) or deduct (type: DEBIT) store credit. Debits fail if the resulting balance would go negative.
Request Body
{
"type": "CREDIT",
"amount": 2000,
"note": "Refund for order ORD-1042",
"orderId": "order_xyz" // optional
}Example Response
{ "success": true, "data": { "balance": 5500, "transaction": { "id": "sct_3", ... } } }Loyalty Program
The loyalty program lets customers earn points on purchases and redeem them for discounts or store credit. The program rules (points per currency unit, redemption rate, tiers) are configured in Admin → Customers → Loyalty Program.
/api/v1/{siteId}/loyalty/{customerId}read:loyaltyReturns the loyalty account for a customer: current points, tier, and recent transactions.
Query Parameters
| Name | Type | Description |
|---|---|---|
customerId* | string | Customer cuid. |
Example Response
{
"success": true,
"data": {
"customerId": "cust_xyz",
"points": 1250,
"tier": "GOLD",
"transactions": [
{ "id": "lt_1", "type": "EARN", "points": 300, "orderId": "order_xyz", "createdAt": "..." },
{ "id": "lt_2", "type": "REDEEM", "points": -100, "note": "Discount applied", "createdAt": "..." }
]
}
}/api/v1/{siteId}/loyalty/{customerId}/awardwrite:loyaltyManually award loyalty points to a customer. Useful for referrals, social sharing, or support actions.
Request Body
{ "points": 500, "note": "Referral bonus" }Example Response
{ "success": true, "data": { "points": 1750, "transaction": { "id": "lt_3", ... } } }/api/v1/{siteId}/loyalty/{customerId}/redeemwrite:loyaltyRedeem loyalty points at checkout. Points are converted to a discount amount according to the program's redemption rate.
Request Body
{ "points": 100, "orderId": "order_xyz" }Example Response
{ "success": true, "data": { "discountAmount": 100, "pointsRemaining": 1150 } }B2B / Wholesale
B2B accounts give wholesale customers access to custom pricing tiers, net payment terms, and company-level order management. Each B2B account is linked to one or more customer contacts and optionally to a price list.
403 Forbidden./api/v1/{siteId}/b2b/accountsread:b2bReturns a paginated list of B2B accounts.
Query Parameters
| Name | Type | Description |
|---|---|---|
page / cursor | integer / string | Offset page or cursor for cursor-based pagination. |
search | string | Search by company name or contact email. |
status | enum | ACTIVE | SUSPENDED | PENDING |
Example Response
{
"success": true,
"items": [
{
"id": "b2b_abc",
"companyName": "Acme Corp",
"taxId": "EL123456789",
"status": "ACTIVE",
"priceListId": "pl_xyz",
"paymentTerms": "NET_30",
"contacts": [{ "id": "cust_1", "email": "buyer@acme.com", "isPrimary": true }]
}
],
"pagination": { "page": 1, "total": 12 }
}/api/v1/{siteId}/b2b/accountswrite:b2bCreate a new B2B account and optionally attach existing customers as contacts.
Request Body
{
"companyName": "Acme Corp",
"taxId": "EL123456789",
"paymentTerms": "NET_30",
"priceListId": "pl_xyz",
"contactCustomerIds": ["cust_1"]
}Example Response
{ "success": true, "data": { "id": "b2b_abc", "companyName": "Acme Corp", "status": "ACTIVE" } }/api/v1/{siteId}/b2b/accounts/{accountId}write:b2bUpdate a B2B account's status, payment terms, or price list.
Request Body
{ "status": "SUSPENDED", "paymentTerms": "NET_60" }Example Response
{ "success": true, "data": { "id": "b2b_abc", "status": "SUSPENDED" } }Cart
/api/v1/{siteId}/cartread:cartRetrieve an existing cart by cartId.
Query Parameters
| Name | Type | Description |
|---|---|---|
cartId* | string | Cart cuid. |
Example Response
{ "success": true, "data": { "id": "cart_xyz", "items": [...], "total": 5998 } }/api/v1/{siteId}/cartwrite:cartCreate a new cart. Associate with a registered customer or a guest session.
Request Body
{ "sessionId": "sess_abc123", "currency": "EUR" }Example Response
{ "success": true, "data": { "id": "cart_xyz", "status": "ACTIVE" } }/api/v1/{siteId}/cartwrite:cartAdd (action: add), update quantity (action: update), or remove (action: remove) an item. Pass cartId as a query parameter.
Parameters
| Name | Type | Description |
|---|---|---|
cartId* | string | Cart cuid. |
Request Body
{
"action": "add",
"item": { "productId": "prd_abc", "variantId": "var_s", "name": "T-Shirt S", "quantity": 2, "price": 2999 }
}Example Response
{ "success": true, "data": { "id": "cart_xyz", "items": [...], "total": 5998 } }/api/v1/{siteId}/cartwrite:cartPermanently delete a cart and all its items.
Parameters
| Name | Type | Description |
|---|---|---|
cartId* | string | Cart cuid. |
Example Response
{ "success": true, "deleted": true }Checkout
/api/v1/{siteId}/checkoutread:checkoutRetrieve a checkout session by ID.
Query Parameters
| Name | Type | Description |
|---|---|---|
sessionId* | string | Checkout session cuid. |
Example Response
{ "success": true, "data": { "id": "cs_abc", "state": "shipping", ... } }/api/v1/{siteId}/checkoutwrite:checkoutInitialise a new checkout session from a cart ID.
Request Body
{ "cartId": "cart_xyz" }Example Response
{ "success": true, "data": { "id": "cs_abc", "state": "init" } }/api/v1/{siteId}/checkoutwrite:checkoutAdvance the checkout to the next state. Valid states (in order): init → contact → shipping → payment → review → complete.
Request Body
{ "sessionId": "cs_abc", "state": "shipping" }Example Response
{ "success": true, "data": { "id": "cs_abc", "state": "shipping" } }Pages
/api/v1/{siteId}/pagesread:contentReturns published pages with optional filtering by slug.
Query Parameters
| Name | Type | Description |
|---|---|---|
slug | string | Return a single page matching this slug. |
page | integer | Offset page number. |
limit | integer | Results per page. |
Example Response
{ "success": true, "data": [{ "id": "pg_1", "title": "About Us", "slug": "about" }] }Site Config
/api/v1/{siteId}/siteread:contentReturns site metadata, verified custom domains, locale, currency, and active theme design tokens.
Example Response
{
"success": true,
"data": {
"id": "site_abc",
"name": "My Store",
"locale": "en",
"currency": "EUR",
"primaryDomain": "mystore.eu",
"theme": { "--color-primary": "#6366f1" }
}
}Webhooks
Webhooks let Shopvo push real-time events to your server. Configure endpoints in Admin → Settings → Webhooks. Each subscription can be scoped to specific events and all payloads are HMAC-SHA256 signed (same scheme as above).
Available events
order.createdA customer completed checkout and an order was placed.
order.updatedOrder status changed (processing, shipped, delivered, etc.).
payment.confirmedPayment successfully captured.
payment.failedPayment attempt failed.
inventory.lowA product's stock dropped below its threshold.
cart.abandonedA cart was left inactive with items for 1 hour.
review.createdA customer submitted a product review.
Payload format
{
"event": "order.created",
"timestamp": "2026-04-21T12:00:00Z",
"siteId": "site_abc",
"data": {
"id": "order_xyz",
"number": "ORD-1042",
"total": 7137
}
}Verify the X-Shopvo-Signature header on every incoming webhook using the verification snippet in the Request Signing section.
Metafields
Metafields let you attach arbitrary key-value metadata to any resource — products, customers, orders, or the site itself. Values are stored as strings and typed via valueType (string, integer, json, boolean).
/api/v1/{siteId}/{resourceType}/{resourceId}/metafieldsmetafields:readReturns all metafields for a resource. Supported resourceType values: product, customer, order, site.
Query Parameters
| Name | Type | Description |
|---|---|---|
resourceType* | enum | product | customer | order | site |
resourceId* | string | ID of the target resource. |
Example Response
{
"success": true,
"data": [
{
"id": "mf_abc",
"namespace": "custom",
"key": "warranty_months",
"value": "24",
"valueType": "integer",
"createdAt": "2026-01-10T12:00:00Z"
}
]
}/api/v1/{siteId}/{resourceType}/{resourceId}/metafieldsmetafields:writeAdd a metafield to a resource. Each metafield is uniquely identified by namespace + key.
Parameters
| Name | Type | Description |
|---|---|---|
resourceType* | enum | product | customer | order | site |
resourceId* | string | ID of the target resource. |
Request Body
{
"namespace": "custom",
"key": "warranty_months",
"value": "24",
"valueType": "integer" // string | integer | json | boolean (default: string)
}Example Response
{ "success": true, "data": { "id": "mf_abc", "namespace": "custom", "key": "warranty_months", "value": "24" } }/api/v1/{siteId}/{resourceType}/{resourceId}/metafields/{metafieldId}metafields:writeUpdate the value or valueType of an existing metafield.
Request Body
{ "value": "36", "valueType": "integer" }Example Response
{ "success": true, "data": { "id": "mf_abc", "value": "36" } }/api/v1/{siteId}/{resourceType}/{resourceId}/metafields/{metafieldId}metafields:writePermanently delete a metafield.
Example Response
{ "success": true, "deleted": true }TypeScript SDK
The official @shopvo/sdk wraps the full v1 API with TypeScript types and built-in HMAC signing.
npm install @shopvo/sdk
import { createClient } from "@shopvo/sdk";
const client = createClient({
apiKey: "sk_live_…",
signingSecret: "sks_…", // optional, enables HMAC on all requests
siteId: "site_…",
baseUrl: "https://your-domain",
});
// Products
const { items } = await client.products.list({ limit: 20, expand: ["variants"] });
const product = await client.products.get("classic-white-t-shirt");
// Variants
const variants = await client.products.listVariants("prd_abc");
const variant = await client.products.createVariant("prd_abc", {
name: "Large / Black",
inventoryCount: 10,
options: { Size: "Large", Color: "Black" },
});
// Orders
const { items: orders } = await client.orders.list({ status: "CONFIRMED" });
const order = await client.orders.get("ORD-1042");
// Customers
const { items: customers } = await client.customers.list({ search: "jane" });Errors & Status Codes
All errors follow a consistent JSON shape:
{ "error": "Human-readable message" }| Status | Meaning |
|---|---|
400 | Bad Request — validation error; check the error message. |
401 | Unauthorized — missing, invalid, or expired API key; or invalid HMAC signature. |
403 | Forbidden — key does not have the required permission scope. |
404 | Not Found — resource does not exist or does not belong to this site. |
409 | Conflict — inventory conflict (e.g. OUT_OF_STOCK). |
429 | Too Many Requests — rate limit exceeded; check Retry-After. |
500 | Internal Server Error — unexpected error on our side. |
