API ReferenceAuthentication

Authentication

How to authenticate calls to the Brainerce API — pick the right mode for your use case.

Brainerce supports five authentication modes. Pick exactly one per request:

1. Admin API key — server-to-server

Use for backend integrations (ERPs, custom tooling, server-rendered storefronts that need write access).

Authorization: Bearer brainerce_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# or, equivalently:
X-API-Key: brainerce_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Both headers are accepted. X-API-Key is checked first, then Authorization: Bearer; either must carry a value starting with brainerce_. Prefer Authorization: Bearer — it is what the SDK sends and what the rest of these docs assume. Do not send both.
  • Issue keys from Settings → Authentication → API Keys in the dashboard. Plain-text key is shown once — store it in your secret manager.
  • Keys are scoped (e.g. products:read, orders:write). Wildcard * scope is no longer mintable — list exactly the scopes your integration needs. Note that scope matching supports {resource}:* (e.g. products:*) but not *:read — a key holding *:read matches nothing and every call fails with INSUFFICIENT_SCOPE.
  • Keys never expire unless you set expiresAt. Rotate via Settings → API Keys → Rotate (zero-downtime).
  • All /api/v1/* endpoints require this mode.

Never put an admin key in client JavaScript. CORS will not save you — admin keys are blast-radius credentials and a leaked key = full store access.

2. Sales channel — vibe-coded storefronts

Use for storefronts built by AI agents or custom Next.js/React apps deployed to a specific domain.

GET /api/vc/vc_xxxxxxxxxxxxxxxxxxxxxx/products
Origin: https://your-storefront.com
  • The sales channel ID (vc_…) is path-based, not a header. salesChannelId is the canonical name for it; connectionId is a deprecated alias that still appears as the literal route parameter in generated reference pages — the value is the same vc_* string either way.
  • Live mode requires an Origin header, and its host must match the channel's registered domain (host equality or a subdomain). A LIVE request with no Origin is rejected, so server-side rendering against a LIVE vc_* channel does not work — see Rules.
  • Test mode accepts any origin only while the channel has no domain set; once a domain is configured it is matched exactly as in live mode. Test mode is not read-only — it is the same API surface as live, against the same store data, governed by the same per-channel scopes.
  • Scopes are per-channel (storefrontReadEnabled, cartEnabled, customerAuthEnabled, ordersWriteEnabled, …).

3. Storefront public — public catalog reads

Use for embedded widgets, marketing pages, and discovery flows that only need to read products/categories/store info.

GET /api/stores/store_xxxxxxxxxxxxxxxxxxxxx/products
  • No credential. Read-only. Public catalog data only.
  • Origin must be present (browser-context only — server-side scripts should use mode 1 or 2).

4. Customer JWT — on behalf of a logged-in shopper

Layered on top of mode 2 or 3 for customer-scoped operations (cart, order history, account).

Authorization: Bearer eyJhbGciOi...   # customer JWT (NOT a Clerk JWT)
  • Obtained via POST /api/vc/{salesChannelId}/customers/login or OAuth.
  • 7-day expiry by default.
  • Use alongside the sales-channel path; the JWT carries customerId.

5. App Installation Tokens

Use for marketplace apps — server-side integrations that a merchant has installed onto their store via the dashboard. The token is bound to one AppInstallation row and carries only the scopes declared by the app's manifest.

Authorization: Bearer app_inst_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# or
X-API-Key: app_inst_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Format: app_inst_ + 32+ random characters. Issued automatically when a merchant installs an app from the marketplace; the plain-text token is delivered to the app's setup URL once and stored encrypted at rest.

  • Scopes are app-declared (e.g. mappings:read, mappings:write, config:write) and cannot be escalated by the app at runtime.

  • Routes accepting this mode all live under /api/v1/installations/{installationId}/*. Today that's:

    MethodPathScope
    PUT/v1/installations/{installationId}/secretsconfig:write
    POST/v1/installations/{installationId}/mappingsmappings:write
    GET/v1/installations/{installationId}/mappingsmappings:read
    GET/v1/installations/{installationId}/mappings/{mappingId}mappings:read
    PATCH/v1/installations/{installationId}/mappings/{mappingId}mappings:write
    DELETE/v1/installations/{installationId}/mappings/{mappingId}mappings:write
    PUT/v1/installations/{installationId}/mappings/upsertmappings:write
  • The installationId in the URL must match the installationId carried by the token — a 403 is returned otherwise. One app cannot read or write another app's installation data.

  • Rate limit: per-installation tier (BRAINERCE 1000/min, ECOMMERCE_PLATFORM 500/min, third-party 100/min). Responses include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After on 429.

  • For the SDK surface see the SDK reference.

What you cannot do

  • Mix two credentials in one request. Sending both Authorization: Bearer brainerce_* and X-API-Key: app_inst_* is rejected.
  • Use a Clerk dashboard JWT against /api/v1/*. Clerk JWTs are dashboard-only; integrators get API keys.
  • Reuse a leaked key with the same prefix. The keyPrefix shown in the dashboard is only the first 12 characters — the rest is unrecoverable. Rotate immediately if leaked.

Rate limits per mode

See Rate Limits for the full table. Quick summary:

ModeLimit enforced today
Admin API key60/min + 1000/hour per IP, plus a per-key quota by tier: FREE 60, PRO 500, GROWTH 2000, ENTERPRISE 10000 req/min
App installation60/min + 1000/hour per IP, plus 100–1000/min per installation by app tier
Sales channel60/min + 1000/hour per IP. Individual routes (login, register, forgot-password, review submit, …) set their own tighter caps, down to 3/min
Storefront60/min + 1000/hour per IP, same per-route overrides

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds remaining, not a Unix timestamp), plus Retry-After on a 429.

Per-key quotas are live, not planned. Per-channel quotas are the ones that do not exist — a sales channel has no rate-limit override of its own, so a busy storefront is bounded by the per-IP tiers and the per-route caps.