Skip to main content
The Admin API (/api/v1/admin/*) exposes complete store management: catalog, pricing, stock, orders, customers, content, marketing, reports, settings. It’s designed for backends, integrations (ERP/PIM/billing), and automations — including AI agents via MCP.

Authentication and permissions

Every call carries a Bearer token:
The token can be:
  • a staff user’s token: obtained with POST /auth/login and renewed with the rotating refresh (see Authentication). For staff accounts, reusing a superseded refresh token closes the session: don’t share it across processes.
  • an API token: the recommended credential for integrations and long-running agents. It’s used as a Bearer, with no login or refresh, has a chosen role and a mandatory expiry (from 1 to 365 days), and can be revoked instantly. An administrator generates it, if the feature is enabled for the store (GET /admin/api-tokens/status; otherwise 403 tenant_keys.admin_api_disabled).
Permissions are granular: catalog, sales, customers, marketing, content, reviews, reports, tax, shipping, system, users. The role of the user or token decides which ones it has (admin, manager, catalog_manager, sales_manager, marketing_manager, content_editor, customer_service, accountant): the full matrix is in GET /admin/roles. Without the area’s permission the response is 403 auth.permission_required. Every call is scoped to its own store (the tenant_id claim).
The Admin API is server-side. Never embed a staff token or an API token in a public client.

Rules that matter to integrators

Since the October 10, 2026 release the API enforces the rules below. Complete fields, bodies, and responses are in the API Reference.

Money: who can do what

On top of the area’s permission, some actions check the role:

Owner, staff users, and API tokens

  • Every store has one owner: an active administrator, recorded and stable, who doesn’t change when another account is promoted. GET /auth/me and the user list mark them with is_owner: true. Only Vellaro support can change the owner.
  • The owner is protected: password, role, deactivation, deletion, and 2FA reset on their account answer 403 user.owner_protected. On the other administrators, the same actions can only be done by the owner (403 user.owner_only). An action that would leave the store without active administrators gives 409 user.last_admin.
  • An API token doesn’t manage users or credentials: creating, editing, or deleting staff users, resetting their 2FA, generating or revoking API tokens and publishable keys answers 403 auth.api_token_forbidden. Reads stay allowed.

Orders, refunds, and returns

PATCH /admin/orders/{id} with "status": "refunded" on a paid or partially_refunded order performs a real refund of the remaining amount through the payment gateway: it moves money, like POST /admin/orders/{id}/refunds. If the gateway fails the response is 502 order.refund_gateway_error and the order stays as it was. On an order that was never collected the response is 409 order.refund_not_collected: the right status is cancelled.
  • Status transitions are validated: one that isn’t allowed gives 409 order.invalid_transition.
  • comment is an internal note by default; with comment_public: true the customer also sees it in their history.
  • A full refund or the cancellation of a paid order also gives back the part paid with a gift voucher. Stock is restocked only once.
  • Returns start from an order line: GET /admin/returns/order-context/{order_id} lists the lines with their returnable quantities, then POST /admin/returns. A variant outside the order or a quantity beyond what’s returnable gives 422 return.item_not_in_order / return.quantity_exceeds_order. Return reasons and statuses are a shared, read-only catalog (403 return.lookup_global on writes).
Explicit refund, also partial (amount omitted = the whole remaining amount):

Catalog and pricing

POST /admin/products saves all the product fields and creates inline variants in the same transaction. The SKU is unique within the store: a duplicate (even between two inline variants) gives 409 product.sku_taken and nothing is written. A duplicate slug gives 409 product.slug_taken; the slug of a deleted product is freed automatically.
  • Special prices and quantity discounts (/admin/products/{id}/specials, /discounts): the price must be greater than zero, otherwise 422 product.price_rule_not_positive. A special applies to everyone (customer_group_id: null) or to an active customer group, within its date window; among several specials the lowest priority wins, then the lowest price. A special can only lower the price, and cart and checkout apply it.
  • Sale on a variant: sale_price, sale_start, and sale_end go together (422 product.sale_window_invalid if the window is invalid); to remove it, send all three as null.

Coupons and gift vouchers

  • Coupons are validated on create and update: 422 coupon.percentage_out_of_range, coupon.negative_discount, coupon.dates_inverted; 409 coupon.code_taken. The list has a status filter (active, scheduled, expired, exhausted, inactive).
  • DELETE /admin/coupons/{id} really deletes a coupon that was never used (the code becomes free again) and deactivates one already used: read deleted and deactivated in the response.
  • Gift vouchers (/admin/vouchers, marketing permission): code, amount, and balance. The balance is spent at checkout (voucher_code), with amount_spent, redemption_count, and the history in GET /admin/vouchers/{id}/history. Here too DELETE only deletes a voucher that was never spent and deactivates it otherwise.

Subscriptions and recurring plans

A plan (POST /admin/recurrings, system permission) describes the renewal schedule:
At purchase the customer pays the product price; the first renewal at 9.90 comes after 14 days, then every month, for 12 renewals. trial_price is accepted but not applied. A subscription with every installment charged moves to completed, a terminal state (409 subscription.already_completed on cancel). A plan that has had subscriptions can’t be deleted (409 recurring.has_subscriptions): deactivate it with {"is_active": false}. If the store’s plan doesn’t include subscriptions, creation answers 403 plan.required.

Customers and affiliates

Anonymization (right to be forgotten), irreversible: personal data disappears, orders stay for tax obligations. The body repeats the customer’s current email as a confirmation.
Wrong confirmation email: 422 customer.anonymize_confirmation; customer already anonymized: 409 customer.anonymized.
  • Deactivating a customer (PATCH /admin/customers/{id} with is_active: false) closes all their sessions at once.
  • Affiliate commissions (/admin/affiliate-commissions): readable with customers; approving, marking as paid, or rejecting also requires reports. The summary (/summary) has one row per affiliate and currency: currencies are never added together.

Reports

  • An order counts as revenue if it’s collected (paid or partially_refunded, not refunded); the amount is the net, total minus refunded.
  • The main figures are in the store’s base currency (the currency field); revenue in other currencies is kept separately in other_currencies.
  • “Today”, days, and months follow the store’s time zone, returned in timezone.

Store settings

PUT /admin/settings (system permission) saves the keys of a group. A value outside the rules blocks the whole save with 422 settings.invalid_value (data.key tells you which).

Examples

Best practices

  • Errors: switch on error_code. A 403 plan.required means the feature isn’t included in the store’s plan: don’t retry.
  • Idempotency & bulk: for mass updates (prices, stock), process in batches and handle 409/422 responses per row.
  • Audit: every admin mutation is recorded in the store’s audit log.
  • Soft-delete: deleting a product sets deleted_at (it doesn’t destroy the order history); deactivating is a different thing (is_active).
  • Webhooks: to react to events without polling, use webhooks.
  • Versioning: the API lives under /api/v1 — backward-compatible changes; breaking changes will go into a new version.
The full reference for the hundreds of admin endpoints is in the API Reference (the Admin API group), generated from the live OpenAPI schema.