v1 · Stable

The storeVero API

A predictable, resource-oriented REST API plus a Shopify-shaped GraphQL Storefront API. JSON everywhere, Bearer-token auth, HMAC-signed webhooks, and generous rate limits. Everything you can do in the dashboard, you can do over the API.

BASE https://storevero.com/api/v1

Introduction

The storeVero API lets you build themes, apps and integrations on top of every storeVero store. It is organised around REST: predictable resource URLs, form- or JSON-encoded request bodies, JSON responses, and standard HTTP verbs and status codes. If you have called Stripe or Shopify, this will feel familiar.

JSON
Admin API answers {"ok": true, "data": …}.
Bearer auth
A token from /auth/login, sent as Authorization: Bearer.
Webhooks
Order, fulfilment, refund and draft-order events.

Base URL & versioning

All Admin API requests are made to the versioned base URL below. The Storefront API is public and scoped to a single store by its handle.

endpoints
Admin API      https://storevero.com/api/v1
Storefront API https://storevero.com/api/v1/ecommerce/storefront/{store-handle}
GraphQL        https://storevero.com/api/v1/storefront/graphql

The current version is v1. Breaking changes ship under a new version prefix; additive changes (new fields, new endpoints) can arrive within v1, so write tolerant parsers that ignore unknown fields.

Authentication

The Admin API authenticates with a Bearer token. Exchange your storeVero email + password at the auth endpoint below — there is no API-keys screen in the dashboard yet. Send the token in the Authorization header on every request. A token acts as that user in every store they belong to, has no scopes and does not expire, so keep it on your server and never in client-side code.

To pick the store, send X-Workspace-Id: <store id> (the workspaces list in the login response has the ids). Without it, the user's current store is used.

Get a token

POST/auth/login
curl -X POST https://storevero.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@store.com","password":"••••••••"}'
const res = await fetch('https://storevero.com/api/v1/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
  body: JSON.stringify({ email: 'you@store.com', password: '••••••••' })
});
const { data } = await res.json();   // { user, token, workspaces }
const token = data.token;
$res = Http::acceptJson()->post('https://storevero.com/api/v1/auth/login', [
  'email' => 'you@store.com',
  'password' => '••••••••',
]);
$token = $res->json('data.token');

Authenticate a request

curl https://storevero.com/api/v1/ecommerce/products \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "X-Workspace-Id: 12" \
  -H "Accept: application/json"
const res = await fetch('https://storevero.com/api/v1/ecommerce/products', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'X-Workspace-Id': '12',
    'Accept': 'application/json'
  }
});
$res = Http::withToken($token)
  ->withHeaders(['X-Workspace-Id' => 12])
  ->acceptJson()
  ->get('https://storevero.com/api/v1/ecommerce/products');
Storefront tokens are different: they belong to one store and are only for the public GraphQL Storefront API. Create one with POST /shopify/storefront-tokens {"title": "My app"} (admin token required) and pass its access_token in the X-Shopify-Storefront-Access-Token header. The storefront REST routes under your store handle need no token at all.

Rate limits

Every API route shares one limiter with a fixed one-minute window. It is the same on every plan. Responses carry X-RateLimit-Limit and X-RateLimit-Remaining, so you can slow down before you are throttled.

CallerRequests / minuteCounted per
With a Bearer token300user
Without a token (storefront routes)120IP address

Over the limit you receive 429 Too Many Requests with a Retry-After header (seconds until the window resets).

Errors

storeVero uses conventional HTTP status codes. 2xx means success, 4xx means the request was rejected (a missing field, a bad token), and 5xx means something went wrong on our side. Errors come back as {"ok": false, "error": "…", "message": "…"}; validation errors return 422 and add a field-keyed errors object.

422 Unprocessable Entity
{
  "ok": false,
  "error": "ValidationException",
  "message": "The name field is required. (and 1 more error)",
  "errors": {
    "name": ["The name field is required."],
    "price": ["The price must be a number."]
  }
}
200OK
201Created
401Unauthenticated — bad or missing token
403Forbidden — the record belongs to another store
404Resource not found
422Validation failed
429Rate limited
500Server error

