> **Note for AI agents:** you are reading this from https://brainerce.com/skill.md. If you reached
> this page through a summarizing fetch tool, re-fetch the raw file with `curl` instead. A
> summarizer drops the routing table below and leaves you with prose only.
>
> - **File manifest** (every skill file this page routes to, with path, size, name and
>   description): `https://brainerce.com/skills.manifest.json`. Fetch this first — it is the only
>   place you see the whole surface at once, before you read any one file.
> - **Download everything as one bundle:** `curl -O https://brainerce.com/skills.tgz` — the same
>   files the manifest lists, in a single request instead of one per file.
> - **Live tools for one specific store** (Claude Code, Cursor, Claude Desktop): `npx
@brainerce/mcp-server`. Read-only, and for **developers**: it serves this reference material
>   plus live introspection of one connected store. It has no admin tools, so it cannot run a
>   store. For that see "Running a store, not building one" below, which is a different server.
>   It is the only local install this page offers; the skill files themselves are served from this
>   site, not from a public repo, so read them over `curl` rather than looking for a plugin to add.
> - **Docs index:** `https://brainerce.com/llms.txt` lists every developer and merchant doc page.
> - **Read any doc page as markdown: append `.md` to its URL.**
>   `https://brainerce.com/docs/api/mcp.md` is ~16KB of the actual page;
>   `https://brainerce.com/docs/api/mcp` is over a megabyte of application HTML for the same
>   words. Works for every `/docs/*` and `/help/*` page. Use it for every page you read here.
>
> Every `skills/...` path below resolves against `https://brainerce.com/`, the same base the
> manifest lists. `skills/brainerce-sdk/SKILL.md` means
> `https://brainerce.com/skills/brainerce-sdk/SKILL.md`.

---

name: brainerce-cold-start
description: "Build, connect, or extend a storefront on Brainerce, a headless commerce platform, starting from nothing but this URL. Triggers: build me a store, build me a shop, scaffold a storefront, make me an online store, I want to sell X online, start selling online, open a shop, launch a brand, set up ecommerce, add a cart and checkout, add commerce to this site, turn this site into a store, connect this design to Brainerce, make this design real, wire up the shop, host this on Brainerce, I have a Brainerce connection id, I have a vc\_ id, add a coupon field, add a payment method, is my Brainerce integration complete, did I miss anything."

---

# Brainerce: cold-start entry

**You are a coding agent, and this is a skill for you to execute.** Brainerce is headless. It
supplies products, cart, checkout, payments, orders, inventory and discounts over an SDK and a
REST API; the project supplies the framework, the routing and the design. There is no CLI to
install and no login to complete before you begin — start with the user's intent below.

## Start here: the store already exists — create it, then design it

**If the user wants a store built, run this now, before reading further:**

```bash
npm create brainerce-store@latest my-store -- --defer-connection
```

**That command IS the storefront.** It writes a real Next.js project with the commerce already
finished and wired to the SDK: product pages, categories, search, cart, the full checkout,
accounts and auth, orders, reviews, loyalty, gift cards, referrals, i18n. You do not build any of
that, and **you must not rewrite it**. It needs no credentials, asks nothing, and interrupts
nobody — `--defer-connection` is what lets it run first.

⛔ **Add `--language he` if the user is writing to you in Hebrew** (or `--language en`; those are
the two). It decides which `messages/` ship and it is **the one decision `npm run connect` cannot
retrofit** — get it wrong and the storefront is rebuilt, not adjusted. Pass it only when their
language is already obvious from the conversation. **Currency needs no flag**: the template reads
the live store at runtime, so a scaffold-time guess corrects itself the moment you connect.

**Your job is the three steps after it:**

1. **Design it. This is the part that is actually yours, and shipping the default is not
   finishing.** The `src/ui/` that arrives is a working reference, not an aesthetic to preserve:
   rebuild it into something made for *this* merchant, at the standard you would hold any
   first-render work to. `src/core/` is the commerce and is off limits — an eslint rule enforces
   the line in both directions, so a refused import means you crossed it. **Add `--canvas` to the
   command above when the user wants a distinctive look** and `src/ui/` arrives as bare skeletons
   with `DESIGN ME` markers, commerce still wired underneath, so there is no old styling to fight.
   ⛔ **The hard rules are not optional**: tokens before components, 3-5 colours, WCAG 2.2 AA
   contrast as a floor that overrides even the merchant's own colour request, max 2 font families,
   mobile first, real imagery and never filler shapes or emoji as icons, and every empty state
   designed rather than blank — which matters more here than elsewhere, because a new store has no
   products and the empty catalog is the first thing its owner sees. The full bar is
   `AI-GUIDE.md` inside the project (and `skills/brainerce-storefront-build/references/design-quality-bar.md`
   if you are not on the Next.js starter). The project also ships a `/design <brief>` command that
   runs this properly.
