Skip to main content
Shopvo
Start free trialSign in

14-day free trial · No credit card required

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

ℹ️All v1 API endpoints authenticate via Bearer API keys. Session cookies and NextAuth tokens are not used on these routes — they are designed exclusively for server-to-server and headless integrations.

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

PrefixEnvironmentUse
sk_live_…Production (live)Real orders and customers. Keep secret.
sk_test_…Test / sandboxDevelopment 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.

ℹ️Signing is optional but strongly recommended for server-to-server integrations. Requests with an invalid signature or a timestamp older than 5 minutes are automatically rejected with 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.

ScopeGrants access to
read:catalogList and get products, variants, categories, brands, reviews
write:catalogCreate, update, delete products and variants; submit reviews
read:contentPages, menus, site configuration
read:ordersOrder list and detail
read:customersCustomer list and detail
write:customersUpdate customer profiles
read:cartFetch cart
write:cartCreate, modify, delete cart
read:checkoutFetch checkout session
write:checkoutCreate and advance checkout
read:inventoryList stock levels
write:inventoryAdjust stock levels
read:promotionsList promotions and price rules
write:promotionsCreate and delete promotions
read:gift-cardsList and read gift cards
write:gift-cardsCreate, update, and void gift cards
read:store-creditRead customer store credit balances and transactions
write:store-creditIssue, adjust, and void store credit
read:loyaltyRead loyalty accounts and point transactions
write:loyaltyAward, deduct, and redeem loyalty points
read:b2bRead B2B accounts and pricing tiers
write:b2bCreate and manage B2B accounts and custom pricing
webhooks:readList webhook subscriptions
webhooks:writeCreate and delete webhook subscriptions
metafields:readRead metafields on any resource
metafields:writeCreate, update, and delete metafields
admin:*Full admin access — all scopes above

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

RouteDefault limit
All routesPlan-dependent (30–1 000 rpm)
/checkout10 rpm
/orders30 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

GET/api/v1/{siteId}/productsread:catalog

Returns a paginated list of products. Supports offset and cursor pagination, field selection, and resource expansion.

Query Parameters

NameTypeDescription
pageintegerPage number for offset pagination (default 1). Ignored when cursor is provided.
limitintegerResults per page (max 100, default 20).
cursorstringOpaque cursor from a previous response for cursor-based pagination.
fieldsstringComma-separated list of fields to include (sparse fieldset).
expand[]string[]Sub-resources to eager load: variants, brand, categories.
searchstringFull-text search across name, SKU, and description.
categoryIdstringFilter by category ID.
brandIdstringFilter by brand ID.
statusenumACTIVE | DRAFT | ARCHIVED (default ACTIVE).
featuredbooleanFilter 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 }
}
GET/api/v1/{siteId}/products/{productId}read:catalog

Returns a single product by ID (cuid) or slug, including all variants.

Query Parameters

NameTypeDescription
productId*stringProduct 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

GET/api/v1/{siteId}/products/{productId}/variantsread:catalog

Returns all variants for a product ordered with the default variant first.

Query Parameters

NameTypeDescription
productId*stringParent 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
 }
 ]
}
POST/api/v1/{siteId}/products/{productId}/variantswrite:catalog

Create 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", ... }
}
PATCH/api/v1/{siteId}/products/{productId}/variants/{variantId}write:catalog

Update 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", ... } }
DELETE/api/v1/{siteId}/products/{productId}/variants/{variantId}write:catalog

Permanently delete a variant. If the deleted variant was the default, the next oldest variant is automatically promoted.

Example Response

{ "success": true, "deleted": true }

Categories

GET/api/v1/{siteId}/categoriesread:catalog

Returns all categories as a flat list, or filters to a single category by slug.

Query Parameters

NameTypeDescription
slugstringReturn a single category matching this slug.
parentIdstringFilter to children of this category ID.

Example Response

{ "success": true, "data": [{ "id": "cat_1", "name": "Apparel", "slug": "apparel" }] }

Brands

GET/api/v1/{siteId}/brandsread:catalog

Returns all brands, or filters to a single brand by slug.

Query Parameters

NameTypeDescription
slugstringReturn a single brand matching this slug.

Example Response

{ "success": true, "data": [{ "id": "br_1", "name": "Essentials", "slug": "essentials" }] }

Product Reviews

GET/api/v1/{siteId}/products/{productId}/reviewsread:catalog

Returns paginated reviews for a product. Defaults to PUBLISHED status.

Query Parameters

NameTypeDescription
productId*stringParent product ID.
pageintegerOffset page number (default 1).
limitintegerResults per page (max 100, default 20).
statusenumPENDING | 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 }
}
POST/api/v1/{siteId}/products/{productId}/reviewswrite:catalog