Pagination

Big lists are paginated with page. Products and orders also take per_page (default 50, max 250); customers and reviews return 50 per page. The page is Laravel's paginator inside data — the records are at data.data, with current_page, last_page and total beside them. Short lists (collections, categories, brands) come back as a plain array in data.

GET /ecommerce/products?page=2&per_page=25
{
  "ok": true,
  "data": {
    "current_page": 2,
    "data": [ /* … 25 products … */ ],
    "per_page": 25,
    "total": 214,
    "last_page": 9,
    "from": 26, "to": 50
  }
}
Admin API

Products

Create, read, update and delete products, including variants, images, pricing and inventory. Products belong to the store tied to your token.

GET/ecommerce/products
POST/ecommerce/products
GET/ecommerce/products/{id}
PUT/ecommerce/products/{id}
DELETE/ecommerce/products/{id}

Create a product

curl -X POST https://storevero.com/api/v1/ecommerce/products \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Aroma Diffuser",
    "price": 1499,
    "sku": "AD-001",
    "stock": 50,
    "category_id": 3,
    "is_active": true
  }'
await fetch('https://storevero.com/api/v1/ecommerce/products', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Aroma Diffuser', price: 1499,
    sku: 'AD-001', stock: 50, category_id: 3, is_active: true
  })
});
Http::withToken($token)->post(
  'https://storevero.com/api/v1/ecommerce/products', [
    'name' => 'Aroma Diffuser', 'price' => 1499,
    'sku' => 'AD-001', 'stock' => 50, 'category_id' => 3, 'is_active' => true,
]);

Response

201 Created
{
  "ok": true,
  "data": {
    "id": 41, "workspace_id": 12, "name": "Aroma Diffuser", "slug": "aroma-diffuser",
    "price": "1499.00", "sku": "AD-001", "stock": 50,
    "category_id": 3, "is_active": true,
    "created_at": "2026-07-29T10:20:31.000000Z",
    "variants": []
  }
}
Admin API

Collections & categories

Organise your catalogue with collections, categories and brands. Creating any of them needs only a name; the lists come back unpaginated.

GET/ecommerce/collections
POST/ecommerce/collections
GET/ecommerce/categories
POST/ecommerce/categories
GET/ecommerce/brands
POST/ecommerce/brands
Admin API

Orders

Read and filter orders, record fulfilments and refunds, and export to CSV. No endpoint moves money: a refund is recorded on the order (amounts, restock, status); return the payment itself from your payment provider's dashboard.

GET/orders
GET/orders/{id}
PUT/orders/{id}
POST/ecommerce/fulfillments
POST/orders/{id}/fulfill
POST/shopify/refunds
POST/orders/{id}/refund
GET/orders/export

Fulfil an order

Creates a fulfilment, marks the order fulfilled once every line has shipped, and sends the fulfillments/create and orders/fulfilled webhooks.

curl -X POST https://storevero.com/api/v1/ecommerce/fulfillments \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": 1042,
    "line_items": [{ "order_item_id": 311, "quantity": 1 }],
    "carrier": "Delhivery",
    "tracking_number": "IN123456789",
    "tracking_url": "https://www.delhivery.com/track/package/IN123456789"
  }'
await fetch('https://storevero.com/api/v1/ecommerce/fulfillments', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    order_id: 1042,
    line_items: [{ order_item_id: 311, quantity: 1 }],
    carrier: 'Delhivery', tracking_number: 'IN123456789'
  })
});
Http::withToken($token)->post(
  'https://storevero.com/api/v1/ecommerce/fulfillments', [
    'order_id' => 1042,
    'line_items' => [['order_item_id' => 311, 'quantity' => 1]],
    'carrier' => 'Delhivery', 'tracking_number' => 'IN123456789',
]);
Admin API

Customers

Look up customers, their order history and lifetime value, and honour privacy requests with a one-call GDPR export.

