API ReferenceResource locations

Resource locations

Where to find every resource — `/v1/*` vs `/stores/{storeId}/*`, with the API key scope required for each.

The Brainerce API spreads across three URL shapes. All accept an API key (Authorization: Bearer brainerce_*); they differ in stability guarantee.

NamespaceStabilityPurpose
/api/v1/*Stable — versioned. Breaking changes ship under /v2/.The external-integrator surface. Products, orders, customers, cart, checkout, media, team, regions, tax.
/api/stores/{storeId}/*Live — additive changes, no version.Store-scoped admin resources: webhooks, API keys, sales channels, modifier groups, team, store settings.
/api/{resource}Live — additive changes, no version.Top-level dashboard resources that were never nested under a store: analytics, bundle offers, order bumps, discount rules, backup, dashboard media, bulk product operations. The store is resolved from the credential or a storeId query param.

All three use the same auth (API key + scopes), the same rate limits, and the same error format. Only /api/v1/* carries the versioning guarantee.

When in doubt: use /v1/*. It is the only surface with a stability promise, and it now covers most of what integrations need. Reach for the other two only for the rows below that have no /v1/* equivalent.

Full resource map

ResourceNamespaceRequired scope (read)Required scope (write)
Products/v1/productsproducts:readproducts:write
Product variants/v1/products/{id}/variantsproducts:readproducts:write
Product inventory/v1/products/{id}/inventoryinventory:readinventory:write
Media library/v1/media, /v1/media/{id}media:readmedia:write
Orders/v1/ordersorders:readorders:write
CustomersPOST /v1/customers, GET /v1/customers/{id}, GET /v1/customers/by-email. There is no list route: you cannot enumerate customers over the API/v1/customers/…customers:readcustomers:write
Customer addresses/v1/customers/{id}/addressescustomers:readcustomers:write
Customer auth (login / register / reset)/v1/customers/login, /v1/customers/register, /v1/customers/forgot-password, /v1/customers/reset-passwordpublic
Cart/v1/cart, /v1/cart/{cartId}session-scopedsession-scoped
Checkout/v1/checkoutsession-scopedsession-scoped
Coupons/v1/couponscoupons:readcoupons:write
Gift card redemption — apply / remove a card on a checkout, and the balance check. Issuing a card is a separate, permission-gated merchant operation and is not on /v1/v1/checkout/{id}/gift-card, /v1/checkout/{id}/gift-card/{tenderId}, /v1/gift-cards/balancecheckout:writecheckout:write
Categories/v1/categoriestaxonomy:readtaxonomy:write
Brands/v1/brandstaxonomy:readtaxonomy:write
Tags/v1/tagstaxonomy:readtaxonomy:write
Attributes/v1/attributestaxonomy:readtaxonomy:write
Metafield definitions/v1/metafield-definitionsmetafields:readmetafields:write
Shipping zones / rates/v1/shipping/zonesshipping:readshipping:write
Tax rates/v1/tax/ratestax:readtax:write
Email templates / settings/v1/email/templates, /v1/email/settingsemail:reademail:write
Team / invitations/v1/team/members, /v1/team/invitationsteam:readteam:write
OAuth providers/v1/oauth-providersoauth:readoauth:write
Sync (push/pull to platforms)/v1/syncreturns 501 NOT_IMPLEMENTED. Use the per-resource routes: POST /v1/coupons/{id}/sync, POST /v1/categories/{id}/publish-sales-channelsync:write
Metafield conflicts (sync resolution)/v1/metafield-conflicts, .../{id}/resolve, .../{id}/ignoremetafields:readmetafields:write
Regions (multi-region pricing / providers)/v1/regions, /v1/regions/{regionId}/prices, .../countries, .../payment-providersregions:readregions:write
Tax classes/v1/tax-classes, .../{id}/assign, .../{id}/set-defaulttax-classes:readtax-classes:write
Store info/v1/storestore:read
Product reviews (storefront submit)/v1/products/{id}/reviewssession-scopedsession-scoped
Storefront Bot settings/v1/storefront-bot/settingsbot-settings:readbot-settings:write
Storefront Bot conversations/v1/storefront-bot/conversations, /v1/storefront-bot/conversations/{id}, .../{id}/summarizebot-conversations:readbot-conversations:write
------------
Webhook subscriptions/stores/{storeId}/webhook-subscriptionswebhooks:readwebhooks:write
Webhook deliveries (history only)/stores/{storeId}/webhook-subscriptions/{id}/deliverieswebhooks:read— (read-only)
Webhook test sendPOST /stores/{storeId}/webhook-subscriptions/{id}/testwebhooks:write
API keys (issue / list / revoke)/stores/{storeId}/api-keysapi-keys:readapi-keys:write
Sales channels (vibe-coded storefronts)/stores/{storeId}/sales-channelssettings:readsettings:write
Modifier groups/stores/{storeId}/modifier-groupsproducts:readproducts:write
OAuth providers (customer login)/stores/{storeId}/oauth-providerssettings:readsettings:write
Team (dashboard surface)/stores/{storeId}/teamteam:writeteam:write
Store settingsPATCH /stores/{storeId}/settingssettings:write
------------
Analytics/analyticsnot under /stores/{storeId}analytics:read
Bundle offers/bundle-offersproducts:readproducts:write
Order bumps/order-bumpsproducts:readproducts:write
Discount rules/discount-rulesdiscount-rules:readdiscount-rules:write
Backup (export / restore)/backupsettings:readsettings:write
Media (dashboard library)/media — the API-key surface is /v1/media above; this one is the dashboard'sproducts:readproducts:write
Bulk product operations (price/stock/status sweeps)/products/bulk/update-prices, .../update-stock, .../update-status, .../delete, …products:readproducts:write (taxonomy:write for category/brand sweeps)
Product reviews (admin moderation)/v1/reviews/{id}/hide, /v1/reviews/{id}/show, or the dashboard's /admin/products/{productId}/reviewsreviews:readreviews:write

The two surfaces use two different scope vocabularies. /v1/* routes declare their scope directly (regions:read, tax-classes:write, reviews:write, …). Everything below the divider is gated by a store permission that is then mapped to a scope — which is why store settings needs settings:write rather than a store-settings:* scope that does not exist, and why bundle offers and order bumps ride on products:* rather than a marketing:* scope that does not exist either. If a call 403s with INSUFFICIENT_SCOPE, the message names the scopes it would have accepted — add one of those.

Storefront vs admin in the same resource

Some resources (products, reviews, customer auth) have both:

  • Storefront read/api/vc/{salesChannelId}/products — public, no API key needed. Returns published items only.
  • Admin read/write/v1/products — requires products:read/products:write scope on an API key.

The storefront namespace is what create-brainerce-store wires by default. The admin namespace is what scripts and back-office systems use. Both can coexist in the same integration:

// public storefront — no key
const storefront = new BrainerceClient({ salesChannelId: 'vc_xxx' });
const { data: published } = await storefront.getProducts();

// admin — same store, and you can ask for drafts
const admin = new BrainerceClient({ apiKey: 'brainerce_xxx' });
const { data: drafts } = await admin.getProducts({ status: 'draft' });

The SDK method is getProducts(), not listProducts(), and the draft filter is status: 'active' | 'draft' — there is no includeDrafts option.

Picking the right scope

Scopes follow {resource}:read and {resource}:write consistently. When you create an API key at Settings → Authentication → API Keys, pick the minimum scope set the integration needs. Common combinations:

Use caseScopes
Read-only data warehouse / BI syncproducts:read, orders:read, customers:read, inventory:read, analytics:read
Inventory ERP syncproducts:read, inventory:read, inventory:write
Order routing to fulfillmentorders:read, orders:write, webhooks:read, webhooks:write
Customer data into CRMcustomers:read, webhooks:read, webhooks:write
Full catalog automationproducts:*, taxonomy:*, media:*, inventory:*

*:read is not a scope

Only three shapes match at authorization time:

  • an exact string — products:read
  • a resource wildcard — products:*, which matches products:read and products:write
  • the literal * — which cannot be minted; key creation rejects it

There is no *:<action> form. A key holding *:read matches nothing at all, and every call it makes fails with 403 INSUFFICIENT_SCOPE — the failure looks like a permissions bug rather than a typo, which is why it is worth naming here. Enumerate the scopes, or use {resource}:* per resource.

One further catch: the resource wildcard is honoured on /v1/* routes only. The store-scoped and top-level dashboard routes below the divider compare scope strings exactly, so products:* does not open /bundle-offers there — list products:read and products:write explicitly if the key needs both surfaces.