2. **Connect**, at the end, with one browser click: `npm run connect` inside the project. Add
   `--seed-products '[...]'` to the same command and the store opens stocked.
3. **Hand it over.** See "How to end the run".

Details of each are below; the command above does not need them.

### ⛔ Nothing else is a Brainerce storefront

**A hand-written HTML page is not one, however good it looks** — and writing one is the most
common and most convincing failure on this page. It has a hardcoded product array, a hand-rolled
`Brainerce` object and `fetch` calls commented out "for production". It is a picture of a shop:
no surviving cart, no checkout, no inventory, no payment, no order, and pasting a real `vc_` id
into it changes nothing because the code returns the same array either way. The endpoints invented
for those commented calls do not exist, so "uncomment when you go live" is a promise that cannot
be kept.

- **Never call a page you hand-wrote scaffolded, connected or live.** Those words mean the command
  above ran. A demo ribbon inside the page does not undo the claim you make outside it.
- **Never invent an endpoint, SDK URL, header or CLI command.** The real ones are in the files this
  page pins; if you cannot reach them, say so rather than guessing a plausible URL.
- **No shell?** Then you cannot build a storefront. Say so, and offer the Admin MCP connector in
  Step 0.5 instead.
- **A design mock is a fine thing to want** — when the user asked for one after you said what it
  is, and you name it as a mock in your first sentence, before the feature list.
- **It is the shop's brand, never Brainerce's.** The template ships no Brainerce logo, colours or
  "Powered by" mark, deliberately. The storefront belongs to the merchant.

## Step 0: which of the three SDK modes applies

Every call is scoped by exactly one of these. For a storefront it is almost always the first row;
resolve it from whatever the user has already given you, and read on if they gave you nothing:

| You have                       | Mode                  | Scope                                |
| ------------------------------ | --------------------- | ------------------------------------ |
| a connection id starting `vc_` | vibe-coded storefront | one sales channel (`salesChannelId`) |
| a plain `storeId`              | public storefront     | the whole store, no channel split    |
| a key starting `brainerce_`    | admin                 | full read/write, merchant-side       |

## Step 0.5: the connection id — ask for it when you need it, not before

**Do not open with "give me your connection id".** The user arrived here by pasting one sentence
into you; a credential demand as your first reply is the single most common way this run dies.
Start working: read their intent, resolve the operation in Step 1, and tell them what you are
about to build.

**Building needs no id at all.** Nothing in a storefront's code depends on which store it points
at: the pages, the routing, the cart, the checkout and every SDK call are identical either way, and
only one environment variable differs. Scaffold with `--defer-connection`, build the whole thing,
and connect at the end with `npm run connect` inside the project. That is the order to prefer,
because an approval before a single file exists is an interruption, not a dependency.

The id becomes necessary at exactly one moment — **before the storefront can load real data**.

**There are two ways to get it, and which one is available to you is not a preference — it is a
capability.** Check whether you can execute shell commands, then take the matching path. Getting
this wrong is the single most common way a run dies, and it dies *after* the user has done their
part, which is the worst possible moment.

### No shell (a chat host) — ask for the connector

⛔ **Do not drive the device flow by hand here.** The piped run below is not available to you, and
the raw HTTP version in its place is worse than not trying: it polls for up to ten minutes, which
is not a thing you can hold across a chat turn, so the person approves a code and you are left
holding nothing.

Ask them to connect Brainerce as an MCP connector, and take the id from a tool call instead:

1. In their AI client's connector settings, add `https://mcp.brainerce.com/api/mcp/openai`.
   ⛔ That exact address. The plain endpoint lists ~275 tools, more than a chat client will load,
   and it fails silently: the connector signs in and then has no tools at all, so the server looks
   broken when it is not. This one lists 40 by name and is verified to load.
2. They sign in and choose the store on the Brainerce approval screen. The store is chosen there,
   so there is nothing for them to copy back to you and no id for them to find.
3. `list_sales_channels` and `create_sales_channel` are on that surface directly. Reuse an
   existing channel when one fits, and create one only when none does — the `vc_` id comes back in
   the result. ⛔ Do not read a full connection id back into the conversation unless they asked for
   it: it is the credential their storefront authenticates with.

The connector is the better outcome for them either way: it stays connected for the next
conversation, where a device code would have expired ten minutes after it was issued. Suggest it
even when you *can* run commands, if they expect to keep working with you.

**Products, on this path, come from the connector too.** `create_product` is on that same surface,
so you add starter products with tool calls. The seeding window described further down belongs to
the device code and is not reachable from here; do not go looking for it.

⛔ **Then skip the rest of Step 0.5 and go to Step 1.** Everything from "You can run commands"
to the end of "Filling the store" is about a script you cannot execute, its event stream and its
seeding flags. None of it applies to you, and reading it only spends your context.

### You can run commands — get the id yourself