GET/customers
GET/customers/{id}
PUT/customers/{id}
GET/customers/{id}/orders
POST/customers/{id}/gdpr-export
Admin API

Inventory

Adjust stock with an auditable movement. You send a delta (positive or negative) and a reason; storeVero records the movement and updates the on-hand balance.

POST/ecommerce/inventory/adjust
curl -X POST https://storevero.com/api/v1/ecommerce/inventory/adjust \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "product_id": 41, "quantity": -3, "reason": "sale" }'
await fetch('https://storevero.com/api/v1/ecommerce/inventory/adjust', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ product_id: 41, quantity: -3, reason: 'sale' })
});
Http::withToken($token)->post(
  'https://storevero.com/api/v1/ecommerce/inventory/adjust', [
    'product_id' => 41, 'quantity' => -3, 'reason' => 'sale',
]);

reason is one of purchase, sale, return, adjustment or transfer.

Admin API

Reviews

Moderate product reviews. Reviews submitted on the storefront arrive unapproved (see the Storefront API); approve one with {"is_approved": true}, answer with {"reply": "…"}, or remove it.

GET/ecommerce/reviews
PUT/ecommerce/reviews/{id}
DELETE/ecommerce/reviews/{id}
Admin API

Bulk operations

Apply one action to many records in a single call. Send the ids, an action and, where the action needs one, a value; the response says how many records changed ({"affected": 12}), not a per-row result. To create products in bulk, use the import endpoint.

POST/bulk/products
POST/bulk/orders
POST/bulk/customers
POST/ecommerce/products/bulk-import
EndpointActions
/bulk/productsset_price, adjust_price_percent, activate, deactivate, add_tags, remove_tags, set_category, delete
/bulk/ordersset_status, mark_paid, archive, delete
/bulk/customersadd_tags, set_group
curl -X POST https://storevero.com/api/v1/bulk/products \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "product_ids": [41, 42, 43], "action": "adjust_price_percent", "value": -10 }'
Storefront API

Storefront REST

Public, store-scoped endpoints for building custom storefronts, mobile apps and headless experiences. Every route is prefixed with the store handle. No admin token required.

GET/…/{store}/products
GET/…/{store}/products/{slug}
GET/…/{store}/categories
GET/…/{store}/search?q=
GET/…/{store}/autocomplete?q=
GET/…/{store}/delivery-options?zip=
GET/…/{store}/reviews/{product id}
POST/…/{store}/reviews
POST/…/{store}/orders

Place an order

Required: email, first_name, address1 and items (each with product_id, quantity and, for products with options, variant_id). Address fields are flat. payment_method defaults to cod; a method the store has not set up is refused with 422 and the list of methods it can take.

curl -X POST https://storevero.com/api/v1/ecommerce/storefront/demo/orders \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "buyer@example.com",
    "first_name": "Asha", "last_name": "Kumar", "phone": "9800000000",
    "address1": "12 MG Road", "city": "Pune", "state": "Maharashtra", "zip": "411001", "country": "IN",
    "items": [{ "product_id": 41, "quantity": 2 }],
    "payment_method": "cod"
  }'
await fetch('https://storevero.com/api/v1/ecommerce/storefront/demo/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
  body: JSON.stringify({
    email: 'buyer@example.com',
    first_name: 'Asha', last_name: 'Kumar', phone: '9800000000',
    address1: '12 MG Road', city: 'Pune', state: 'Maharashtra', zip: '411001', country: 'IN',
    items: [{ product_id: 41, quantity: 2 }],
    payment_method: 'cod'
  })
});
Http::acceptJson()->post('https://storevero.com/api/v1/ecommerce/storefront/demo/orders', [
  'email' => 'buyer@example.com',
  'first_name' => 'Asha', 'last_name' => 'Kumar', 'phone' => '9800000000',
  'address1' => '12 MG Road', 'city' => 'Pune', 'state' => 'Maharashtra', 'zip' => '411001', 'country' => 'IN',
  'items' => [['product_id' => 41, 'quantity' => 2]],
  'payment_method' => 'cod',
]);
Storefront API

