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.
| Namespace | Stability | Purpose |
|---|---|---|
/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
| Resource | Namespace | Required scope (read) | Required scope (write) |
|---|---|---|---|
| Products | /v1/products | products:read | products:write |
| Product variants | /v1/products/{id}/variants | products:read | products:write |
| Product inventory | /v1/products/{id}/inventory | inventory:read | inventory:write |
| Media library | /v1/media, /v1/media/{id} | media:read | media:write |
| Orders | /v1/orders | orders:read | orders:write |
Customers — POST /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:read | customers:write |
| Customer addresses | /v1/customers/{id}/addresses | customers:read | customers:write |
| Customer auth (login / register / reset) | /v1/customers/login, /v1/customers/register, /v1/customers/forgot-password, /v1/customers/reset-password | public | — |
| Cart | /v1/cart, /v1/cart/{cartId} | session-scoped | session-scoped |
| Checkout | /v1/checkout | session-scoped | session-scoped |
| Coupons | /v1/coupons | coupons:read | coupons: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/balance | checkout:write | checkout:write |
| Categories | /v1/categories | taxonomy:read | taxonomy:write |
| Brands | /v1/brands | taxonomy:read | taxonomy:write |
| Tags | /v1/tags | taxonomy:read | taxonomy:write |
| Attributes | /v1/attributes | taxonomy:read | taxonomy:write |
| Metafield definitions | /v1/metafield-definitions | metafields:read | metafields:write |
| Shipping zones / rates | /v1/shipping/zones | shipping:read | shipping:write |
| Tax rates | /v1/tax/rates | tax:read | tax:write |
| Email templates / settings | /v1/email/templates, /v1/email/settings | email:read | email:write |
| Team / invitations | /v1/team/members, /v1/team/invitations | team:read | team:write |
| OAuth providers | /v1/oauth-providers | oauth:read | oauth:write |
| Sync (push/pull to platforms) | /v1/sync501 NOT_IMPLEMENTED. Use the per-resource routes: POST /v1/coupons/{id}/sync, POST /v1/categories/{id}/publish-sales-channel | — | sync:write |
| Metafield conflicts (sync resolution) | /v1/metafield-conflicts, .../{id}/resolve, .../{id}/ignore | metafields:read | metafields:write |
| Regions (multi-region pricing / providers) | /v1/regions, /v1/regions/{regionId}/prices, .../countries, .../payment-providers | regions:read | regions:write |
| Tax classes | /v1/tax-classes, .../{id}/assign, .../{id}/set-default | tax-classes:read | tax-classes:write |
| Store info | /v1/store | store:read | — |
| Product reviews (storefront submit) | /v1/products/{id}/reviews | session-scoped | session-scoped |
| Storefront Bot settings | /v1/storefront-bot/settings | bot-settings:read | bot-settings:write |
| Storefront Bot conversations | /v1/storefront-bot/conversations, /v1/storefront-bot/conversations/{id}, .../{id}/summarize | bot-conversations:read | bot-conversations:write |
| --- | --- | --- | --- |
| Webhook subscriptions | /stores/{storeId}/webhook-subscriptions | webhooks:read | webhooks:write |
| Webhook deliveries (history only) | /stores/{storeId}/webhook-subscriptions/{id}/deliveries | webhooks:read | — (read-only) |
| Webhook test send | POST /stores/{storeId}/webhook-subscriptions/{id}/test | — | webhooks:write |
| API keys (issue / list / revoke) | /stores/{storeId}/api-keys | api-keys:read | api-keys:write |
| Sales channels (vibe-coded storefronts) | /stores/{storeId}/sales-channels | settings:read | settings:write |
| Modifier groups | /stores/{storeId}/modifier-groups | products:read | products:write |
| OAuth providers (customer login) | /stores/{storeId}/oauth-providers | settings:read | settings:write |
| Team (dashboard surface) | /stores/{storeId}/team | team:write | team:write |
| Store settings | PATCH /stores/{storeId}/settings | — | settings:write |
| --- | --- | --- | --- |
| Analytics | /analytics — not under /stores/{storeId} | analytics:read | — |
| Bundle offers | /bundle-offers | products:read | products:write |
| Order bumps | /order-bumps | products:read | products:write |
| Discount rules | /discount-rules | discount-rules:read | discount-rules:write |
| Backup (export / restore) | /backup | settings:read | settings:write |
| Media (dashboard library) | /media — the API-key surface is /v1/media above; this one is the dashboard's | products:read | products:write |
| Bulk product operations (price/stock/status sweeps) | /products/bulk/update-prices, .../update-stock, .../update-status, .../delete, … | products:read | products: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}/reviews | reviews:read | reviews: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 needssettings:writerather than astore-settings:*scope that does not exist, and why bundle offers and order bumps ride onproducts:*rather than amarketing:*scope that does not exist either. If a call 403s withINSUFFICIENT_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— requiresproducts:read/products:writescope 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 case | Scopes |
|---|---|
| Read-only data warehouse / BI sync | products:read, orders:read, customers:read, inventory:read, analytics:read |
| Inventory ERP sync | products:read, inventory:read, inventory:write |
| Order routing to fulfillment | orders:read, orders:write, webhooks:read, webhooks:write |
| Customer data into CRM | customers:read, webhooks:read, webhooks:write |
| Full catalog automation | products:*, 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 matchesproducts:readandproducts: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.
Related
- Authentication — the full auth flow per mode
- Rate limits — tiered limits per key
- Versioning — what counts as a breaking change in
/v1/* - Webhooks — subscribe, verify signatures, read deliveries
- Public API (v1) reference — every
/v1/*endpoint