**Prefer the deferred order in Step 1: scaffold, build, then `npm run connect`.** It needs nothing
from this subsection, and the approval costs the run one click at the end instead of an
interruption at the start.

Two situations still drive the script directly:

- **You want the id before scaffolding** (the user asked to see it live immediately, or you are
  wiring an existing frontend rather than creating one):

  ```bash
  curl -sSL https://brainerce.com/connect.mjs | node -
  ```

- **The user handed you a `vc_` id** and you want it checked before twenty files depend on it:

  ```bash
  curl -sSL https://brainerce.com/connect.mjs | node - --connection-id vc_xxx
  ```

⛔ **Run it in the background, never blocking.** It polls for up to ten minutes waiting for a human
click; in the foreground it holds your tool call open and you sit idle for all of it. In the
background you read its events and keep working.

⛔ **It emits one JSON object per line, and that stream is a contract, not log noise.** Relay
`awaiting_approval` (it carries the link, the code, and `opened` — telling someone to click a link
that already opened is its own confusion), stop on `denied` and `device_expired` rather than
retrying, and report both halves of `seeded`. **The full event table, every failure mode, the Node
version gate and the `Origin` requirement on `/api/vc/*` are in
`skills/brainerce-storefront-build/references/connect-script.md`** — read it before you run the
script, not after something surprises you. It is short, has no dependencies and writes no files,
so reading it first is also reasonable if your sandbox flags piped code.

⛔ **Whichever you run, keep working while they approve.** The framework, routing, layout, design
and page plan need no id. The two things that genuinely need the real one are scaffolding and the
first API call — never do either against a placeholder.

### Filling the store, in the same run

A newly connected store has no products, and the `vc_` id cannot create any: it is read-only over
the catalog. Approval opens a short seeding window on top of the device code, so putting starter
products in needs no admin key.

**One rule: if the run performs an approval, it can seed, and the flag is always
`--seed-products`.** Whichever of the three does the approving spends the window for you the moment
it lands, and none of them ever hands you the device code.

```bash
# 1. The preferred order: built first, connected and stocked at the end.
npm run connect -- --seed-products \
  '[{"name":"Ceramic Pour-Over 600ml","basePrice":149,"description":"600ml glass carafe."},
    {"name":"Burr Grinder","basePrice":389}]'

# 2. The scaffolder's own approval, when the user asked to see it live immediately.
npm create brainerce-store@latest my-store -- --seed-products '[...]'

# 3. The piped run, when you are getting the id before scaffolding.
curl -sSL https://brainerce.com/connect.mjs | node - --seed-products '[...]'
```

**Prefer the first.** Seeding at the end is not a compromise for having deferred the connection —
it is the better moment: the storefront already exists, so the catalog can match what you actually
built instead of what was guessed before any file was written. The other two ask for the products
before a single file is written, which is the same guess the deferred order exists to avoid.

One quoted JSON argument in every case (under a pipe, stdin is the script). If your shell mangles
the quoting — PowerShell does, and it is the default for a lot of agent runs — put the identical
JSON in the `BRAINERCE_SEED_PRODUCTS` environment variable instead; all three read it. A bare array is the shape; an
object with a `products` key is accepted too, since that is the request body shape. The payload is
checked against the route's own rules **before** the approval is even requested, so a quoted price
or a stray `sku` costs you nothing but a re-run. Watch for `seed_products_accepted`, then `seeded`
with `created` and `failed`, and relay both halves.

Three things about that flag, and each one bites on its own:

- **No approval, no seeding.** `--connection-id`, `BRAINERCE_CONNECTION_ID` and `--no-device` skip
  the approval, so there is no grant to spend, and `--defer-connection` moves the approval into
  `npm run connect` where the flag belongs instead. All three combinations are **refused up
  front** — `seed_not_possible` from the piped run, a plain message naming the right command from
  the other two — rather than connecting and silently dropping the catalog.
- **You cannot add it afterwards.** The window opens at the approval and the same command closes
  it, so a second attempt costs the user a second approval. Decide it before you run whichever
  command does the approving — which, on the deferred order, is at the end of the build.
- **The device code never reaches you.** It is never printed and never emitted by any of the three,
  so the credential that unlocks this window cannot land in your transcript or a CI log.

**Only drive the calls yourself if the piped run does not fit.** It is the same window either way,
so the reasons are narrow: a sandbox that will not pipe remote code, or a run that needs to
interleave the polling with something of its own. Doing it yourself also means taking on what the
script was doing for you: the Node version gate (`^20.19.0 || ^22.13.0 || >=24.0.0`) and the
capabilities probe that validates the id, which needs an `Origin` header on every `/api/vc/*` call.
`skills/brainerce-storefront-build/references/discovery.md` carries that contract in full.