Storefront GraphQL

A Shopify-shaped GraphQL Storefront API — edges, nodes and cart mutations — so tools and snippets from the Shopify ecosystem port over with minimal changes. Authenticate with a storefront access token.

POST/storefront/graphql
query ProductByHandle($handle: String!) {
  productByHandle(handle: $handle) {
    id
    title
    handle
    priceRange { minVariantPrice { amount currencyCode } }
    images(first: 3) { edges { node { url altText } } }
  }
}
curl -X POST https://storevero.com/api/v1/storefront/graphql \
  -H "X-Shopify-Storefront-Access-Token: YOUR_STOREFRONT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "query": "{ productByHandle(handle: \"aroma-diffuser\") { title } }" }'
mutation {
  cartCreate(input: { lines: [{ merchandiseId: "41", quantity: 2 }] }) {
    cart { id checkoutUrl
      lines(first: 10) { edges { node { quantity } } }
    }
  }
}
Automation

Webhooks

Add an endpoint in Settings → Webhooks (or POST /shopify/webhooks with topic, address and secret) and storeVero will POST a JSON payload to it when the event happens. When the webhook has a secret, every delivery carries an X-StoreVero-Hmac-Sha256 header — the base64 HMAC-SHA256 of the raw request body. Verify it before trusting the body. Webhooks saved without a secret are not signed.

Events you can subscribe to

orders/createOrder created
orders/paidOrder paid
orders/updatedOrder edited
orders/fulfilledOrder fulfilled
orders/refundedReturn requested
fulfillments/createFulfilment created
fulfillments/updateFulfilment updated
refunds/createRefund created
draft_orders/createDraft order created
draft_orders/updateDraft order updated
draft_orders/completedDraft order completed

What a delivery looks like

POST https://your-endpoint
Content-Type: application/json
X-StoreVero-Topic: orders/create
X-StoreVero-Event-Id: 5f0c1a9e-…
X-StoreVero-Hmac-Sha256: 3q2+7w…=        (only when a secret is set)

{
  "id": "5f0c1a9e-…",
  "topic": "orders/create",
  "created_at": "2026-09-15T10:20:31+00:00",
  "workspace": 12,
  "data": { /* the order, fulfilment, refund or draft order */ }
}

Each event is sent once, with a 10-second timeout; failed deliveries are not retried automatically. Every attempt, with the HTTP status your endpoint returned, is listed under Logs in Settings → Webhooks, and Send test posts a sample delivery to one endpoint.

Verify a signature

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_STOREVERO_HMAC_SHA256'] ?? '';
$expected = base64_encode(hash_hmac('sha256', $payload, $webhookSecret, true));
if (! hash_equals($expected, $signature)) {
  http_response_code(401); exit;
}
// ✓ verified — parse and handle
$event = json_decode($payload, true);
import crypto from 'crypto';
// rawBody: the exact bytes received (not re-serialised JSON)
function verify(rawBody, signature, secret) {
  const expected = Buffer.from(crypto.createHmac('sha256', secret)
    .update(rawBody).digest('base64'));
  const given = Buffer.from(signature || '');
  return given.length === expected.length && crypto.timingSafeEqual(expected, given);
}
Automation

SDKs & libraries

There are no official SDKs or Postman collection yet. The API is plain HTTP + JSON, so any HTTP client works — every cURL, JavaScript (fetch) and PHP (Laravel Http) sample on this page is a complete request you can copy.

Automation

Changelog

2026-07-29
Reviews API
Public storefront reviews endpoints (submit + list with average) and admin moderation.
2026-07-20
Analytics reports
New GET /dashboard/reports with real daily sales, channel split and top products.
2026-06-30
GraphQL Storefront
productByHandle, cartCreate and cartLinesAdd reach general availability.
2026-06-01
Bulk operations
POST /bulk/* endpoints for products, orders and customers.

Ready to build?

Create a free store, generate an API key, and make your first call in minutes.

Get your API key
Chat with us