Submit a new review. Starts with PENDING status unless auto-approve is enabled on the site.

Parameters

NameTypeDescription
productId*stringParent 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

ℹ️Orders endpoints are rate limited to 30 rpm.
GET/api/v1/{siteId}/ordersread:orders

Returns a paginated list of orders. Supports cursor and offset pagination.

Query Parameters

NameTypeDescription
page / cursorinteger / stringOffset page number or cursor for cursor-based pagination.
limitintegerResults per page (max 100, default 20).
statusenumPENDING | CONFIRMED | PROCESSING | SHIPPED | DELIVERED | CANCELLED | REFUNDED
paymentStatusenumPENDING | PAID | FAILED | REFUNDED | PARTIALLY_REFUNDED
customerIdstringFilter by customer ID.
from / todate-timeISO 8601 timestamps to filter by creation date range.

Example Response

{ "success": true, "items": [...], "pagination": { "page": 1, "limit": 20, "total": 42 } }
GET/api/v1/{siteId}/orders/{orderId}read:orders

Returns a single order by ID (cuid) or order number (e.g. ORD-123456).

Query Parameters

NameTypeDescription
orderId*stringOrder 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

GET/api/v1/{siteId}/customersread:customers

Returns a paginated list of customers. Supports cursor pagination and search.

Query Parameters

NameTypeDescription
page / cursorinteger / stringOffset page or cursor for cursor-based pagination.
searchstringSearch by email, first name, or last name.
groupstringFilter by customer group ID.

Example Response

{ "success": true, "items": [...], "pagination": { "page": 1, "total": 320 } }
GET/api/v1/{siteId}/customers/{customerId}read:customers

Returns a single customer by ID.

Query Parameters

NameTypeDescription
customerId*stringCustomer cuid.

Example Response

{
 "success": true,
 "data": {
 "id": "cust_xyz",
 "email": "jane@example.com",
 "firstName": "Jane",
 "lastName": "Smith",
 "totalOrders": 4,
 "totalSpent": 28400
 }
}
PATCH/api/v1/{siteId}/customers/{customerId}write:customers

Update a customer's profile fields.

Request Body

{ "firstName": "Jane", "lastName": "Smith", "phone": "+30 210 1234567" }

Example Response

{ "success": true, "data": { ... } }

Inventory

GET/api/v1/{siteId}/inventoryread:inventory

Returns stock levels for all products and variants. Optionally filter by SKU.

Query Parameters

NameTypeDescription
pageintegerOffset page number (default 1).
limitintegerResults per page (max 100, default 20).
skustringFilter 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 }
}
PUT/api/v1/{siteId}/inventory/{productId}write:inventory

Adjust the stock level of a product or variant using set, increment, or decrement operations.

Parameters

NameTypeDescription
productId*stringProduct 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

GET/api/v1/{siteId}/promotionsread:promotions

Returns a paginated list of discount promotions and coupon codes.

Query Parameters

NameTypeDescription
pageintegerOffset page number (default 1).
limitintegerResults per page (max 100, default 20).
statusenumACTIVE | 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 }
}
POST/api/v1/{siteId}/promotionswrite:promotions

Create 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" } }
POST/api/v1/{siteId}/promotions/validate

Validate 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.

GET/api/v1/{siteId}/gift-cardsread:gift-cards

Returns a paginated list of gift cards. Filter by status, customer, or code.

Query Parameters

NameTypeDescription
statusenumACTIVE | REDEEMED | EXPIRED | VOID
customerIdstringFilter cards issued to a specific customer.
codestringExact 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 }
}
POST/api/v1/{siteId}/gift-cardswrite:gift-cards

Issue 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 } }
POST/api/v1/{siteId}/gift-cards/{giftCardId}/redeemwrite:gift-cards

Apply 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" } }
PATCH/api/v1/{siteId}/gift-cards/{giftCardId}write:gift-cards

Void 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.

GET/api/v1/{siteId}/store-credit/{customerId}read:store-credit

Returns the store credit account for a customer, including the current balance and a list of recent transactions.

Query Parameters

NameTypeDescription
customerId*stringCustomer 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": "..." }
 ]
 }
}
POST/api/v1/{siteId}/store-credit/{customerId}write:store-credit

Issue (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.

GET/api/v1/{siteId}/loyalty/{customerId}read:loyalty

Returns the loyalty account for a customer: current points, tier, and recent transactions.

Query Parameters

NameTypeDescription
customerId*stringCustomer 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": "..." }
 ]
 }
}
POST/api/v1/{siteId}/loyalty/{customerId}/awardwrite:loyalty