1. `POST https://api.brainerce.com/api/device-auth/start` with `{ "clientName": "Claude Code" }`.
   `clientName` is optional, up to 60 characters, and the user reads it on the approval screen, so
   send it. You get back `deviceCode`, `userCode`, `verificationUri`, `verificationUriComplete`,
   `expiresIn` and `interval`. **`deviceCode` is a 64-character bearer credential. Hold it in
   memory for this run only: never print it, never write it to a file, never let it reach a log.**
2. Give the user `verificationUriComplete` in plain prose. It already carries the code, so there is
   nothing to type, and no browser is opened for you on this path. Tell them the page should show
   `userCode` and that it has to match, which is what proves the request came from this run and not
   from someone else who talked them into approving it.
3. `POST https://api.brainerce.com/api/device-auth/poll` with `{ "deviceCode": "..." }`, no faster
   than `interval` (5 seconds; sooner returns `slow_down`). Statuses are `pending`, `approved`
   (carrying `connectionId` and `storeId`), `denied` and `expired`. Keep building the framework,
   routing, layout and page plan while you poll. On `denied` or `expired`, stop and ask rather than
   starting another request.
4. Once you have `approved`, seed with the same device code. Polling burns the authorization
   request but not the seeding grant, so this order is the one that works.

```
POST https://api.brainerce.com/api/device-auth/seed
{ "deviceCode": "<the 64-character one you polled with>",
  "products": [{ "name": "Ceramic Pour-Over 600ml", "basePrice": 149, "description": "..." }] }
```

No auth header: the device code is the credential. 1 to 20 products, once, within ten minutes of
the approval. A product takes `name`, `basePrice` and an optional `description`, and nothing else:
no sku, no variants, no categories, no images. Each one lands as a simple, active product
attributed to the person who approved, not to you. There is no store id in the request. It comes
from the approval record, so this can only ever touch the store that person just approved.

Seed only when the user has described what they sell, using products that fit their brief; a store
that opens with three plausible items beats one that opens empty. **Never invent a catalog for
someone who did not describe one**, and never seed a store that already has products, which is
possible here because the approver can point the approval at a store they already run. **Never ask
for an admin key to seed.** This route exists so you do not have to, the window closes on its own,
and there is nothing durable left behind.

The response returns `created` and `failed` separately. Report both: a partial write said as a
clean win is how a merchant finds two of five products missing a week later.

## Step 1: resolve what this run is

⛔ **No shell? Step 1 is not yours, and the honest thing is to say so.** Every operation below —
create, connect, iterate, verify — edits files on disk. A chat host has none. Improvising around
that is the specific failure this warning exists to stop: pasting a project into the conversation
for someone to assemble by hand, or reporting a storefront you never wrote.

⛔ **And a hand-written HTML page is not a storefront** — see "Read this before you write a single
line" at the top. That rule applies here too, and this is where it usually gets broken.

What you CAN do from a chat is **run the store**, using the connector from Step 0.5: products,
inventory, categories, orders, fulfilment, customers, coupons, discount rules and analytics are all
tool calls. That is a real job and most asks that sound like "build me a store" turn out to want a
piece of it. Do that part, and be clear about which part you did.

For the storefront itself, hand off rather than stall. Say plainly that building one needs a tool
with a filesystem, and give them the line to paste into it:

```
Build me an online store with Brainerce. Follow https://brainerce.com/skill.md
My sales channel is vc_xxx.
```

Claude Code, Cursor, Windsurf, GitHub Copilot and the Codex CLI all take that as written. Naming
the channel matters: it is the one thing that run cannot work out on its own, and without it the
new session stops on the same question you already answered.

Resolve the operation in this order — stop at the first rung that decides. Do not let an empty
directory override what the user is actually asking for.

1. **The ask names an audit.** "Is my integration complete", "did I miss anything", a check before
   shipping to a real store owner → **verify**, regardless of what is on disk.
2. **A design was brought in or fetched** — Lovable, v0, Bolt output, a design-tool export, a zip —
   even into an empty directory → **connect**. Intent beats directory state.
3. **On disk, an integration already exists.** `@brainerce/sdk` or `brainerce` in `package.json`, or
   a `vc_` id already sitting in `.env` / `.env.local` → **iterate**.
4. **On disk, real UI with no Brainerce wiring.** The project has real UI that already exists on
   disk, that is, more than a default framework starter, with a product grid, a cart, or a checkout
   that already renders something, and no Brainerce client configured → **connect**.
5. **On disk, nothing.** Empty directory, no brought-in design → **create**.
6. **Still unclear → ask the user which of the four this is. Do not guess.**

Each operation below links a file in the manifest above; open it and follow it rather than
re-deriving its content here. Before you promise any feature, check "Asks with no storefront path
yet" below.

- **create**: nothing exists yet, no design was brought in. _"Build me a store", "scaffold a shop",
  "I want to sell X online"._
  Read `skills/brainerce-storefront-build/SKILL.md` for the build order and
  `skills/brainerce-integration-verify/references/required-features.md` for the
  functional-coverage checklist **before you write or scaffold anything** — both are build inputs,
  not an end-of-run audit. ⛔ **Then scaffold with `--defer-connection`, and do not stop for an approval mid-build.**

  ```bash
  npm create brainerce-store@latest my-store -- --defer-connection --language he
  ```

  Flags, including the one-way `--language` decision, are in "Start here" at the top.

  ⛔ **The commerce is already written — see "Start here" at the top.** `src/ui/` is yours,
  `src/core/` is not, and the scaffold also ships its own `CLAUDE.md`/`AGENTS.md` and a
  `/design <brief>` command for restyling. It is a starting point, not full coverage: check it
  against the feature checklist you already read before calling it done, and remember that
  **interrupting a build to ask for a credential is the failure this ordering exists to prevent.**

  When the storefront is finished, and only then, have the user run `npm run connect` inside the
  project. One browser approval, and it writes the sales channel id into `.env.local`, creating a
  store and a channel if they have neither.

  **If the user described what they sell, seed the catalog in that same command** — the approval
  opens a short window that needs no admin key, and this is the only moment it is open:

  ```bash
  npm run connect -- --seed-products '[{"name":"Burr Grinder","basePrice":389}]'
  ```

  Deferring is what makes this the *best* moment to choose a catalog, not a worse one: the
  storefront already exists, so the products can match what you actually built. Limits, accepted
  fields and the shell-quoting escape hatch are under "Filling the store" above.

  Two variants, for when the situation already differs. Seeding follows the approval, so it
  differs with them:
  - The user handed you an id → add `-- --connection-id vc_xxx`. No approval, so no grant:
    `--seed-products` is refused here, and products come from the dashboard or the Admin MCP.
  - They asked to see it live immediately → drop `--defer-connection` and the CLI approves before
    scaffolding, which also lets it read the store's real name and language. It never prompts, so
    it is safe in an agent shell; it is simply an interruption you chose. This one *does* approve,
    so `--seed-products '[...]'` goes on the `npm create` line — at the cost of picking the catalog
    before a single file exists.
- **connect**: a frontend already exists (Lovable, v0, Bolt, a hand-built prototype, or a project
  already on disk) and it needs to be wired to a real Brainerce backend without changing how it
  looks. _"Connect this to Brainerce", "wire up the shop", "make this design real", "host this on
  Brainerce"._ This is what "migrating an existing store" means on Brainerce **for a coding agent**:
  wiring an existing **design** to the backend, not a data import from a competitor platform. If the
  user actually wants to bring a catalog over from Shopify or WooCommerce, that is a merchant-side
  action, not something you drive through the SDK — point them to the **Migration Tool**, a
  marketplace app installed from their dashboard (Apps → Migration Tool) that runs a one-time,
  read-only import of products and orders (no customers, no live sync). Say this plainly rather than
  treating it as the same request.
  **After an import, build exactly as you would for any other store.** Imported products are native
  Brainerce products from that point on — same shape, same SDK calls, nothing marks them as
  foreign, and there is no separate "imported" code path to write. Re-running the import
  reconciles against what is already there rather than duplicating it, so a second run is not a
  hazard you need to design around. The one thing that genuinely did not come across is
  **customers**: no accounts are created and no passwords come across, so there is nothing for a
  returning shopper to log into and no address book to read. Every shopper registers fresh — do not
  build an "existing customers sign in here" path, and tell the owner plainly that their customer
  list did not migrate, because they will otherwise assume it did. (Imported _orders_ do retain the
  buyer's name and address on the order record itself, so order history reads correctly; that is
  order data, not a customer account.)
  Read `skills/brainerce-storefront-build/references/connect-existing-frontend.md` first: mock UI
  hides broken wiring in different places than a fresh build does. Read
  `skills/brainerce-integration-verify/references/required-features.md` too, but **as a hint, not a
  spec** — on a retrofit it tells you what an already-present surface should show, never what
  surfaces to add. A landing page with one buy button does not acquire a login flow and an account
  area because the checklist lists them. See that file's own "Read this list against the operation"
  table before you use it here.
- **iterate**: a Brainerce storefront already exists and the ask is one more feature (a coupon
  field, an OAuth provider, a new payment method). Read
  `skills/brainerce-storefront-build/SKILL.md` for where the feature fits in the existing build
  order, then the narrower skill for that feature if one exists (see the manifest).
  **Iterate is incremental — establish state before you write anything.** The storefront was built
  by someone (often an earlier run of this same skill), so assume the feature may already be half
  there. Read the existing client setup, find whether the surface exists already, and check the live
  connection with `skills/brainerce-storefront-build/references/discovery.md` — `get-required-features`
  returns the checklist already filtered to this store, so "what is missing" is a call, not a guess.
  Then apply **only the delta the ask requires and leave everything else alone**. Do not re-scaffold,
  do not rewrite a working component to match the style you would have used, and do not "fix" a
  feature nobody asked about. Never run the scaffolder in a directory that already has a storefront:
  it is for an empty one, and the most expensive failure on this path is a rebuild of work that was
  already correct.