Manually 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", ... } } }
POST/api/v1/{siteId}/loyalty/{customerId}/redeemwrite:loyalty

Redeem 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.

ℹ️B2B features are only available on Growth plan and above. Calling these endpoints on a store without B2B enabled returns 403 Forbidden.
GET/api/v1/{siteId}/b2b/accountsread:b2b

Returns a paginated list of B2B accounts.

Query Parameters

NameTypeDescription
page / cursorinteger / stringOffset page or cursor for cursor-based pagination.
searchstringSearch by company name or contact email.
statusenumACTIVE | 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 }
}
POST/api/v1/{siteId}/b2b/accountswrite:b2b

Create 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" } }
PATCH/api/v1/{siteId}/b2b/accounts/{accountId}write:b2b

Update 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

GET/api/v1/{siteId}/cartread:cart

Retrieve an existing cart by cartId.

Query Parameters

NameTypeDescription
cartId*stringCart cuid.

Example Response

{ "success": true, "data": { "id": "cart_xyz", "items": [...], "total": 5998 } }
POST/api/v1/{siteId}/cartwrite:cart

Create 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" } }
PATCH/api/v1/{siteId}/cartwrite:cart

Add (action: add), update quantity (action: update), or remove (action: remove) an item. Pass cartId as a query parameter.

Parameters

NameTypeDescription
cartId*stringCart 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 } }
DELETE/api/v1/{siteId}/cartwrite:cart

Permanently delete a cart and all its items.

Parameters

NameTypeDescription
cartId*stringCart cuid.

Example Response

{ "success": true, "deleted": true }

Checkout

ℹ️Checkout endpoints are rate limited to 10 rpm.
GET/api/v1/{siteId}/checkoutread:checkout

Retrieve a checkout session by ID.

Query Parameters

NameTypeDescription
sessionId*stringCheckout session cuid.

Example Response

{ "success": true, "data": { "id": "cs_abc", "state": "shipping", ... } }
POST/api/v1/{siteId}/checkoutwrite:checkout

Initialise a new checkout session from a cart ID.

Request Body

{ "cartId": "cart_xyz" }

Example Response

{ "success": true, "data": { "id": "cs_abc", "state": "init" } }
PATCH/api/v1/{siteId}/checkoutwrite:checkout

Advance 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

GET/api/v1/{siteId}/pagesread:content

Returns published pages with optional filtering by slug.

Query Parameters

NameTypeDescription
slugstringReturn a single page matching this slug.
pageintegerOffset page number.
limitintegerResults per page.

Example Response

{ "success": true, "data": [{ "id": "pg_1", "title": "About Us", "slug": "about" }] }

Site Config

GET/api/v1/{siteId}/siteread:content

Returns 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.created

A customer completed checkout and an order was placed.

order.updated

Order status changed (processing, shipped, delivered, etc.).

payment.confirmed

Payment successfully captured.

payment.failed

Payment attempt failed.

inventory.low

A product's stock dropped below its threshold.

cart.abandoned

A cart was left inactive with items for 1 hour.

review.created

A 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).

GET/api/v1/{siteId}/{resourceType}/{resourceId}/metafieldsmetafields:read

Returns all metafields for a resource. Supported resourceType values: product, customer, order, site.

Query Parameters

NameTypeDescription
resourceType*enumproduct | customer | order | site
resourceId*stringID 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"
 }
 ]
}
POST/api/v1/{siteId}/{resourceType}/{resourceId}/metafieldsmetafields:write

Add a metafield to a resource. Each metafield is uniquely identified by namespace + key.

Parameters

NameTypeDescription
resourceType*enumproduct | customer | order | site
resourceId*stringID 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" } }
PATCH/api/v1/{siteId}/{resourceType}/{resourceId}/metafields/{metafieldId}metafields:write

Update the value or valueType of an existing metafield.

Request Body

{ "value": "36", "valueType": "integer" }

Example Response

{ "success": true, "data": { "id": "mf_abc", "value": "36" } }
DELETE/api/v1/{siteId}/{resourceType}/{resourceId}/metafields/{metafieldId}metafields:write

Permanently 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" }
StatusMeaning
400Bad Request — validation error; check the error message.
401Unauthorized — missing, invalid, or expired API key; or invalid HMAC signature.
403Forbidden — key does not have the required permission scope.
404Not Found — resource does not exist or does not belong to this site.
409Conflict — inventory conflict (e.g. OUT_OF_STOCK).
429Too Many Requests — rate limit exceeded; check Retry-After.
500Internal Server Error — unexpected error on our side.