- **verify**: an audit before shipping to a real store owner. Read
  `skills/brainerce-integration-verify/SKILL.md` and its `references/required-features.md`. It
  audits code against the mandatory-feature checklist instead of trusting that a file exists.

## Asks with no storefront path yet

These arrive often and **inventing a method name here is the single most common way a run ships
broken code that type-checks.** Name the blocker in the user's own words and build the rest of the
store around it.

**Does not exist. Do not build it, do not invent a method:**

- **Live or two-way sync with Shopify or WooCommerce.** The Migration Tool is one-time and
  read-only; nothing is ever pushed back, and imported products are native Brainerce products from
  then on.
- **A Meta, TikTok, Amazon or eBay sales channel.** **Google** is the only channel-scoped app
  published. Read the merchant's own **Apps** list before naming any integration.
- **Wishlist, favourites, save for later.** Nothing, anywhere in the platform. Client state only,
  and say plainly it does not follow the customer to another device.
- **A customer cancelling or returning their own order.** Both are merchant actions:
  `cancelOrder` throws by design, and buying a return label needs an admin key that must never sit
  in storefront code. Give them a contact route, not a button that cannot work.
- **Recurring anything** — subscriptions, standing orders, recurring donations. The module is
  merchant-facing, `capabilities` never reports it, and the storefront surface lands later.
  ⛔ Read the ask precisely, because **two neighbours DO work**: paid loyalty membership
  (`subscribeToMembership`) and ONE-OFF donations (`createDonation`). Never bolt a "monthly"
  toggle onto `createDonation` — it silently produces a one-off gift.
- **A shopper-assembled box, "pick any 3".** Merchant-defined kits only.
- **Selling a gift card** — a page where the shopper picks an amount and gets a code. Redemption
  works and is mandatory; minting is a merchant action. There is no gift-card product type either,
  so a product named "Gift Card" mints nothing and an amount picker ending in a checkout sells an
  empty box.

**Exists, and easy to get wrong. Read the reference before you build it:**

- **Donations** (`createDonation`, `getDonation`) — not a checkout: no line item, no quantity, no
  shipping, no order. ⛔ Gate the page on `getStoreInfo().donationsEnabled`, which unlike every
  other conditional feature does **not** auto-hide, and `createDonation` returns `PENDING`, so poll
  `getDonation` for `PAID` before thanking anyone.
- **Kits** (`product.type === 'KIT'`) — ONE cart line, added by the kit's own `productId`, no
  `variantId`, no `selections`. ⛔ Never add components as separate lines: that charges twice and
  reserves twice. Pricing is not necessarily fixed and a kit carries no `inventory` object of its
  own, so read `kitAvailable` — without it a sold-out kit renders as buyable.

Both are covered in full in `skills/brainerce-sdk/SKILL.md` and its `references/`, with the
required-feature checklist in `skills/brainerce-integration-verify/references/required-features.md`.

## Gift cards are built, and a gift card is not a discount

Redeeming a gift card at checkout is live and **mandatory**, so build the field on every store. It
auto-hides on `features.hasGiftCards`, a per-store switch rather than a count, so build it even
where the store has issued no card yet.

⛔ **A gift card is a means of PAYMENT, not a discount, and modelling it as one ships a storefront
that misstates tax.** `checkout.total` does NOT change when a card is applied and tax is still
calculated on the full amount; what drops is `checkout.providerAmountDue`. Render it on its own
line BELOW the total, then an "Amount due" row — never inside the discount block, never added to
`discountAmount`. This one is stated here rather than pinned away because "a gift card is a kind
of discount" is exactly what generic e-commerce knowledge will fill the gap with.

⛔ **Issuing is an admin API and never belongs in a storefront.** `issueGiftCard` and its siblings
mint stored value; an API key in anything a browser can reach hands a stranger the ability to
create money. Build the redemption field and stop there.

Everything else — the three redemption methods and their shapes, reading applied cards from
`checkout.tenders`, removing by `tenderId`, why every refusal returns one identical message, the
currency rule, applied-versus-balance, completing an order when `providerAmountDue` hits `0.00`,
and the eight admin endpoints with their four scopes — is in
`skills/brainerce-sdk/references/sdk-gift-cards-admin.md`, with the checkout sequence in
`skills/brainerce-checkout-flows/SKILL.md`. Read the first before writing the field.

## Where the how comes from

- **One SDK method's exact signature, arguments, return shape:**
  `skills/brainerce-sdk/SKILL.md` and its `references/`, including the full
  `references/type-definitions.md`.
- **The checkout, auth, cart-persistence and inventory-reservation sequence.** These are fixed
  regardless of framework, and improvising the order is what breaks silently:
  `skills/brainerce-checkout-flows/SKILL.md`.
- **Content pages, navigation, blog, SEO:** `skills/brainerce-content-and-navigation/SKILL.md`.
- **Custom fields (metafields) and catalog modeling** (variant vs. option vs. tag):
  `skills/brainerce-custom-fields/SKILL.md` and `skills/brainerce-store-architecture/SKILL.md`.
- **Live data for a specific, already-connected store**, meaning installed payment providers, real
  products, actual capabilities, rather than generic reference material: install
  `npx @brainerce/mcp-server` in Claude Code, Cursor, or the CLI, and prefer its live tools over any
  static file here. They reflect the one store you are building against; this page does not. They
  are read-only, so it can tell you how the store is configured but cannot change anything in it.

### When none of the above covers it

Work down this ladder, and stop at the first rung that answers you. Skipping to the bottom is
what produces invented methods; re-searching for something already pinned above just burns the
run.

1. **The file this page pinned for that topic.** If it is listed above, read it directly — do
   not go looking for a better source first.
2. **`references/type-definitions.md`** for an exact shape, and the live MCP tools
   (`get-store-capabilities`, `get-required-features`) for how _this_ store is configured.
3. **`https://brainerce.com/llms.txt`** — the index of every developer and merchant doc page.
   Find the page there, then fetch it.
4. **Nothing matched?** Then the thing does not exist. Check the "Asks with no storefront path
   yet" table above and say so plainly. Do not fill the gap from general e-commerce knowledge:
   a plausible-looking method that Brainerce does not have will type-check against nothing and
   fail at runtime, and the user will believe it was real.

⛔ **Existing code is not a source of truth — not in this project, and not in another one you
have seen.** It may target an older SDK version, and it will mislead you silently because it
compiles. The pinned files above are versioned with the platform; a snippet you found is not.

## Running a store, not building one

The skills above are for developers. If the person you are helping owns a store and is asking to
run it, meaning launch a sale, check today's numbers, fulfil an order, add a product, none of that
is reachable from the SDK docs above, and `npx @brainerce/mcp-server` cannot do it either. It needs
the hosted Brainerce **admin** MCP server, connected with that store's own credentials:

```bash
claude mcp add --transport http --scope user brainerce-admin "https://api.brainerce.com/api/mcp" --header "Authorization: Bearer THE_KEY"
```

`THE_KEY` is the merchant's own `brainerce_` API key, created in the dashboard under **Settings →
API Keys**; clients that support it can sign in with OAuth instead of pasting a key. This is the
one place an admin key belongs, and it belongs in the MCP client config, never in storefront code. **Do not add `?mode=compact`** — it lists only dispatchers and hides
every tool behind `search_tools`, and a model that cannot reach a write tool substitutes a
different one instead of saying it is blocked.

Once that connection exists, these describe what to do with it. All six are for store owners:

- `skills/brainerce-daily-summary/SKILL.md` — the morning briefing: what sold, what came in
  overnight, what needs attention today.
- `skills/brainerce-analytics/SKILL.md` — one number and what it means: revenue, order counts,
  average order value, best sellers, trends, month against month.
- `skills/brainerce-launch-a-sale/SKILL.md` — sales, discounts and vouchers, above all the choice
  between an automatic discount and a code the shopper types.
- `skills/brainerce-order-handling/SKILL.md` — a single order: look it up, fulfil it, add tracking,
  change its status, cancel it.
- `skills/brainerce-product-onboarding/SKILL.md` — add a product properly, through category, tags,
  stock and publishing, or correct stock after a count.
- `skills/brainerce-store-health/SKILL.md` — sweep the store for drafts, out-of-stock items,
  expired promotions and orders stuck waiting on someone.

## Don't burn the user's run

- **Don't start a dev server to prove it works.** By default do not run `npm run dev` to curl
  pages, drive the cart, or open a headless browser. Correctness comes from following the
  references; a headless run cannot complete an email verification or a real payment anyway, so
  the loop costs minutes and returns almost no signal. Do it **only** when the user explicitly
  asks you to verify or test — and then keep it to one pass that the pages compile and render.
- **The store starts empty unless you seeded it.** You cannot create products with a `vc_` id:
  the catalog write endpoints require a `brainerce_` admin key. **Never ask for one to make a demo
  look full.** The way in without a key is the seeding window under "Filling the store" above if
  you have a shell, or `create_product` on the connector path. Either way the storefront still has
  to render an honest empty state, because the merchant can empty the catalog again tomorrow — and
  saying where more products come from is part of ending the run, below.
  `skills/brainerce-storefront-build/references/seed-catalog.md` has both seeding paths, including
  the admin-key one for a user who already handed you a key.
- **Ask once, then wait.** When something is genuinely a human step in the dashboard (a channel,
  a payment provider, a domain), say precisely where to click and stop. Do not re-ask, and do not
  invent a placeholder to keep moving.

## Rules that cause production incidents when skipped

Read `skills/brainerce-storefront-build/references/critical-rules.md` in full before writing SDK
code. The two that catch people most often: prices come back as strings, so parse before doing
arithmetic on them (tax is the one exception, `TaxBreakdown` amounts are already numbers); and
every DB-backed feature is scoped by store (and by connection id, for vibe-coded mode), so never
hardcode currency, locale, or store id.

## How to end the run

⛔ **A run that ends at "it compiles" is not finished.** The user asked for a store, and a store
that cannot take money is not one. You already hold everything needed to say so precisely: the
capabilities response you fetched during the build reports exactly which features this store has,
and the empty ones are the handoff. Close with these four things, in this order.

**1. Where it is.** `npm run dev`, then `http://localhost:3000`. Say it plainly — this is the
first time the person can look at what they asked for.

**2. What is actually in it.** The products you seeded, by name, and where the rest come from
(the dashboard, or **Apps → Browse → Migration Tool** for a one-time import from Shopify or
WooCommerce). If seeding failed or you never seeded, say the catalog is empty rather than letting
them discover it.

**3. What only they can switch on**, read off `capabilities.features` rather than guessed. Name
only what is actually missing, with where to click:

| What the response says             | What to tell them                                                             |
| ---------------------------------- | ----------------------------------------------------------------------------- |
| `paymentProviders` is empty        | **Checkout cannot take money yet.** **Apps → Browse → Payment Gateways**, install one, then configure it from **Apps** in the sidebar. This is the one that blocks a real sale, so lead with it. |
| `hasShippingZones` is `false`      | No delivery area is defined, so shipping cannot be quoted. **Shipping → Zones & Rates** in the sidebar. |
| `oauthProviders` empty or disabled | Social login is off; email sign-in still works. Only mention it if you built the buttons. |
| `hasGiftCards`, `hasLoyaltyProgram`, `hasDiscountRules`, `hasCoupons` are `false` | The storefront already honours each of these the day it is turned on, because you built them to auto-hide. Say that — it is the reason the code looks like it has features the store does not. |

Their own domain and their plan are dashboard-side too; the **Free plan allows exactly one sales
channel**, which is worth saying before they try to add a second.

**4. What you would not do for them.** If you skipped something, or a check did not pass, say which
and why. Do not close on a summary that reads better than the run went.

**5. How it goes live, because `localhost:3000` is not a shop.** Brainerce is the commerce backend,
not a host: the project is an ordinary Next.js app and they deploy it wherever they like. Say that,
and name the two things that break on the first deploy and nowhere before it, because both are
invisible while it works on their machine:

- **`SITE_URL` must be set in the host's environment.** Some hosts never tell the app what address
  visitors used — OpenAI Sites is one — so it introduces itself as `localhost`, and a channel with
  a Domain recorded refuses those requests. The symptom is product pages failing while the products
  are perfectly fine.
- **The same address must be recorded on the channel**, under **Sales Channels → the channel →
  Domain**. That check runs on Brainerce's side and no setting inside the project can satisfy it.

The full walkthrough, including Test versus Live mode, is
`https://brainerce.com/help/connect/connect-with-npx.md` under "Publishing it to a real web
address". Point them at it rather than reciting it.

**Then stop.** Do not re-ask for a credential, do not offer to keep polishing, and do not start a
dev server to prove your own work. If they want more, they will say so — and if they want to *run*
the store rather than build it, point them at "Running a store, not building one" above.

### If the host refuses to publish over the SDK's age

Some hosts run a **package-age cooldown**: a supply-chain defence that refuses any dependency
published in the last few days. pnpm ships the setting (`minimumReleaseAge`) and **OpenAI Sites
enforces seven days**, so a refusal naming the `brainerce` SDK version is what you may see there.

⛔ **This is not a defect in the storefront, the SDK, or the store, and there is nothing to fix.**
The build is complete and correct, the connection is real, and the draft the host saved is real.
The same publish succeeds once the pinned version ages past the host's window — no rebuild, no
edit, no re-approval.

⛔ **Do NOT try to fix it.** The three things that suggest themselves all make it worse: lowering
the pin drops the SDK below APIs the template calls and breaks type-check; raising it reaches for
a *newer* release, which is younger still; and hand-editing the version in `package.json` leaves
the merchant a storefront that no longer compiles. The template pins an exact version precisely so
this resolves itself.

**Report it as a waiting state, not a failure.** Say what shipped, that publishing is held by the
host's cooldown rather than by anything in their store, and that retrying the publish later is the
whole remedy. Everything else in the handoff above still applies.
