Update to Brainerce
Snapshot Oct 24, 2026 · 18:05 UTC · version 2.0.4
Collection source: downloaded plugin package. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.
Payment or plan references changed
Full description — Now says: audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how...
Observed in published text. Live prices and checkout terms have not been verified by this change.
Up to 10 plugins free · no card required.
All fields and original values.
Full description
Sep 30, 2026 · 23:07 UTC
diagnose one that is already wrong. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, how do I set up my menu, my product list is a mess, or I have hundreds of variants I...
Oct 24, 2026 · 18:05 UTC
audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags,...
Skill instructions
Sep 30, 2026 · 23:07 UTC
"Decide how a Brainerce catalog should be structured, and diagnose one that is already wrong. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, how do I set up my menu, m...
Oct 24, 2026 · 18:05 UTC
'Decide how a Brainerce catalog should be structured, and audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, ...
Compare saved observations
Download comparison JSONSKILL.md text changes
− removed · + added
--- before +++ after @@ -1,13 +1,15 @@ --- name: brainerce-store-architecture -description: "Decide how a Brainerce catalog should be structured, and diagnose one that is already wrong. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, how do I set up my menu, my product list is a mess, or I have hundreds of variants I cannot manage. Answers the modelling question before anything is created: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Also audits an existing catalog and returns an ordered improvement plan. For store owners, and for developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages, menus or blog, use brainerce-content-and-navigation. Keywords: catalog structure, variants or options, variant explosion, too many variants, categories vs tags, hierarchy, menu structure, product model, organise my shop, catalog audit." +description: 'Decide how a Brainerce catalog should be structured, and audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, or I have too many variants to manage. Also owns store settings (currency, language, going live), shipping zones, rates, labels, sales-channel publishing, subscriptions, apps, email domains, webhooks and backups. Free shipping as a standing rate lives here; as a promotion it does not. For store owners and developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages or menus, use brainerce-content-and-navigation. Keywords: catalog structure, variant explosion, categories vs tags, shipping zones, sales channels, go live, currency, subscriptions, email domain, webhooks.' compatibility: ChatGPT, Claude Code, Claude Desktop, Cursor maintainer: Brainerce metadata: author: Brainerce - version: "1.0.0" + version: '1.0.0' --- +⛔ **Before you call anything a dashboard task, look for `search_tools` in your tool list.** Some connections carry a small curated list PLUS a way to reach everything else. Where `search_tools` exists, search for what the merchant asked for and call any hit through the `call_read_tool` / `call_write_tool` / `call_destructive_tool` it names. The dashboard is the honest answer only when your list has no `search_tools`, or the search comes back empty. A few things are refused through it deliberately (payment provider setup, refunds and recording payments, gift cards), which is why an empty result there is a real answer rather than a gap. + Get the catalog model right the first time, or find out exactly where an existing one went wrong. ## Core principle @@ -37,28 +39,30 @@ ## The five mechanisms -| Mechanism | What it is | Own stock? | Where it is set up | -|---|---|---|---| -| **Variant** (from an attribute) | One row per combination, each with its own price, SKU and inventory | Yes | Dashboard variant editor, or `variants` on `create_product` | -| **Modifier group** | A choice at checkout that changes the price but not what you ship | No | Dashboard, Products then Modifiers | -| **Custom field** (metafield) | Structured data on the product; 15 types; can be buyer-facing or a storefront filter | No | Definition in the dashboard, value from chat | -| **Category** | The product's one permanent home in the menu tree | n/a | `create_category`, or the dashboard | -| **Tag** | A flat label that cuts across the tree | n/a | `create_tag`, or the dashboard | +Where a tool is named below, "full connector" means Claude Code, Cursor and Claude Desktop connected to the Admin MCP, which lists every tool; the ChatGPT app carries a curated list, and each section says which of its tools are on it. + +| Mechanism | What it is | Own stock? | Where it is set up | +| ------------------------------- | ------------------------------------------------------------------------------------ | ---------- | ----------------------------------------------------------- | +| **Variant** (from an attribute) | One row per combination, each with its own price, SKU and inventory | Yes | Dashboard variant editor, or `variants` on `create_product` | +| **Modifier group** | A choice at checkout that changes the price but not what you ship | No | Dashboard, Products then Modifiers | +| **Custom field** (metafield) | Structured data on the product; 15 types; can be buyer-facing or a storefront filter | No | Definition in the dashboard, value from chat | +| **Category** | The product's one permanent home in the menu tree | n/a | `create_category`, or the dashboard | +| **Tag** | A flat label that cuts across the tree | n/a | `create_tag`, or the dashboard | ## Want X, use Y -| The merchant wants | The right mechanism | -|---|---| -| Shoppers pick between options and each option is counted, priced and shipped separately | Variant | -| Shoppers pick an add-on that changes the total but not what leaves the warehouse | Modifier group | -| Shoppers type or choose something personal, and it should print on the order | Custom field with Customer Input turned on | -| Shoppers narrow the product list by a property | Custom field of type Select, Multi-select or Yes/No, with storefront filters turned on | -| A spec that only needs to be displayed, never chosen or filtered | Custom field of any type | -| A permanent place in the navigation menu | Category, nested under a parent | -| A collection that cuts across the menu, or a seasonal grouping | Tag | -| Who manufactured it | Brand | -| A different price in another country | Regional pricing, dashboard | -| The same catalog sold in more than one place | Sales channels | +| The merchant wants | The right mechanism | +| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| Shoppers pick between options and each option is counted, priced and shipped separately | Variant | +| Shoppers pick an add-on that changes the total but not what leaves the warehouse | Modifier group | +| Shoppers type or choose something personal, and it should print on the order | Custom field with Customer Input turned on | +| Shoppers narrow the product list by a property | Custom field of type Select, Multi-select or Yes/No, with storefront filters turned on | +| A spec that only needs to be displayed, never chosen or filtered | Custom field of any type | +| A permanent place in the navigation menu | Category, nested under a parent | +| A collection that cuts across the menu, or a seasonal grouping | Tag | +| Who manufactured it | Brand | +| A different price in another country | Regional pricing: `create_region`, then `upsert_region_prices`, full connector | +| The same catalog sold in more than one place | Sales channels | Two of those rows are the ones merchants get wrong, and they have their own rules below. @@ -150,6 +154,112 @@ Then hand back: the category tree with parents and children, the attribute or attributes that become variants, the custom fields with their types and whether each is buyer-facing or filterable, and any modifier groups. Say which parts you can build now and which are dashboard steps. +## Shipping zones and rates + +Where you deliver, and what you charge for it. This is **configuration, not a promotion**: a merchant asking "how much do I charge to ship to Europe" or "which countries can I sell to" is here, not in `brainerce-launch-a-sale`. That skill only lays a free-shipping offer on top of rates that already exist, so if there are no rates, there is nothing for it to discount. + +The model is two levels, and merchants get it wrong in one direction: + +- A **zone** is a place: a country, a set of countries, a region. `list_shipping_zones`, `get_shipping_zone`, `create_shipping_zone`, `update_shipping_zone`, `delete_shipping_zone`. +- A **rate** is a price inside a zone: flat, by weight, by order value, or free over a threshold. `list_shipping_rates`, `create_shipping_rate`, `update_shipping_rate`, `delete_shipping_rate`. + +**What a rate's `rateConfig` must look like, per type.** FLAT_RATE `{ amount }`; WEIGHT_BASED `{ baseRate, perKg, maxWeight? }`; PRICE_BASED `{ tiers: [{ minOrderAmount, rate }] }`; FREE `{ minOrderAmount? }`; LOCAL_PICKUP `{ amount, pickupLocation: { name, line1, city, country, ... } }`. All amounts are plain numbers of 0 or more. A config that does not match the type is refused, so read the error and fix the shape; do not retry the same call. Changing a rate's `type` needs its new `rateConfig` in the same call. + +**A delivery area drawn on a map.** `update_shipping_zone` takes `geometry` (GeoJSON Polygon or MultiPolygon, coordinates in [longitude, latitude] order, every ring closed), optional `geometryLabels` (display names, one per shape), and `salesChannelIds` to offer the zone on certain channels only. A zone matches an address that is inside its country list OR inside its drawn shape. Create the zone first, then update it to add the shape. An invalid shape is refused; `null` removes it. `calculate_shipping` shows what a buyer at an address would be offered and why a zone did or did not match. + +So "free delivery over 200 shekels in Israel" is a rate on the Israel zone, not a discount rule. Reach for a `SHIPPING_DISCOUNT` only when the merchant wants a temporary offer over the top of their normal rates, and say which one you built. + +⛔ **Every shipping zone, rate and class tool is OAuth-only, and so are `calculate_shipping` and `set_manual_tracking`.** On a `brainerce_` API-key connection they refuse outright; the other shipment and label tools work on either credential once a shipping app is connected. The ChatGPT app's curated list carries three of them since 2026-09-15: `list_shipping_zones`, `create_shipping_zone` and `create_shipping_rate`, which is enough to add basic shipping from chat: a zone for the destination countries (`["*"]` is worldwide), then at least one rate on it, because a zone with no rate offers nothing at checkout. Editing or deleting a zone or a rate, and everything below, stays on the full connector. Check your tool list, and route to the dashboard's Shipping settings otherwise. + +**A country with no zone cannot be checked out to.** That is the finding worth surfacing in an audit: a merchant who says "customers abroad cannot buy" usually has no zone covering them. + +### Shipping classes, and the two diagnostics + +A **shipping class** is a tag a product carries ("Oversized", "Fragile") so a rate can charge it differently. On the full connector: `list_shipping_classes`, `create_shipping_class`, `update_shipping_class`, `delete_shipping_class`. Creating a class changes nothing on its own: a product has to carry it (`update_product` with `shippingClassId`) and a rate has to price it (`classCosts` on `create_shipping_rate` or `update_shipping_rate`), so say both steps when you create one. Deleting a class unassigns every product that carried it, and the products then ship at the rate's base cost. + +Two read-only tools answer the questions merchants actually ask: + +- `calculate_shipping` runs the store's own zones and rates for an address exactly as checkout does and returns the offered rates plus a verdict per zone saying why it did or did not match. It is the tool for "why does my customer see no shipping methods". It is not a live carrier quote. +- `check_free_shipping` compares, per sales channel, the free-shipping threshold the storefront bar promises with any FREE-type rate that has its own minimum. `hasMismatch: true` is the case where the bar says one figure and checkout charges by another. + +### Shipments and labels + +Separate from zones and rates, which are what the shopper pays, a **shipment** is the parcel record: carrier, tracking number, label. On the full connector `list_shipments`, `get_shipment` and `get_shipment_tracking` read them, `create_shipment` records a parcel the merchant hands to a carrier themselves, `set_manual_tracking` attaches a tracking number and ships the order (and emails the customer; there is no quiet mode on that path), and `cancel_shipment` voids a bought label at the carrier. `get_ship_from_origin` and `save_ship_from_origin` hold the address every quote and label is sent from; without a complete one no carrier can quote. `validate_shipping_address` asks the carrier whether an address is deliverable. + +`get_order_shipping_rates` is a live carrier quote for one order, and `buy_shipping_label` quotes first and buys only with `confirm: true`, charging the store's carrier account. Carrier credentials are entered in the dashboard, never through the connector. Shipment tools work only once a shipping app is connected; before that they refuse, and the refusal is the connection, not the order. Always show the merchant the amount and carrier before passing `confirm`. None of the shipment or label tools are on the ChatGPT app. + +## Selling the same catalog in more than one place + +Sales channels are how one catalog reaches a storefront, a marketplace and a social shop without duplicating products. On the full connector `list_sales_channels`, `get_sales_channel`, `create_sales_channel`, `update_sales_channel` and `test_sales_channel_connection` manage the channels themselves. + +Publishing is separate from creating, and this is the part merchants miss: **a product is not on a channel until it is published to one.** `publish_product_to_channel` and `publish_product_to_channels` do that, `unpublish_product_from_channel` reverses it, and `get_product_channel_publishes` says where a product is currently live. `get_product_channel_overlay` and `save_product_channel_overlay` let one product carry a different title or price per channel. Coupons and taxonomy publish the same way, with `publish_coupon_to_channel` and `publish_taxonomy_to_channel`. + +**Naming the channel.** Every one of these tools (and `get_sales_channel`, `update_sales_channel`, `delete_sales_channel`, `test_sales_channel_connection`, `set_store_member_sales_channels`, `invite_store_member`) takes the channel as the `id` from `list_sales_channels`, **or** its public `vc_*` connection id, **or** its exact name — any of the three, and the plural tools accept a mix. Use whichever the merchant gave you; do not translate a `vc_*` id or a name into the record id first. A reference that matches none of them errors naming it, and the fix is to read `list_sales_channels` rather than guess. + +Products a storefront run seeded through the device-auth window (`--seed-products`) arrive already published to that channel; the seed report says `published: false` for any that did not, and those are published from here or the dashboard, never re-created. + +`list_products` takes a `salesChannelId` filter, so "what is actually live on my Instagram shop" is answerable. + +On the ChatGPT app's curated list: `list_sales_channels`, `create_sales_channel`, `publish_product_to_channel`, and since 2026-09-15 `bulk_update_platforms` and `set_sales_channel_live`. Unpublishing one product, the plural publish, per-channel overlays, and coupon and taxonomy publishing stay on the full connector. + +### Tracking ids and the signup form on a channel + +`update_sales_channel` also sets the channel's tracking ids: `gtmContainerId` (GTM-ABC1234), `ga4MeasurementId` (G-ABC1234XYZ), `metaPixelId` (10 to 20 digits) and `tiktokPixelId`, plus `googleSiteVerification`. An empty string clears one and the channel falls back to the id discovered from its connected app. A malformed id is refused, because it would otherwise be written into the storefront page. `requireBirthday: true` makes shoppers give a birthday (month and day, never a year) when they register; leave it off unless the merchant asks, because it works better as an incentive for a birthday gift than as a gate. + +### Publish the whole catalog at once + +Publish a catalog: `bulk_update_platforms({ filter: { status: 'active' }, vcPublishTo: [channel] })`; `vcPublishTo` accepts the channel id, its `vc_` id or its name. `filter: { status: 'active' }` is the "all my products" form; `filter: {}` alone is refused, and so is a call with neither `productIds` nor `filter`. It changes visibility only, `vcUnpublishFrom` undoes it, and a large catalog is queued: the result then carries `{ queued: true, jobId }`, so report it as in progress and poll `get_bulk_job_status` rather than calling it again. For one product `publish_product_to_channel` is the smaller call. + +### Going live + +Going live: `set_sales_channel_live({ channelId, domain, live: true })` records the public https domain and switches the channel to LIVE; the payment provider is still connected in the dashboard. A TEST channel takes no real orders. The domain must already point at the deployed storefront, because this records it and deploys nothing; the call is refused, with nothing changed, when `live: true` and no valid domain exists. A LIVE channel with a payment provider still in TEST mode declines real cards, so read `get_store_setup_status` after flipping it and say what is still missing. `live: false` puts the channel back into TEST. Do not repeat a full `vc_` id into the conversation; the result carries only its last four characters. Every other channel setting is `update_sales_channel`, full connector only. + +### What a channel is connected to + +On the full connector, `get_sales_channel_integrations` says which marketing connectors (Google Merchant Center, Meta, TikTok) are bound to a channel and how their last sync went, which is the read for "my products are not on Google". It also carries the Google Customer Reviews opt-in state (connected Merchant ID + whether the merchant confirmed the opt-in) — Google's own verified-buyer survey feeding the seller rating, not the same thing as Brainerce's storefront product-review feature. `sync_sales_channel_now` re-pushes every published product to those connectors in the background; it is skipped on a TEST channel, because connectors only receive LIVE ones. `get_sales_channel_tracking` shows the GA4, GTM, Meta and TikTok tag ids the storefront serves, public ids only. `set_product_channel_app_visibility` hides one product from a connector app (Google Merchant Center today) without unpublishing it from the storefront. `get_connection_health` rolls up every channel and installed app with its last ping and last error. + +⛔ **`create_product({ platforms })` is not a publish path.** It expects a channel record id behind a literal `vc:` prefix; a `vc_` connection id or a channel name matches nothing, the call still succeeds, and the product stays unpublished. `update_product` has no `platforms` field. Publish with `publish_product_to_channel`, which takes any of the three channel references above. + +## Subscriptions and recurring plans + +A store can sell on a repeating charge rather than one payment. On the full connector this is `list_recurring_plans`, `create_recurring_plan` and `update_recurring_plan` for what is offered, and `list_recurring_subscriptions` plus `arm_recurring_subscription`, `pause_recurring_subscription`, `resume_recurring_subscription` and `cancel_recurring_subscription` for individual customers. `get_recurring_plan_armability` says whether a plan can actually start charging, and it is the thing to read before promising a merchant it is live. Not on the ChatGPT app's curated list. + +## Store settings, currency and language + +`update_store` is Settings, General from chat. On the ChatGPT app it takes the store's name, country, currency, default language and the two tax flags (`taxEnabled`, `pricesIncludeTax`); on the full connector it also takes timezone, discount stacking mode, pricing-formula mode, contact email and phone, social links, brand colours, logo (an asset from `ingest_media_asset`, never an external URL), homepage meta description, the review flags and which dashboard modules are enabled. Omitted fields are unchanged. + +`update_store` changes currency and language too: currency relabels every price (nothing is converted), is refused while a checkout is pending and needs `confirm: true` once the store has orders; language seeds email templates. `set_store_languages` adds or removes extra storefront languages; `activate_store` moves a store that sells without a payment provider from SETUP to ACTIVE. + +Say the relabelling out loud before you touch currency: 100 ILS becomes 100 EUR, on every product, sale price, shipping rate, coupon amount and gift-card balance, and the default region follows the store in the same change. A merchant who says "switch me to euros" on a store with prices almost never means that, so ask what they want the prices to be first. On language, every product or category text without a translation is then shown as the new language, not translated; ten languages are supported (en, he, ar, es, fr, de, ja, zh, ru, pt). Removing a language with `set_store_languages` deletes that language's email templates and any edits to them, which cannot be undone. + +`set_store_languages` also takes `autoTranslateNewProducts` (translate a new product into every supported language) and `nonDefaultLocaleStrategy` (what happens to AI-written content when the merchant writes in a language other than the default: `translate-to-default`, `keep-input`, `ask` or `single-language`). + +The business identity printed on purchase orders is not on `update_store`: use `update_store_business_profile` for `legalName`, `taxId` and `businessAddress` (null clears each), and for the reporting settings `analyticsCurrencyMode` (NATIVE groups revenue per currency; AGGREGATE converts into `reportingCurrency`) and `fxProvider` (ECB, OPENEXCHANGE or MANUAL). It is for the document identity and reporting only: the postal address on marketing emails is set in the dashboard under Settings, Email. + +`taxEnabled: true` only switches the flag on. With no rate checkout charges 0 tax. The rate is `create_tax_rate`, on the ChatGPT app since 2026-09-15 alongside `list_tax_rates`: read the list first and refuse a duplicate country and region pair, take the percentage the merchant gave (18 means 18%) and never one you assume, and always a country (there is no rate that applies everywhere; a country with no rate pays 0 tax), because the rate is the merchant's legal number and the tool registers nothing with any authority. Editing and deleting rates, the Canada preset, and tax classes are full connector only. + +`activate_store` is idempotent and is only for a store that sells without a payment provider (cash on delivery, bank transfer): the dashboard activates a store the moment a provider goes live, and checkout does not gate on the status, so activating does not make a store ready to sell. `get_store_setup_status` is still the checklist. + +⛔ `update_store`, `set_store_languages` and `activate_store` are OAuth-only; `get_store_setup_status` and the tax-rate tools work on an API key too. On the ChatGPT app `update_store` carries only the fields named above; timezone, contact details, colours, logo and modules are full-connector work there, not dashboard work. + +## Apps + +On the full connector `browse_marketplace_apps` and `get_marketplace_app` find an app, `install_app` installs it by slug, `configure_app` sets its non-secret settings, and `suspend_app`, `resume_app`, `disconnect_app` and `uninstall_app` manage it afterwards. `trigger_app_sync` queues a full import from a connector such as Shopify or WooCommerce; `get_app_sync_status` and `get_app_activity` report on it. + +The assistant can install an app and change its non-secret settings, but it can never enter or read an API key, password or token; those are entered only in the dashboard, and a payment gateway installed from chat takes test cards only until you connect it live there. `install_app` creates the installation only: OAuth consent, provider credentials and KYC are finished by the merchant under Apps in the dashboard, and `configure_app` refuses by name any key, token, password or the test/live switch of a payment app. Say the dashboard step in the same breath as the install, and never report a payment provider as live because it is installed. `install_app`, `sync_sales_channel_now`, `get_sales_channel_tracking` and `get_sales_channel_integrations` are OAuth-only; the rest work on an API key too. None of the app tools are on the ChatGPT app. + +## Email sending and domains + +Transactional emails (order confirmation, shipped, and the rest) are templates per event and per language. On the full connector `list_email_templates`, `get_email_template` and `update_email_template` edit them, `preview_email_template` renders one with sample data without sending, `list_email_template_versions` and `restore_email_template_version` roll one back, `reset_email_template` and `reset_all_email_templates` return to the built-in design (destructive; the second needs `confirm: true`), and `create_email_template` and `delete_email_template` exist for a second variant of one event, which is rare. `send_test_email` sends only to the signed-in merchant's own address; there is no recipient argument and it is never a way to email a customer. `list_email_logs` is the send log with delivery status per message. + +A custom sending domain lets the store send from its own address instead of the shared Brainerce one. `create_email_domain` registers it and returns the DNS records the merchant adds at their registrar (public values, show them verbatim), `list_email_domains` shows verification status, and `delete_email_domain` removes one. `verify_email_domain` looks each record up in public DNS and puts a `check` on every record in `dnsRecords` (found, missing, wrong, proxied, doubled, duplicate or unknown), so tell the merchant exactly which record to fix; `unknown` means the lookup could not run, not that the record is missing. Approval itself happens on the provider's schedule once every record is found, so a call made right after the records were added can still say PENDING; tell the merchant to wait rather than looping. Once VERIFIED, select the domain in `update_email_settings`. Every domain tool is OAuth-only, and none of the email tools are on the ChatGPT app. + +## Webhooks and backups + +Webhooks are developer plumbing, but merchants ask "why did my integration stop getting orders". On the full connector `get_webhook_health` is the one-glance answer, `list_webhook_subscriptions` and `list_webhook_deliveries` show the detail, `update_webhook_subscription` edits the URL or events (the list replaces the whole set) and pauses or resumes one, and `isActive: true` on a tripped subscription resets its circuit breaker. `send_test_webhook_event` queues a signed test event, and `delete_webhook_subscription` removes one for good. Creating a webhook subscription and rotating its secret are dashboard-only because both display the signing secret once. No tool ever returns the secret. + +Backups: `create_store_export` starts a background export (a full backup when `scope` is omitted), `list_store_exports` and `get_export_status` follow it, and the download link on a completed export is one the merchant opens in a browser. `get_backup_schedule`, `upsert_backup_schedule` and `delete_backup_schedule` manage the automatic schedule. Restoring a backup is dashboard-only; the tools create, list, link and delete exports. Only one export can run per store at a time, so a conflict means check the list rather than retry. None of the webhook or backup tools are on the ChatGPT app. + ## What can be done from here, and what cannot **From this conversation:** @@ -159,23 +269,42 @@ - `create_product`, including its `variants` array. - `update_product` to move products between categories. ⛔ `categories` REPLACES the set while `categoryNames` is additive, so read the product first and know which one you want. - `set_product_metafield` to write a custom-field value, but only for a field some product already carries. See `brainerce-custom-fields` for why. +- `create_shipping_zone` and `create_shipping_rate` for basic shipping, `publish_product_to_channel` to put one product on a storefront and `bulk_update_platforms` to put the whole catalog on it, `set_sales_channel_live` to record the domain and switch the channel to LIVE, `create_tax_rate` after `list_tax_rates` for the rate, and `update_store` for the store's name, country, currency, default language and the two tax flags (`taxEnabled`, `pricesIncludeTax`). All of these are on the ChatGPT app's curated list. `get_store_setup_status`, the checklist of what the store still needs, is there too and is the one tool in this list that also works on an API key. + +⛔ **`list_categories`, `create_category`, `list_tags`, `create_tag`, `set_product_metafield`, `publish_product_to_channel`, `set_sales_channel_live`, `update_store`, `create_shipping_zone` and `create_shipping_rate` are all OAuth-only** (`bulk_update_platforms`, `list_tax_rates` and `create_tax_rate` are not, and work on a key with `products:write` or `tax:write`). On a connection made with a `brainerce_` API key every one of them fails with "This tool requires OAuth authentication and is not available via API key", which takes most of this list with it and makes Review mode's steps 3, 4 and 5 unrunnable. That is the connection, not the store: say so once and route the tree-building to the dashboard rather than retrying. + +The one route that survives an API-key connection is `create_product` with `categoryNames` and `tags`, which resolve or create by name case-insensitively without touching the taxonomy tools. It builds flat categories only, with no `parentId`, so it cannot build a nested tree. -**Dashboard only, and say so rather than improvising:** +**Full connector only (Claude Code, Cursor, Claude Desktop), so on the ChatGPT app these are a dashboard route:** -- Creating a custom-field definition, and its type, its allowed values, its Customer Input flag and its storefront-filter flag. -- Attributes and the variant editor beyond what `create_product` accepts. -- Modifier groups, in every respect. -- Regional pricing. -- Deleting anything at all. +- Creating a custom-field definition (`create_metafield_definition`), its type, its allowed values, its Customer Input flag and its storefront-filter flag. +- Attributes and their options (`create_attribute`, `create_attribute_option`, `assign_attributes_to_product`) and the variant tools beyond what `create_product` accepts. +- Modifier groups (`create_modifier_group`, `create_modifier`, `attach_modifier_group`). +- Regional pricing (`create_region`, `set_region_payment_providers`, `upsert_region_prices`). +- Editing or deleting shipping zones and rates, shipping classes, shipments and labels. +- Timezone, contact details, colours, logo and modules on `update_store`; `set_store_languages`; `activate_store`. +- Editing or deleting a tax rate, and tax classes. +- Installing and configuring apps, email templates and domains, webhooks, backups. +- Deleting anything at all: the delete tools exist on the full connector and none of them is on the ChatGPT app. + +**Dashboard only on every connection, and say so rather than improvising:** + +- Connecting a payment provider and putting it live. `install_app` can install one, and `get_store_setup_status` reports it, but the credentials, the OAuth consent and the switch to live are entered only in the dashboard, and a gateway installed from chat takes test cards until then. +- API keys, in every respect. +- Creating a webhook subscription and rotating its secret. +- Restoring a backup. +- Carrier credentials for a shipping app, and any other secret. +- Drawing a shipping zone on the map (polygon zones); the zone tools take countries, regions and postal codes only. ## Rules - Answer the modelling question before creating anything. A product created under the wrong model is a rebuild, not an edit. - Never propose a variant for a property that does not change price, stock or SKU. Say the arithmetic out loud when refusing. - Never propose a tag where the merchant described a menu. -- Read before you write. `list_categories` and `list_tags` first, every time, because this connector cannot delete the near-duplicate you create. +- Read before you write. `list_categories` and `list_tags` first, every time, because the ChatGPT app cannot delete the near-duplicate you create. - Give the number. "Too many variants" is ignored; "300 variants, 280 at the same price" is acted on. -- When the plan needs a dashboard step, name the exact place, and do not imply you did it. +- When the plan needs a dashboard step, name the exact place, and do not imply you did it. Keep the three buckets apart: on the ChatGPT app, on the full connector, dashboard only. "Not in my tool list" is the first two, never the third. +- Anything that spends money or emails a shopper (`buy_shipping_label`, `set_manual_tracking`, a currency change on a store with orders) gets the amount or the effect said out loud and a yes before the call. ## Reference files in this skill
Full technical diff · 2 changed fields
changed /description
"Decide how a Brainerce catalog should be structured, and diagnose one that is already wrong. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, how do I set up my menu, my product list is a mess, or I have hundreds of variants I cannot manage. Answers the modelling question before anything is created: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Also audits an existing catalog and returns an ordered improvement plan. For store owners, and for developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages, menus or blog, use brainerce-content-and-navigation. Keywords: catalog structure, variants or options, variant explosion, too many variants, categories vs tags, hierarchy, menu structure, product model, organise my shop, catalog audit."
"Decide how a Brainerce catalog should be structured, and audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, or I have too many variants to manage. Also owns store settings (currency, language, going live), shipping zones, rates, labels, sales-channel publishing, subscriptions, apps, email domains, webhooks and backups. Free shipping as a standing rate lives here; as a promotion it does not. For store owners and developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages or menus, use brainerce-content-and-navigation. Keywords: catalog structure, variant explosion, categories vs tags, shipping zones, sales channels, go live, currency, subscriptions, email domain, webhooks."
changed /skill_md_contents
"---\nname: brainerce-store-architecture\ndescription: \"Decide how a Brainerce catalog should be structured, and diagnose one that is already wrong. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, how do I set up my menu, my product list is a mess, or I have hundreds of variants I cannot manage. Answers the modelling question before anything is created: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Also audits an existing catalog and returns an ordered improvement plan. For store owners, and for developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages, menus or blog, use brainerce-content-and-navigation. Keywords: catalog structure, variants or options, variant explosion, too many variants, categories vs tags, hierarchy, menu structure, product model, organise my shop, catalog audit.\"\ncompatibility: ChatGPT, Claude Code, Claude Desktop, Cursor\nmaintainer: Brainerce\nmetadata:\n author: Brainerce\n version: \"1.0.0\"\n---\n\nGet the catalog model right the first time, or find out exactly where an existing one went wrong.\n\n## Core principle\n\nA Brainerce store has five different mechanisms for \"this product has a property\", and they are not interchangeable. Picking the wrong one is cheap on day one and expensive on day two hundred, because the fix means touching every product. So the modelling question comes before the create call, always.\n\nThe single test that resolves most of it: **does this property have its own stock count, its own price, or its own SKU?** If yes it is a variant. If no it is something else, and the rest of this skill says which.\n\n## When to use this skill first\n\n- \"How should I organise my shop\", \"what is the right structure for this\".\n- \"Should size be a variant or an option\", \"variant or custom field\".\n- \"Categories or tags\", \"how do I build my menu\".\n- \"I am about to add my first products\" and nothing exists yet.\n- \"I have 400 variants on one product and I cannot manage it\".\n- \"Review my catalog\", \"is my structure sensible\".\n\n## When NOT to use this skill first\n\n- Adding one product now, the model already settled: `brainerce-product-onboarding`.\n- Custom fields in depth, including writing values: `brainerce-custom-fields`.\n- Pages, navigation copy, policies, blog, SEO: `brainerce-content-and-navigation`.\n- A read-only sweep for drafts, empty stock and stuck orders: `brainerce-store-health`.\n- Building the storefront code that renders any of this: `brainerce-storefront-build`.\n\n---\n\n## The five mechanisms\n\n| Mechanism | What it is | Own stock? | Where it is set up |\n|---|---|---|---|\n| **Variant** (from an attribute) | One row per combination, each with its own price, SKU and inventory | Yes | Dashboard variant editor, or `variants` on `create_product` |\n| **Modifier group** | A choice at checkout that changes the price but not what you ship | No | Dashboard, Products then Modifiers |\n| **Custom field** (metafield) | Structured data on the product; 15 types; can be buyer-facing or a storefront filter | No | Definition in the dashboard, value from chat |\n| **Category** | The product's one permanent home in the menu tree | n/a | `create_category`, or the dashboard |\n| **Tag** | A flat label that cuts across the tree | n/a | `create_tag`, or the dashboard |\n\n## Want X, use Y\n\n| The merchant wants | The right mechanism |\n|---|---|\n| Shoppers pick between options and each option is counted, priced and shipped separately | Variant |\n| Shoppers pick an add-on that changes the total but not what leaves the warehouse | Modifier group |\n| Shoppers type or choose something personal, and it should print on the order | Custom field with Customer Input turned on |\n| Shoppers narrow the product list by a property | Custom field of type Select, Multi-select or Yes/No, with storefront filters turned on |\n| A spec that only needs to be displayed, never chosen or filtered | Custom field of any type |\n| A permanent place in the navigation menu | Category, nested under a parent |\n| A collection that cuts across the menu, or a seasonal grouping | Tag |\n| Who manufactured it | Brand |\n| A different price in another country | Regional pricing, dashboard |\n| The same catalog sold in more than one place | Sales channels |\n\nTwo of those rows are the ones merchants get wrong, and they have their own rules below.\n\n---\n\n## Rule 1, the variant rule\n\n**A variant exists only for a property that changes price, stock or SKU. Everything else is a custom field.**\n\nThe reason is arithmetic. Variants are the Cartesian product of their attributes, so every attribute multiplies. Three attributes with 6, 5 and 15 values is 450 rows, and each row wants its own price, its own SKU and its own stock number. Nobody maintains 450 rows. The catalog stops being edited, stock drifts, and the storefront starts offering combinations that do not exist.\n\n**The worked example, and it is the one to reach for.** A jewellery store sells a ring in several diamond sizes, several metal colours, and every ring size.\n\n- **Diamond size is a variant.** A bigger stone costs more. Price changes, so it earns its own row.\n- **Metal colour is a custom field, not a variant.** Same price, same stone, same SKU in most catalogs. Make it a Select custom field with Customer Input turned on, so the shopper still chooses it on the product page.\n- **Ring size is a custom field, not a variant.** Same reasoning, and it is the attribute that multiplies hardest.\n\nThat takes the product from 450 unmaintainable rows to 6 real ones plus two fields.\n\n**The honest exception, and say it out loud.** If the merchant genuinely holds separate stock per metal colour, boxed and on a shelf, then colour changes stock and it IS a variant after all. The rule is the test, not the answer. Ask \"if a customer buys the gold one, which number goes down\" before deciding.\n\n### Spotting a variation explosion\n\nYou are looking at one when any of these is true:\n\n- A product has more variant rows than the merchant could plausibly count in a stock take.\n- Most variant rows carry the same price. A property that never changes the price is not earning its row.\n- Whole blocks of rows sit at zero stock and always have, because those combinations were generated but never existed.\n\nThe fix to propose: keep the one attribute that moves the price, and demote the others to custom fields with Customer Input. Say plainly that this is a rebuild of that product rather than an edit, and that the variant editor is a dashboard job.\n\n## Rule 2, the navigation rule\n\n**A hierarchical menu is parent and child categories. Tags are for filtering across the tree, never for hierarchy.**\n\n\"Mobile\" as a parent, with \"iPhone\" and \"Samsung\" as children, is how a shopper walks down to what they want. Each category gets its own landing page and a breadcrumb on every product in it. Two or three levels is the practical limit; `create_category` accepts `parentId` and the storefront is built around depth two.\n\nTags are flat by design. They have no landing page and no children. `summer-sale`, `gift-under-50` and `bestseller` are tags because they cut sideways across the menu and because they change with the season, while the menu should not.\n\nThe test: **would removing this label leave the product with no home in the menu?** If yes it is a category. If the product still has a home and this was just another way to group it, it is a tag.\n\nA product normally sits in one category. Putting it in several creates duplicate navigation entries and a breadcrumb that cannot decide what it is.\n\n### Spotting a flat catalog\n\n- `list_categories` returns many categories and none of them has a parent. A store with thirty top-level categories has a menu nobody can read.\n- The same words appear as both a category and a tag.\n- Tags carry the structural nouns (\"phones\", \"laptops\") while categories carry the seasonal ones. That is the two mechanisms swapped.\n\n---\n\n## Review mode, auditing a catalog that already exists\n\nRead only. Diagnose, present the plan, then let the merchant pick. Do not restructure anything mid-audit.\n\n1. **`count_products`** first, so you know whether a sample or the whole catalog is being judged, and say which.\n2. **`list_products`**, paging through it. Each product comes back with its variants and its custom-field values, so this is where variation explosion and empty modelling both show up. Note the variant count per product and whether the prices differ across those variants.\n3. **`list_categories`**, for the tree. Count how many have a parent. None with a parent on a large catalog is the flat-catalog finding.\n4. **`list_tags`**, and compare the names against the category names. Overlap is the swapped-mechanism finding.\n5. **`list_product_metafields`** on a few representative products, to see which custom fields are actually in use and which products are missing the ones their neighbours have.\n\nThen report against the four failure modes, most costly first:\n\n- **Too many variants on a product**, with the number and which attribute is not earning its rows.\n- **A flat category list**, with the tree you would propose.\n- **Tags doing a category's job**, naming the specific tags.\n- **A property that should be a custom field and is not recorded anywhere**, which is usually the one costing the merchant filter traffic.\n\n**Use this shape:**\n\n> ✓ I went through 240 products. Three things worth changing, most costly first:\n>\n> 1. **Solitaire Ring has 300 variants** and 280 of them are the same price. Only diamond size moves the price. Metal colour and ring size should be custom fields the shopper picks on the page, which takes it to 6 rows.\n> 2. **All 34 of your categories are top level.** There is no menu to walk down. I would nest them under Rings, Necklaces and Earrings.\n> 3. **`rings` and `necklaces` exist as tags as well as categories.** The tags are doing nothing the categories do not, and they split your filters in two.\n>\n> Also fine: every product has a category, and your custom fields are used consistently across the Rings range.\n>\n> Number 2 is the one I can do from here. Want me to build that tree?\n\n## Planning mode, before the first product exists\n\nFour questions, then a structure. Do not create anything until the merchant has seen the plan.\n\n1. **What are you selling, and roughly how many lines?**\n2. **For one typical product, what does a shopper choose before buying?** List every choice.\n3. **Which of those choices changes the price, or is counted separately in your stockroom?** Those are the variants. Everything else on the list is a custom field.\n4. **How would a shopper browse to it if they did not use search?** That answer is the category tree.\n\nThen hand back: the category tree with parents and children, the attribute or attributes that become variants, the custom fields with their types and whether each is buyer-facing or filterable, and any modifier groups. Say which parts you can build now and which are dashboard steps.\n\n## What can be done from here, and what cannot\n\n**From this conversation:**\n\n- `create_category` with `parentId`, so the whole tree can be built from chat.\n- `create_tag`, after `list_tags` to avoid a near-duplicate.\n- `create_product`, including its `variants` array.\n- `update_product` to move products between categories. ⛔ `categories` REPLACES the set while `categoryNames` is additive, so read the product first and know which one you want.\n- `set_product_metafield` to write a custom-field value, but only for a field some product already carries. See `brainerce-custom-fields` for why.\n\n**Dashboard only, and say so rather than improvising:**\n\n- Creating a custom-field definition, and its type, its allowed values, its Customer Input flag and its storefront-filter flag.\n- Attributes and the variant editor beyond what `create_product` accepts.\n- Modifier groups, in every respect.\n- Regional pricing.\n- Deleting anything at all.\n\n## Rules\n\n- Answer the modelling question before creating anything. A product created under the wrong model is a rebuild, not an edit.\n- Never propose a variant for a property that does not change price, stock or SKU. Say the arithmetic out loud when refusing.\n- Never propose a tag where the merchant described a menu.\n- Read before you write. `list_categories` and `list_tags` first, every time, because this connector cannot delete the near-duplicate you create.\n- Give the number. \"Too many variants\" is ignored; \"300 variants, 280 at the same price\" is acted on.\n- When the plan needs a dashboard step, name the exact place, and do not imply you did it.\n\n## Reference files in this skill\n\n- `references/mechanisms.md`: all five mechanisms at field level, including the attribute display types, the modifier group selection and free-quantity rules, and the two custom-field flags. Read it when an argument turns on a detail rather than on the choice.\n\n## Cross-skill connections\n\n- Custom fields in depth, including writing values: `brainerce-custom-fields`.\n- Pages, navigation copy, policies and blog: `brainerce-content-and-navigation`.\n- Creating the products once the model is settled: `brainerce-product-onboarding`.\n- Rendering any of this in a storefront: `brainerce-storefront-build`.\n- The exact SDK shape of a category tree or a metafield: `brainerce-sdk`.\n\nRoute once, and do not bounce back and forth.\n"
"---\nname: brainerce-store-architecture\ndescription: 'Decide how a Brainerce catalog should be structured, and audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, or I have too many variants to manage. Also owns store settings (currency, language, going live), shipping zones, rates, labels, sales-channel publishing, subscriptions, apps, email domains, webhooks and backups. Free shipping as a standing rate lives here; as a promotion it does not. For store owners and developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages or menus, use brainerce-content-and-navigation. Keywords: catalog structure, variant explosion, categories vs tags, shipping zones, sales channels, go live, currency, subscriptions, email domain, webhooks.'\ncompatibility: ChatGPT, Claude Code, Claude Desktop, Cursor\nmaintainer: Brainerce\nmetadata:\n author: Brainerce\n version: '1.0.0'\n---\n\n⛔ **Before you call anything a dashboard task, look for `search_tools` in your tool list.** Some connections carry a small curated list PLUS a way to reach everything else. Where `search_tools` exists, search for what the merchant asked for and call any hit through the `call_read_tool` / `call_write_tool` / `call_destructive_tool` it names. The dashboard is the honest answer only when your list has no `search_tools`, or the search comes back empty. A few things are refused through it deliberately (payment provider setup, refunds and recording payments, gift cards), which is why an empty result there is a real answer rather than a gap.\n\nGet the catalog model right the first time, or find out exactly where an existing one went wrong.\n\n## Core principle\n\nA Brainerce store has five different mechanisms for \"this product has a property\", and they are not interchangeable. Picking the wrong one is cheap on day one and expensive on day two hundred, because the fix means touching every product. So the modelling question comes before the create call, always.\n\nThe single test that resolves most of it: **does this property have its own stock count, its own price, or its own SKU?** If yes it is a variant. If no it is something else, and the rest of this skill says which.\n\n## When to use this skill first\n\n- \"How should I organise my shop\", \"what is the right structure for this\".\n- \"Should size be a variant or an option\", \"variant or custom field\".\n- \"Categories or tags\", \"how do I build my menu\".\n- \"I am about to add my first products\" and nothing exists yet.\n- \"I have 400 variants on one product and I cannot manage it\".\n- \"Review my catalog\", \"is my structure sensible\".\n\n## When NOT to use this skill first\n\n- Adding one product now, the model already settled: `brainerce-product-onboarding`.\n- Custom fields in depth, including writing values: `brainerce-custom-fields`.\n- Pages, navigation copy, policies, blog, SEO: `brainerce-content-and-navigation`.\n- A read-only sweep for drafts, empty stock and stuck orders: `brainerce-store-health`.\n- Building the storefront code that renders any of this: `brainerce-storefront-build`.\n\n---\n\n## The five mechanisms\n\nWhere a tool is named below, \"full connector\" means Claude Code, Cursor and Claude Desktop connected to the Admin MCP, which lists every tool; the ChatGPT app carries a curated list, and each section says which of its tools are on it.\n\n| Mechanism | What it is | Own stock? | Where it is set up |\n| ------------------------------- | ------------------------------------------------------------------------------------ | ---------- | ----------------------------------------------------------- |\n| **Variant** (from an attribute) | One row per combination, each with its own price, SKU and inventory | Yes | Dashboard variant editor, or `variants` on `create_product` |\n| **Modifier group** | A choice at checkout that changes the price but not what you ship | No | Dashboard, Products then Modifiers |\n| **Custom field** (metafield) | Structured data on the product; 15 types; can be buyer-facing or a storefront filter | No | Definition in the dashboard, value from chat |\n| **Category** | The product's one permanent home in the menu tree | n/a | `create_category`, or the dashboard |\n| **Tag** | A flat label that cuts across the tree | n/a | `create_tag`, or the dashboard |\n\n## Want X, use Y\n\n| The merchant wants | The right mechanism |\n| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| Shoppers pick between options and each option is counted, priced and shipped separately | Variant |\n| Shoppers pick an add-on that changes the total but not what leaves the warehouse | Modifier group |\n| Shoppers type or choose something personal, and it should print on the order | Custom field with Customer Input turned on |\n| Shoppers narrow the product list by a property | Custom field of type Select, Multi-select or Yes/No, with storefront filters turned on |\n| A spec that only needs to be displayed, never chosen or filtered | Custom field of any type |\n| A permanent place in the navigation menu | Category, nested under a parent |\n| A collection that cuts across the menu, or a seasonal grouping | Tag |\n| Who manufactured it | Brand |\n| A different price in another country | Regional pricing: `create_region`, then `upsert_region_prices`, full connector |\n| The same catalog sold in more than one place | Sales channels |\n\nTwo of those rows are the ones merchants get wrong, and they have their own rules below.\n\n---\n\n## Rule 1, the variant rule\n\n**A variant exists only for a property that changes price, stock or SKU. Everything else is a custom field.**\n\nThe reason is arithmetic. Variants are the Cartesian product of their attributes, so every attribute multiplies. Three attributes with 6, 5 and 15 values is 450 rows, and each row wants its own price, its own SKU and its own stock number. Nobody maintains 450 rows. The catalog stops being edited, stock drifts, and the storefront starts offering combinations that do not exist.\n\n**The worked example, and it is the one to reach for.** A jewellery store sells a ring in several diamond sizes, several metal colours, and every ring size.\n\n- **Diamond size is a variant.** A bigger stone costs more. Price changes, so it earns its own row.\n- **Metal colour is a custom field, not a variant.** Same price, same stone, same SKU in most catalogs. Make it a Select custom field with Customer Input turned on, so the shopper still chooses it on the product page.\n- **Ring size is a custom field, not a variant.** Same reasoning, and it is the attribute that multiplies hardest.\n\nThat takes the product from 450 unmaintainable rows to 6 real ones plus two fields.\n\n**The honest exception, and say it out loud.** If the merchant genuinely holds separate stock per metal colour, boxed and on a shelf, then colour changes stock and it IS a variant after all. The rule is the test, not the answer. Ask \"if a customer buys the gold one, which number goes down\" before deciding.\n\n### Spotting a variation explosion\n\nYou are looking at one when any of these is true:\n\n- A product has more variant rows than the merchant could plausibly count in a stock take.\n- Most variant rows carry the same price. A property that never changes the price is not earning its row.\n- Whole blocks of rows sit at zero stock and always have, because those combinations were generated but never existed.\n\nThe fix to propose: keep the one attribute that moves the price, and demote the others to custom fields with Customer Input. Say plainly that this is a rebuild of that product rather than an edit, and that the variant editor is a dashboard job.\n\n## Rule 2, the navigation rule\n\n**A hierarchical menu is parent and child categories. Tags are for filtering across the tree, never for hierarchy.**\n\n\"Mobile\" as a parent, with \"iPhone\" and \"Samsung\" as children, is how a shopper walks down to what they want. Each category gets its own landing page and a breadcrumb on every product in it. Two or three levels is the practical limit; `create_category` accepts `parentId` and the storefront is built around depth two.\n\nTags are flat by design. They have no landing page and no children. `summer-sale`, `gift-under-50` and `bestseller` are tags because they cut sideways across the menu and because they change with the season, while the menu should not.\n\nThe test: **would removing this label leave the product with no home in the menu?** If yes it is a category. If the product still has a home and this was just another way to group it, it is a tag.\n\nA product normally sits in one category. Putting it in several creates duplicate navigation entries and a breadcrumb that cannot decide what it is.\n\n### Spotting a flat catalog\n\n- `list_categories` returns many categories and none of them has a parent. A store with thirty top-level categories has a menu nobody can read.\n- The same words appear as both a category and a tag.\n- Tags carry the structural nouns (\"phones\", \"laptops\") while categories carry the seasonal ones. That is the two mechanisms swapped.\n\n---\n\n## Review mode, auditing a catalog that already exists\n\nRead only. Diagnose, present the plan, then let the merchant pick. Do not restructure anything mid-audit.\n\n1. **`count_products`** first, so you know whether a sample or the whole catalog is being judged, and say which.\n2. **`list_products`**, paging through it. Each product comes back with its variants and its custom-field values, so this is where variation explosion and empty modelling both show up. Note the variant count per product and whether the prices differ across those variants.\n3. **`list_categories`**, for the tree. Count how many have a parent. None with a parent on a large catalog is the flat-catalog finding.\n4. **`list_tags`**, and compare the names against the category names. Overlap is the swapped-mechanism finding.\n5. **`list_product_metafields`** on a few representative products, to see which custom fields are actually in use and which products are missing the ones their neighbours have.\n\nThen report against the four failure modes, most costly first:\n\n- **Too many variants on a product**, with the number and which attribute is not earning its rows.\n- **A flat category list**, with the tree you would propose.\n- **Tags doing a category's job**, naming the specific tags.\n- **A property that should be a custom field and is not recorded anywhere**, which is usually the one costing the merchant filter traffic.\n\n**Use this shape:**\n\n> ✓ I went through 240 products. Three things worth changing, most costly first:\n>\n> 1. **Solitaire Ring has 300 variants** and 280 of them are the same price. Only diamond size moves the price. Metal colour and ring size should be custom fields the shopper picks on the page, which takes it to 6 rows.\n> 2. **All 34 of your categories are top level.** There is no menu to walk down. I would nest them under Rings, Necklaces and Earrings.\n> 3. **`rings` and `necklaces` exist as tags as well as categories.** The tags are doing nothing the categories do not, and they split your filters in two.\n>\n> Also fine: every product has a category, and your custom fields are used consistently across the Rings range.\n>\n> Number 2 is the one I can do from here. Want me to build that tree?\n\n## Planning mode, before the first product exists\n\nFour questions, then a structure. Do not create anything until the merchant has seen the plan.\n\n1. **What are you selling, and roughly how many lines?**\n2. **For one typical product, what does a shopper choose before buying?** List every choice.\n3. **Which of those choices changes the price, or is counted separately in your stockroom?** Those are the variants. Everything else on the list is a custom field.\n4. **How would a shopper browse to it if they did not use search?** That answer is the category tree.\n\nThen hand back: the category tree with parents and children, the attribute or attributes that become variants, the custom fields with their types and whether each is buyer-facing or filterable, and any modifier groups. Say which parts you can build now and which are dashboard steps.\n\n## Shipping zones and rates\n\nWhere you deliver, and what you charge for it. This is **configuration, not a promotion**: a merchant asking \"how much do I charge to ship to Europe\" or \"which countries can I sell to\" is here, not in `brainerce-launch-a-sale`. That skill only lays a free-shipping offer on top of rates that already exist, so if there are no rates, there is nothing for it to discount.\n\nThe model is two levels, and merchants get it wrong in one direction:\n\n- A **zone** is a place: a country, a set of countries, a region. `list_shipping_zones`, `get_shipping_zone`, `create_shipping_zone`, `update_shipping_zone`, `delete_shipping_zone`.\n- A **rate** is a price inside a zone: flat, by weight, by order value, or free over a threshold. `list_shipping_rates`, `create_shipping_rate`, `update_shipping_rate`, `delete_shipping_rate`.\n\n**What a rate's `rateConfig` must look like, per type.** FLAT_RATE `{ amount }`; WEIGHT_BASED `{ baseRate, perKg, maxWeight? }`; PRICE_BASED `{ tiers: [{ minOrderAmount, rate }] }`; FREE `{ minOrderAmount? }`; LOCAL_PICKUP `{ amount, pickupLocation: { name, line1, city, country, ... } }`. All amounts are plain numbers of 0 or more. A config that does not match the type is refused, so read the error and fix the shape; do not retry the same call. Changing a rate's `type` needs its new `rateConfig` in the same call.\n\n**A delivery area drawn on a map.** `update_shipping_zone` takes `geometry` (GeoJSON Polygon or MultiPolygon, coordinates in [longitude, latitude] order, every ring closed), optional `geometryLabels` (display names, one per shape), and `salesChannelIds` to offer the zone on certain channels only. A zone matches an address that is inside its country list OR inside its drawn shape. Create the zone first, then update it to add the shape. An invalid shape is refused; `null` removes it. `calculate_shipping` shows what a buyer at an address would be offered and why a zone did or did not match.\n\nSo \"free delivery over 200 shekels in Israel\" is a rate on the Israel zone, not a discount rule. Reach for a `SHIPPING_DISCOUNT` only when the merchant wants a temporary offer over the top of their normal rates, and say which one you built.\n\n⛔ **Every shipping zone, rate and class tool is OAuth-only, and so are `calculate_shipping` and `set_manual_tracking`.** On a `brainerce_` API-key connection they refuse outright; the other shipment and label tools work on either credential once a shipping app is connected. The ChatGPT app's curated list carries three of them since 2026-09-15: `list_shipping_zones`, `create_shipping_zone` and `create_shipping_rate`, which is enough to add basic shipping from chat: a zone for the destination countries (`[\"*\"]` is worldwide), then at least one rate on it, because a zone with no rate offers nothing at checkout. Editing or deleting a zone or a rate, and everything below, stays on the full connector. Check your tool list, and route to the dashboard's Shipping settings otherwise.\n\n**A country with no zone cannot be checked out to.** That is the finding worth surfacing in an audit: a merchant who says \"customers abroad cannot buy\" usually has no zone covering them.\n\n### Shipping classes, and the two diagnostics\n\nA **shipping class** is a tag a product carries (\"Oversized\", \"Fragile\") so a rate can charge it differently. On the full connector: `list_shipping_classes`, `create_shipping_class`, `update_shipping_class`, `delete_shipping_class`. Creating a class changes nothing on its own: a product has to carry it (`update_product` with `shippingClassId`) and a rate has to price it (`classCosts` on `create_shipping_rate` or `update_shipping_rate`), so say both steps when you create one. Deleting a class unassigns every product that carried it, and the products then ship at the rate's base cost.\n\nTwo read-only tools answer the questions merchants actually ask:\n\n- `calculate_shipping` runs the store's own zones and rates for an address exactly as checkout does and returns the offered rates plus a verdict per zone saying why it did or did not match. It is the tool for \"why does my customer see no shipping methods\". It is not a live carrier quote.\n- `check_free_shipping` compares, per sales channel, the free-shipping threshold the storefront bar promises with any FREE-type rate that has its own minimum. `hasMismatch: true` is the case where the bar says one figure and checkout charges by another.\n\n### Shipments and labels\n\nSeparate from zones and rates, which are what the shopper pays, a **shipment** is the parcel record: carrier, tracking number, label. On the full connector `list_shipments`, `get_shipment` and `get_shipment_tracking` read them, `create_shipment` records a parcel the merchant hands to a carrier themselves, `set_manual_tracking` attaches a tracking number and ships the order (and emails the customer; there is no quiet mode on that path), and `cancel_shipment` voids a bought label at the carrier. `get_ship_from_origin` and `save_ship_from_origin` hold the address every quote and label is sent from; without a complete one no carrier can quote. `validate_shipping_address` asks the carrier whether an address is deliverable.\n\n`get_order_shipping_rates` is a live carrier quote for one order, and `buy_shipping_label` quotes first and buys only with `confirm: true`, charging the store's carrier account. Carrier credentials are entered in the dashboard, never through the connector. Shipment tools work only once a shipping app is connected; before that they refuse, and the refusal is the connection, not the order. Always show the merchant the amount and carrier before passing `confirm`. None of the shipment or label tools are on the ChatGPT app.\n\n## Selling the same catalog in more than one place\n\nSales channels are how one catalog reaches a storefront, a marketplace and a social shop without duplicating products. On the full connector `list_sales_channels`, `get_sales_channel`, `create_sales_channel`, `update_sales_channel` and `test_sales_channel_connection` manage the channels themselves.\n\nPublishing is separate from creating, and this is the part merchants miss: **a product is not on a channel until it is published to one.** `publish_product_to_channel` and `publish_product_to_channels` do that, `unpublish_product_from_channel` reverses it, and `get_product_channel_publishes` says where a product is currently live. `get_product_channel_overlay` and `save_product_channel_overlay` let one product carry a different title or price per channel. Coupons and taxonomy publish the same way, with `publish_coupon_to_channel` and `publish_taxonomy_to_channel`.\n\n**Naming the channel.** Every one of these tools (and `get_sales_channel`, `update_sales_channel`, `delete_sales_channel`, `test_sales_channel_connection`, `set_store_member_sales_channels`, `invite_store_member`) takes the channel as the `id` from `list_sales_channels`, **or** its public `vc_*` connection id, **or** its exact name — any of the three, and the plural tools accept a mix. Use whichever the merchant gave you; do not translate a `vc_*` id or a name into the record id first. A reference that matches none of them errors naming it, and the fix is to read `list_sales_channels` rather than guess.\n\nProducts a storefront run seeded through the device-auth window (`--seed-products`) arrive already published to that channel; the seed report says `published: false` for any that did not, and those are published from here or the dashboard, never re-created.\n\n`list_products` takes a `salesChannelId` filter, so \"what is actually live on my Instagram shop\" is answerable.\n\nOn the ChatGPT app's curated list: `list_sales_channels`, `create_sales_channel`, `publish_product_to_channel`, and since 2026-09-15 `bulk_update_platforms` and `set_sales_channel_live`. Unpublishing one product, the plural publish, per-channel overlays, and coupon and taxonomy publishing stay on the full connector.\n\n### Tracking ids and the signup form on a channel\n\n`update_sales_channel` also sets the channel's tracking ids: `gtmContainerId` (GTM-ABC1234), `ga4MeasurementId` (G-ABC1234XYZ), `metaPixelId` (10 to 20 digits) and `tiktokPixelId`, plus `googleSiteVerification`. An empty string clears one and the channel falls back to the id discovered from its connected app. A malformed id is refused, because it would otherwise be written into the storefront page. `requireBirthday: true` makes shoppers give a birthday (month and day, never a year) when they register; leave it off unless the merchant asks, because it works better as an incentive for a birthday gift than as a gate.\n\n### Publish the whole catalog at once\n\nPublish a catalog: `bulk_update_platforms({ filter: { status: 'active' }, vcPublishTo: [channel] })`; `vcPublishTo` accepts the channel id, its `vc_` id or its name. `filter: { status: 'active' }` is the \"all my products\" form; `filter: {}` alone is refused, and so is a call with neither `productIds` nor `filter`. It changes visibility only, `vcUnpublishFrom` undoes it, and a large catalog is queued: the result then carries `{ queued: true, jobId }`, so report it as in progress and poll `get_bulk_job_status` rather than calling it again. For one product `publish_product_to_channel` is the smaller call.\n\n### Going live\n\nGoing live: `set_sales_channel_live({ channelId, domain, live: true })` records the public https domain and switches the channel to LIVE; the payment provider is still connected in the dashboard. A TEST channel takes no real orders. The domain must already point at the deployed storefront, because this records it and deploys nothing; the call is refused, with nothing changed, when `live: true` and no valid domain exists. A LIVE channel with a payment provider still in TEST mode declines real cards, so read `get_store_setup_status` after flipping it and say what is still missing. `live: false` puts the channel back into TEST. Do not repeat a full `vc_` id into the conversation; the result carries only its last four characters. Every other channel setting is `update_sales_channel`, full connector only.\n\n### What a channel is connected to\n\nOn the full connector, `get_sales_channel_integrations` says which marketing connectors (Google Merchant Center, Meta, TikTok) are bound to a channel and how their last sync went, which is the read for \"my products are not on Google\". It also carries the Google Customer Reviews opt-in state (connected Merchant ID + whether the merchant confirmed the opt-in) — Google's own verified-buyer survey feeding the seller rating, not the same thing as Brainerce's storefront product-review feature. `sync_sales_channel_now` re-pushes every published product to those connectors in the background; it is skipped on a TEST channel, because connectors only receive LIVE ones. `get_sales_channel_tracking` shows the GA4, GTM, Meta and TikTok tag ids the storefront serves, public ids only. `set_product_channel_app_visibility` hides one product from a connector app (Google Merchant Center today) without unpublishing it from the storefront. `get_connection_health` rolls up every channel and installed app with its last ping and last error.\n\n⛔ **`create_product({ platforms })` is not a publish path.** It expects a channel record id behind a literal `vc:` prefix; a `vc_` connection id or a channel name matches nothing, the call still succeeds, and the product stays unpublished. `update_product` has no `platforms` field. Publish with `publish_product_to_channel`, which takes any of the three channel references above.\n\n## Subscriptions and recurring plans\n\nA store can sell on a repeating charge rather than one payment. On the full connector this is `list_recurring_plans`, `create_recurring_plan` and `update_recurring_plan` for what is offered, and `list_recurring_subscriptions` plus `arm_recurring_subscription`, `pause_recurring_subscription`, `resume_recurring_subscription` and `cancel_recurring_subscription` for individual customers. `get_recurring_plan_armability` says whether a plan can actually start charging, and it is the thing to read before promising a merchant it is live. Not on the ChatGPT app's curated list.\n\n## Store settings, currency and language\n\n`update_store` is Settings, General from chat. On the ChatGPT app it takes the store's name, country, currency, default language and the two tax flags (`taxEnabled`, `pricesIncludeTax`); on the full connector it also takes timezone, discount stacking mode, pricing-formula mode, contact email and phone, social links, brand colours, logo (an asset from `ingest_media_asset`, never an external URL), homepage meta description, the review flags and which dashboard modules are enabled. Omitted fields are unchanged.\n\n`update_store` changes currency and language too: currency relabels every price (nothing is converted), is refused while a checkout is pending and needs `confirm: true` once the store has orders; language seeds email templates. `set_store_languages` adds or removes extra storefront languages; `activate_store` moves a store that sells without a payment provider from SETUP to ACTIVE.\n\nSay the relabelling out loud before you touch currency: 100 ILS becomes 100 EUR, on every product, sale price, shipping rate, coupon amount and gift-card balance, and the default region follows the store in the same change. A merchant who says \"switch me to euros\" on a store with prices almost never means that, so ask what they want the prices to be first. On language, every product or category text without a translation is then shown as the new language, not translated; ten languages are supported (en, he, ar, es, fr, de, ja, zh, ru, pt). Removing a language with `set_store_languages` deletes that language's email templates and any edits to them, which cannot be undone.\n\n`set_store_languages` also takes `autoTranslateNewProducts` (translate a new product into every supported language) and `nonDefaultLocaleStrategy` (what happens to AI-written content when the merchant writes in a language other than the default: `translate-to-default`, `keep-input`, `ask` or `single-language`).\n\nThe business identity printed on purchase orders is not on `update_store`: use `update_store_business_profile` for `legalName`, `taxId` and `businessAddress` (null clears each), and for the reporting settings `analyticsCurrencyMode` (NATIVE groups revenue per currency; AGGREGATE converts into `reportingCurrency`) and `fxProvider` (ECB, OPENEXCHANGE or MANUAL). It is for the document identity and reporting only: the postal address on marketing emails is set in the dashboard under Settings, Email.\n\n`taxEnabled: true` only switches the flag on. With no rate checkout charges 0 tax. The rate is `create_tax_rate`, on the ChatGPT app since 2026-09-15 alongside `list_tax_rates`: read the list first and refuse a duplicate country and region pair, take the percentage the merchant gave (18 means 18%) and never one you assume, and always a country (there is no rate that applies everywhere; a country with no rate pays 0 tax), because the rate is the merchant's legal number and the tool registers nothing with any authority. Editing and deleting rates, the Canada preset, and tax classes are full connector only.\n\n`activate_store` is idempotent and is only for a store that sells without a payment provider (cash on delivery, bank transfer): the dashboard activates a store the moment a provider goes live, and checkout does not gate on the status, so activating does not make a store ready to sell. `get_store_setup_status` is still the checklist.\n\n⛔ `update_store`, `set_store_languages` and `activate_store` are OAuth-only; `get_store_setup_status` and the tax-rate tools work on an API key too. On the ChatGPT app `update_store` carries only the fields named above; timezone, contact details, colours, logo and modules are full-connector work there, not dashboard work.\n\n## Apps\n\nOn the full connector `browse_marketplace_apps` and `get_marketplace_app` find an app, `install_app` installs it by slug, `configure_app` sets its non-secret settings, and `suspend_app`, `resume_app`, `disconnect_app` and `uninstall_app` manage it afterwards. `trigger_app_sync` queues a full import from a connector such as Shopify or WooCommerce; `get_app_sync_status` and `get_app_activity` report on it.\n\nThe assistant can install an app and change its non-secret settings, but it can never enter or read an API key, password or token; those are entered only in the dashboard, and a payment gateway installed from chat takes test cards only until you connect it live there. `install_app` creates the installation only: OAuth consent, provider credentials and KYC are finished by the merchant under Apps in the dashboard, and `configure_app` refuses by name any key, token, password or the test/live switch of a payment app. Say the dashboard step in the same breath as the install, and never report a payment provider as live because it is installed. `install_app`, `sync_sales_channel_now`, `get_sales_channel_tracking` and `get_sales_channel_integrations` are OAuth-only; the rest work on an API key too. None of the app tools are on the ChatGPT app.\n\n## Email sending and domains\n\nTransactional emails (order confirmation, shipped, and the rest) are templates per event and per language. On the full connector `list_email_templates`, `get_email_template` and `update_email_template` edit them, `preview_email_template` renders one with sample data without sending, `list_email_template_versions` and `restore_email_template_version` roll one back, `reset_email_template` and `reset_all_email_templates` return to the built-in design (destructive; the second needs `confirm: true`), and `create_email_template` and `delete_email_template` exist for a second variant of one event, which is rare. `send_test_email` sends only to the signed-in merchant's own address; there is no recipient argument and it is never a way to email a customer. `list_email_logs` is the send log with delivery status per message.\n\nA custom sending domain lets the store send from its own address instead of the shared Brainerce one. `create_email_domain` registers it and returns the DNS records the merchant adds at their registrar (public values, show them verbatim), `list_email_domains` shows verification status, and `delete_email_domain` removes one. `verify_email_domain` looks each record up in public DNS and puts a `check` on every record in `dnsRecords` (found, missing, wrong, proxied, doubled, duplicate or unknown), so tell the merchant exactly which record to fix; `unknown` means the lookup could not run, not that the record is missing. Approval itself happens on the provider's schedule once every record is found, so a call made right after the records were added can still say PENDING; tell the merchant to wait rather than looping. Once VERIFIED, select the domain in `update_email_settings`. Every domain tool is OAuth-only, and none of the email tools are on the ChatGPT app.\n\n## Webhooks and backups\n\nWebhooks are developer plumbing, but merchants ask \"why did my integration stop getting orders\". On the full connector `get_webhook_health` is the one-glance answer, `list_webhook_subscriptions` and `list_webhook_deliveries` show the detail, `update_webhook_subscription` edits the URL or events (the list replaces the whole set) and pauses or resumes one, and `isActive: true` on a tripped subscription resets its circuit breaker. `send_test_webhook_event` queues a signed test event, and `delete_webhook_subscription` removes one for good. Creating a webhook subscription and rotating its secret are dashboard-only because both display the signing secret once. No tool ever returns the secret.\n\nBackups: `create_store_export` starts a background export (a full backup when `scope` is omitted), `list_store_exports` and `get_export_status` follow it, and the download link on a completed export is one the merchant opens in a browser. `get_backup_schedule`, `upsert_backup_schedule` and `delete_backup_schedule` manage the automatic schedule. Restoring a backup is dashboard-only; the tools create, list, link and delete exports. Only one export can run per store at a time, so a conflict means check the list rather than retry. None of the webhook or backup tools are on the ChatGPT app.\n\n## What can be done from here, and what cannot\n\n**From this conversation:**\n\n- `create_category` with `parentId`, so the whole tree can be built from chat.\n- `create_tag`, after `list_tags` to avoid a near-duplicate.\n- `create_product`, including its `variants` array.\n- `update_product` to move products between categories. ⛔ `categories` REPLACES the set while `categoryNames` is additive, so read the product first and know which one you want.\n- `set_product_metafield` to write a custom-field value, but only for a field some product already carries. See `brainerce-custom-fields` for why.\n- `create_shipping_zone` and `create_shipping_rate` for basic shipping, `publish_product_to_channel` to put one product on a storefront and `bulk_update_platforms` to put the whole catalog on it, `set_sales_channel_live` to record the domain and switch the channel to LIVE, `create_tax_rate` after `list_tax_rates` for the rate, and `update_store` for the store's name, country, currency, default language and the two tax flags (`taxEnabled`, `pricesIncludeTax`). All of these are on the ChatGPT app's curated list. `get_store_setup_status`, the checklist of what the store still needs, is there too and is the one tool in this list that also works on an API key.\n\n⛔ **`list_categories`, `create_category`, `list_tags`, `create_tag`, `set_product_metafield`, `publish_product_to_channel`, `set_sales_channel_live`, `update_store`, `create_shipping_zone` and `create_shipping_rate` are all OAuth-only** (`bulk_update_platforms`, `list_tax_rates` and `create_tax_rate` are not, and work on a key with `products:write` or `tax:write`). On a connection made with a `brainerce_` API key every one of them fails with \"This tool requires OAuth authentication and is not available via API key\", which takes most of this list with it and makes Review mode's steps 3, 4 and 5 unrunnable. That is the connection, not the store: say so once and route the tree-building to the dashboard rather than retrying.\n\nThe one route that survives an API-key connection is `create_product` with `categoryNames` and `tags`, which resolve or create by name case-insensitively without touching the taxonomy tools. It builds flat categories only, with no `parentId`, so it cannot build a nested tree.\n\n**Full connector only (Claude Code, Cursor, Claude Desktop), so on the ChatGPT app these are a dashboard route:**\n\n- Creating a custom-field definition (`create_metafield_definition`), its type, its allowed values, its Customer Input flag and its storefront-filter flag.\n- Attributes and their options (`create_attribute`, `create_attribute_option`, `assign_attributes_to_product`) and the variant tools beyond what `create_product` accepts.\n- Modifier groups (`create_modifier_group`, `create_modifier`, `attach_modifier_group`).\n- Regional pricing (`create_region`, `set_region_payment_providers`, `upsert_region_prices`).\n- Editing or deleting shipping zones and rates, shipping classes, shipments and labels.\n- Timezone, contact details, colours, logo and modules on `update_store`; `set_store_languages`; `activate_store`.\n- Editing or deleting a tax rate, and tax classes.\n- Installing and configuring apps, email templates and domains, webhooks, backups.\n- Deleting anything at all: the delete tools exist on the full connector and none of them is on the ChatGPT app.\n\n**Dashboard only on every connection, and say so rather than improvising:**\n\n- Connecting a payment provider and putting it live. `install_app` can install one, and `get_store_setup_status` reports it, but the credentials, the OAuth consent and the switch to live are entered only in the dashboard, and a gateway installed from chat takes test cards until then.\n- API keys, in every respect.\n- Creating a webhook subscription and rotating its secret.\n- Restoring a backup.\n- Carrier credentials for a shipping app, and any other secret.\n- Drawing a shipping zone on the map (polygon zones); the zone tools take countries, regions and postal codes only.\n\n## Rules\n\n- Answer the modelling question before creating anything. A product created under the wrong model is a rebuild, not an edit.\n- Never propose a variant for a property that does not change price, stock or SKU. Say the arithmetic out loud when refusing.\n- Never propose a tag where the merchant described a menu.\n- Read before you write. `list_categories` and `list_tags` first, every time, because the ChatGPT app cannot delete the near-duplicate you create.\n- Give the number. \"Too many variants\" is ignored; \"300 variants, 280 at the same price\" is acted on.\n- When the plan needs a dashboard step, name the exact place, and do not imply you did it. Keep the three buckets apart: on the ChatGPT app, on the full connector, dashboard only. \"Not in my tool list\" is the first two, never the third.\n- Anything that spends money or emails a shopper (`buy_shipping_label`, `set_manual_tracking`, a currency change on a store with orders) gets the amount or the effect said out loud and a yes before the call.\n\n## Reference files in this skill\n\n- `references/mechanisms.md`: all five mechanisms at field level, including the attribute display types, the modifier group selection and free-quantity rules, and the two custom-field flags. Read it when an argument turns on a detail rather than on the choice.\n\n## Cross-skill connections\n\n- Custom fields in depth, including writing values: `brainerce-custom-fields`.\n- Pages, navigation copy, policies and blog: `brainerce-content-and-navigation`.\n- Creating the products once the model is settled: `brainerce-product-onboarding`.\n- Rendering any of this in a storefront: `brainerce-storefront-build`.\n- The exact SDK shape of a category tree or a metafield: `brainerce-sdk`.\n\nRoute once, and do not bounce back and forth.\n"Full snapshot data
{
"description": "Decide how a Brainerce catalog should be structured, and audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, or I have too many variants to manage. Also owns store settings (currency, language, going live), shipping zones, rates, labels, sales-channel publishing, subscriptions, apps, email domains, webhooks and backups. Free shipping as a standing rate lives here; as a promotion it does not. For store owners and developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages or menus, use brainerce-content-and-navigation. Keywords: catalog structure, variant explosion, categories vs tags, shipping zones, sales channels, go live, currency, subscriptions, email domain, webhooks.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 1267
},
{
"relative_path": "assets/icon.png",
"size_in_bytes": 3208
},
{
"relative_path": "references/mechanisms.md",
"size_in_bytes": 5698
}
],
"name": "brainerce-store-architecture",
"skill_md_contents": "---\nname: brainerce-store-architecture\ndescription: 'Decide how a Brainerce catalog should be structured, and audit an existing one: what becomes a variant, what becomes a custom field, what becomes a category, what becomes a tag. Use when the merchant asks how should I organise my shop, should this be a variant or an option, categories or tags, or I have too many variants to manage. Also owns store settings (currency, language, going live), shipping zones, rates, labels, sales-channel publishing, subscriptions, apps, email domains, webhooks and backups. Free shipping as a standing rate lives here; as a promotion it does not. For store owners and developers deciding a data model. Do not choose this to add one product, that is brainerce-product-onboarding. Do not choose this for custom fields in depth, use brainerce-custom-fields. Do not choose this for pages or menus, use brainerce-content-and-navigation. Keywords: catalog structure, variant explosion, categories vs tags, shipping zones, sales channels, go live, currency, subscriptions, email domain, webhooks.'\ncompatibility: ChatGPT, Claude Code, Claude Desktop, Cursor\nmaintainer: Brainerce\nmetadata:\n author: Brainerce\n version: '1.0.0'\n---\n\n⛔ **Before you call anything a dashboard task, look for `search_tools` in your tool list.** Some connections carry a small curated list PLUS a way to reach everything else. Where `search_tools` exists, search for what the merchant asked for and call any hit through the `call_read_tool` / `call_write_tool` / `call_destructive_tool` it names. The dashboard is the honest answer only when your list has no `search_tools`, or the search comes back empty. A few things are refused through it deliberately (payment provider setup, refunds and recording payments, gift cards), which is why an empty result there is a real answer rather than a gap.\n\nGet the catalog model right the first time, or find out exactly where an existing one went wrong.\n\n## Core principle\n\nA Brainerce store has five different mechanisms for \"this product has a property\", and they are not interchangeable. Picking the wrong one is cheap on day one and expensive on day two hundred, because the fix means touching every product. So the modelling question comes before the create call, always.\n\nThe single test that resolves most of it: **does this property have its own stock count, its own price, or its own SKU?** If yes it is a variant. If no it is something else, and the rest of this skill says which.\n\n## When to use this skill first\n\n- \"How should I organise my shop\", \"what is the right structure for this\".\n- \"Should size be a variant or an option\", \"variant or custom field\".\n- \"Categories or tags\", \"how do I build my menu\".\n- \"I am about to add my first products\" and nothing exists yet.\n- \"I have 400 variants on one product and I cannot manage it\".\n- \"Review my catalog\", \"is my structure sensible\".\n\n## When NOT to use this skill first\n\n- Adding one product now, the model already settled: `brainerce-product-onboarding`.\n- Custom fields in depth, including writing values: `brainerce-custom-fields`.\n- Pages, navigation copy, policies, blog, SEO: `brainerce-content-and-navigation`.\n- A read-only sweep for drafts, empty stock and stuck orders: `brainerce-store-health`.\n- Building the storefront code that renders any of this: `brainerce-storefront-build`.\n\n---\n\n## The five mechanisms\n\nWhere a tool is named below, \"full connector\" means Claude Code, Cursor and Claude Desktop connected to the Admin MCP, which lists every tool; the ChatGPT app carries a curated list, and each section says which of its tools are on it.\n\n| Mechanism | What it is | Own stock? | Where it is set up |\n| ------------------------------- | ------------------------------------------------------------------------------------ | ---------- | ----------------------------------------------------------- |\n| **Variant** (from an attribute) | One row per combination, each with its own price, SKU and inventory | Yes | Dashboard variant editor, or `variants` on `create_product` |\n| **Modifier group** | A choice at checkout that changes the price but not what you ship | No | Dashboard, Products then Modifiers |\n| **Custom field** (metafield) | Structured data on the product; 15 types; can be buyer-facing or a storefront filter | No | Definition in the dashboard, value from chat |\n| **Category** | The product's one permanent home in the menu tree | n/a | `create_category`, or the dashboard |\n| **Tag** | A flat label that cuts across the tree | n/a | `create_tag`, or the dashboard |\n\n## Want X, use Y\n\n| The merchant wants | The right mechanism |\n| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| Shoppers pick between options and each option is counted, priced and shipped separately | Variant |\n| Shoppers pick an add-on that changes the total but not what leaves the warehouse | Modifier group |\n| Shoppers type or choose something personal, and it should print on the order | Custom field with Customer Input turned on |\n| Shoppers narrow the product list by a property | Custom field of type Select, Multi-select or Yes/No, with storefront filters turned on |\n| A spec that only needs to be displayed, never chosen or filtered | Custom field of any type |\n| A permanent place in the navigation menu | Category, nested under a parent |\n| A collection that cuts across the menu, or a seasonal grouping | Tag |\n| Who manufactured it | Brand |\n| A different price in another country | Regional pricing: `create_region`, then `upsert_region_prices`, full connector |\n| The same catalog sold in more than one place | Sales channels |\n\nTwo of those rows are the ones merchants get wrong, and they have their own rules below.\n\n---\n\n## Rule 1, the variant rule\n\n**A variant exists only for a property that changes price, stock or SKU. Everything else is a custom field.**\n\nThe reason is arithmetic. Variants are the Cartesian product of their attributes, so every attribute multiplies. Three attributes with 6, 5 and 15 values is 450 rows, and each row wants its own price, its own SKU and its own stock number. Nobody maintains 450 rows. The catalog stops being edited, stock drifts, and the storefront starts offering combinations that do not exist.\n\n**The worked example, and it is the one to reach for.** A jewellery store sells a ring in several diamond sizes, several metal colours, and every ring size.\n\n- **Diamond size is a variant.** A bigger stone costs more. Price changes, so it earns its own row.\n- **Metal colour is a custom field, not a variant.** Same price, same stone, same SKU in most catalogs. Make it a Select custom field with Customer Input turned on, so the shopper still chooses it on the product page.\n- **Ring size is a custom field, not a variant.** Same reasoning, and it is the attribute that multiplies hardest.\n\nThat takes the product from 450 unmaintainable rows to 6 real ones plus two fields.\n\n**The honest exception, and say it out loud.** If the merchant genuinely holds separate stock per metal colour, boxed and on a shelf, then colour changes stock and it IS a variant after all. The rule is the test, not the answer. Ask \"if a customer buys the gold one, which number goes down\" before deciding.\n\n### Spotting a variation explosion\n\nYou are looking at one when any of these is true:\n\n- A product has more variant rows than the merchant could plausibly count in a stock take.\n- Most variant rows carry the same price. A property that never changes the price is not earning its row.\n- Whole blocks of rows sit at zero stock and always have, because those combinations were generated but never existed.\n\nThe fix to propose: keep the one attribute that moves the price, and demote the others to custom fields with Customer Input. Say plainly that this is a rebuild of that product rather than an edit, and that the variant editor is a dashboard job.\n\n## Rule 2, the navigation rule\n\n**A hierarchical menu is parent and child categories. Tags are for filtering across the tree, never for hierarchy.**\n\n\"Mobile\" as a parent, with \"iPhone\" and \"Samsung\" as children, is how a shopper walks down to what they want. Each category gets its own landing page and a breadcrumb on every product in it. Two or three levels is the practical limit; `create_category` accepts `parentId` and the storefront is built around depth two.\n\nTags are flat by design. They have no landing page and no children. `summer-sale`, `gift-under-50` and `bestseller` are tags because they cut sideways across the menu and because they change with the season, while the menu should not.\n\nThe test: **would removing this label leave the product with no home in the menu?** If yes it is a category. If the product still has a home and this was just another way to group it, it is a tag.\n\nA product normally sits in one category. Putting it in several creates duplicate navigation entries and a breadcrumb that cannot decide what it is.\n\n### Spotting a flat catalog\n\n- `list_categories` returns many categories and none of them has a parent. A store with thirty top-level categories has a menu nobody can read.\n- The same words appear as both a category and a tag.\n- Tags carry the structural nouns (\"phones\", \"laptops\") while categories carry the seasonal ones. That is the two mechanisms swapped.\n\n---\n\n## Review mode, auditing a catalog that already exists\n\nRead only. Diagnose, present the plan, then let the merchant pick. Do not restructure anything mid-audit.\n\n1. **`count_products`** first, so you know whether a sample or the whole catalog is being judged, and say which.\n2. **`list_products`**, paging through it. Each product comes back with its variants and its custom-field values, so this is where variation explosion and empty modelling both show up. Note the variant count per product and whether the prices differ across those variants.\n3. **`list_categories`**, for the tree. Count how many have a parent. None with a parent on a large catalog is the flat-catalog finding.\n4. **`list_tags`**, and compare the names against the category names. Overlap is the swapped-mechanism finding.\n5. **`list_product_metafields`** on a few representative products, to see which custom fields are actually in use and which products are missing the ones their neighbours have.\n\nThen report against the four failure modes, most costly first:\n\n- **Too many variants on a product**, with the number and which attribute is not earning its rows.\n- **A flat category list**, with the tree you would propose.\n- **Tags doing a category's job**, naming the specific tags.\n- **A property that should be a custom field and is not recorded anywhere**, which is usually the one costing the merchant filter traffic.\n\n**Use this shape:**\n\n> ✓ I went through 240 products. Three things worth changing, most costly first:\n>\n> 1. **Solitaire Ring has 300 variants** and 280 of them are the same price. Only diamond size moves the price. Metal colour and ring size should be custom fields the shopper picks on the page, which takes it to 6 rows.\n> 2. **All 34 of your categories are top level.** There is no menu to walk down. I would nest them under Rings, Necklaces and Earrings.\n> 3. **`rings` and `necklaces` exist as tags as well as categories.** The tags are doing nothing the categories do not, and they split your filters in two.\n>\n> Also fine: every product has a category, and your custom fields are used consistently across the Rings range.\n>\n> Number 2 is the one I can do from here. Want me to build that tree?\n\n## Planning mode, before the first product exists\n\nFour questions, then a structure. Do not create anything until the merchant has seen the plan.\n\n1. **What are you selling, and roughly how many lines?**\n2. **For one typical product, what does a shopper choose before buying?** List every choice.\n3. **Which of those choices changes the price, or is counted separately in your stockroom?** Those are the variants. Everything else on the list is a custom field.\n4. **How would a shopper browse to it if they did not use search?** That answer is the category tree.\n\nThen hand back: the category tree with parents and children, the attribute or attributes that become variants, the custom fields with their types and whether each is buyer-facing or filterable, and any modifier groups. Say which parts you can build now and which are dashboard steps.\n\n## Shipping zones and rates\n\nWhere you deliver, and what you charge for it. This is **configuration, not a promotion**: a merchant asking \"how much do I charge to ship to Europe\" or \"which countries can I sell to\" is here, not in `brainerce-launch-a-sale`. That skill only lays a free-shipping offer on top of rates that already exist, so if there are no rates, there is nothing for it to discount.\n\nThe model is two levels, and merchants get it wrong in one direction:\n\n- A **zone** is a place: a country, a set of countries, a region. `list_shipping_zones`, `get_shipping_zone`, `create_shipping_zone`, `update_shipping_zone`, `delete_shipping_zone`.\n- A **rate** is a price inside a zone: flat, by weight, by order value, or free over a threshold. `list_shipping_rates`, `create_shipping_rate`, `update_shipping_rate`, `delete_shipping_rate`.\n\n**What a rate's `rateConfig` must look like, per type.** FLAT_RATE `{ amount }`; WEIGHT_BASED `{ baseRate, perKg, maxWeight? }`; PRICE_BASED `{ tiers: [{ minOrderAmount, rate }] }`; FREE `{ minOrderAmount? }`; LOCAL_PICKUP `{ amount, pickupLocation: { name, line1, city, country, ... } }`. All amounts are plain numbers of 0 or more. A config that does not match the type is refused, so read the error and fix the shape; do not retry the same call. Changing a rate's `type` needs its new `rateConfig` in the same call.\n\n**A delivery area drawn on a map.** `update_shipping_zone` takes `geometry` (GeoJSON Polygon or MultiPolygon, coordinates in [longitude, latitude] order, every ring closed), optional `geometryLabels` (display names, one per shape), and `salesChannelIds` to offer the zone on certain channels only. A zone matches an address that is inside its country list OR inside its drawn shape. Create the zone first, then update it to add the shape. An invalid shape is refused; `null` removes it. `calculate_shipping` shows what a buyer at an address would be offered and why a zone did or did not match.\n\nSo \"free delivery over 200 shekels in Israel\" is a rate on the Israel zone, not a discount rule. Reach for a `SHIPPING_DISCOUNT` only when the merchant wants a temporary offer over the top of their normal rates, and say which one you built.\n\n⛔ **Every shipping zone, rate and class tool is OAuth-only, and so are `calculate_shipping` and `set_manual_tracking`.** On a `brainerce_` API-key connection they refuse outright; the other shipment and label tools work on either credential once a shipping app is connected. The ChatGPT app's curated list carries three of them since 2026-09-15: `list_shipping_zones`, `create_shipping_zone` and `create_shipping_rate`, which is enough to add basic shipping from chat: a zone for the destination countries (`[\"*\"]` is worldwide), then at least one rate on it, because a zone with no rate offers nothing at checkout. Editing or deleting a zone or a rate, and everything below, stays on the full connector. Check your tool list, and route to the dashboard's Shipping settings otherwise.\n\n**A country with no zone cannot be checked out to.** That is the finding worth surfacing in an audit: a merchant who says \"customers abroad cannot buy\" usually has no zone covering them.\n\n### Shipping classes, and the two diagnostics\n\nA **shipping class** is a tag a product carries (\"Oversized\", \"Fragile\") so a rate can charge it differently. On the full connector: `list_shipping_classes`, `create_shipping_class`, `update_shipping_class`, `delete_shipping_class`. Creating a class changes nothing on its own: a product has to carry it (`update_product` with `shippingClassId`) and a rate has to price it (`classCosts` on `create_shipping_rate` or `update_shipping_rate`), so say both steps when you create one. Deleting a class unassigns every product that carried it, and the products then ship at the rate's base cost.\n\nTwo read-only tools answer the questions merchants actually ask:\n\n- `calculate_shipping` runs the store's own zones and rates for an address exactly as checkout does and returns the offered rates plus a verdict per zone saying why it did or did not match. It is the tool for \"why does my customer see no shipping methods\". It is not a live carrier quote.\n- `check_free_shipping` compares, per sales channel, the free-shipping threshold the storefront bar promises with any FREE-type rate that has its own minimum. `hasMismatch: true` is the case where the bar says one figure and checkout charges by another.\n\n### Shipments and labels\n\nSeparate from zones and rates, which are what the shopper pays, a **shipment** is the parcel record: carrier, tracking number, label. On the full connector `list_shipments`, `get_shipment` and `get_shipment_tracking` read them, `create_shipment` records a parcel the merchant hands to a carrier themselves, `set_manual_tracking` attaches a tracking number and ships the order (and emails the customer; there is no quiet mode on that path), and `cancel_shipment` voids a bought label at the carrier. `get_ship_from_origin` and `save_ship_from_origin` hold the address every quote and label is sent from; without a complete one no carrier can quote. `validate_shipping_address` asks the carrier whether an address is deliverable.\n\n`get_order_shipping_rates` is a live carrier quote for one order, and `buy_shipping_label` quotes first and buys only with `confirm: true`, charging the store's carrier account. Carrier credentials are entered in the dashboard, never through the connector. Shipment tools work only once a shipping app is connected; before that they refuse, and the refusal is the connection, not the order. Always show the merchant the amount and carrier before passing `confirm`. None of the shipment or label tools are on the ChatGPT app.\n\n## Selling the same catalog in more than one place\n\nSales channels are how one catalog reaches a storefront, a marketplace and a social shop without duplicating products. On the full connector `list_sales_channels`, `get_sales_channel`, `create_sales_channel`, `update_sales_channel` and `test_sales_channel_connection` manage the channels themselves.\n\nPublishing is separate from creating, and this is the part merchants miss: **a product is not on a channel until it is published to one.** `publish_product_to_channel` and `publish_product_to_channels` do that, `unpublish_product_from_channel` reverses it, and `get_product_channel_publishes` says where a product is currently live. `get_product_channel_overlay` and `save_product_channel_overlay` let one product carry a different title or price per channel. Coupons and taxonomy publish the same way, with `publish_coupon_to_channel` and `publish_taxonomy_to_channel`.\n\n**Naming the channel.** Every one of these tools (and `get_sales_channel`, `update_sales_channel`, `delete_sales_channel`, `test_sales_channel_connection`, `set_store_member_sales_channels`, `invite_store_member`) takes the channel as the `id` from `list_sales_channels`, **or** its public `vc_*` connection id, **or** its exact name — any of the three, and the plural tools accept a mix. Use whichever the merchant gave you; do not translate a `vc_*` id or a name into the record id first. A reference that matches none of them errors naming it, and the fix is to read `list_sales_channels` rather than guess.\n\nProducts a storefront run seeded through the device-auth window (`--seed-products`) arrive already published to that channel; the seed report says `published: false` for any that did not, and those are published from here or the dashboard, never re-created.\n\n`list_products` takes a `salesChannelId` filter, so \"what is actually live on my Instagram shop\" is answerable.\n\nOn the ChatGPT app's curated list: `list_sales_channels`, `create_sales_channel`, `publish_product_to_channel`, and since 2026-09-15 `bulk_update_platforms` and `set_sales_channel_live`. Unpublishing one product, the plural publish, per-channel overlays, and coupon and taxonomy publishing stay on the full connector.\n\n### Tracking ids and the signup form on a channel\n\n`update_sales_channel` also sets the channel's tracking ids: `gtmContainerId` (GTM-ABC1234), `ga4MeasurementId` (G-ABC1234XYZ), `metaPixelId` (10 to 20 digits) and `tiktokPixelId`, plus `googleSiteVerification`. An empty string clears one and the channel falls back to the id discovered from its connected app. A malformed id is refused, because it would otherwise be written into the storefront page. `requireBirthday: true` makes shoppers give a birthday (month and day, never a year) when they register; leave it off unless the merchant asks, because it works better as an incentive for a birthday gift than as a gate.\n\n### Publish the whole catalog at once\n\nPublish a catalog: `bulk_update_platforms({ filter: { status: 'active' }, vcPublishTo: [channel] })`; `vcPublishTo` accepts the channel id, its `vc_` id or its name. `filter: { status: 'active' }` is the \"all my products\" form; `filter: {}` alone is refused, and so is a call with neither `productIds` nor `filter`. It changes visibility only, `vcUnpublishFrom` undoes it, and a large catalog is queued: the result then carries `{ queued: true, jobId }`, so report it as in progress and poll `get_bulk_job_status` rather than calling it again. For one product `publish_product_to_channel` is the smaller call.\n\n### Going live\n\nGoing live: `set_sales_channel_live({ channelId, domain, live: true })` records the public https domain and switches the channel to LIVE; the payment provider is still connected in the dashboard. A TEST channel takes no real orders. The domain must already point at the deployed storefront, because this records it and deploys nothing; the call is refused, with nothing changed, when `live: true` and no valid domain exists. A LIVE channel with a payment provider still in TEST mode declines real cards, so read `get_store_setup_status` after flipping it and say what is still missing. `live: false` puts the channel back into TEST. Do not repeat a full `vc_` id into the conversation; the result carries only its last four characters. Every other channel setting is `update_sales_channel`, full connector only.\n\n### What a channel is connected to\n\nOn the full connector, `get_sales_channel_integrations` says which marketing connectors (Google Merchant Center, Meta, TikTok) are bound to a channel and how their last sync went, which is the read for \"my products are not on Google\". It also carries the Google Customer Reviews opt-in state (connected Merchant ID + whether the merchant confirmed the opt-in) — Google's own verified-buyer survey feeding the seller rating, not the same thing as Brainerce's storefront product-review feature. `sync_sales_channel_now` re-pushes every published product to those connectors in the background; it is skipped on a TEST channel, because connectors only receive LIVE ones. `get_sales_channel_tracking` shows the GA4, GTM, Meta and TikTok tag ids the storefront serves, public ids only. `set_product_channel_app_visibility` hides one product from a connector app (Google Merchant Center today) without unpublishing it from the storefront. `get_connection_health` rolls up every channel and installed app with its last ping and last error.\n\n⛔ **`create_product({ platforms })` is not a publish path.** It expects a channel record id behind a literal `vc:` prefix; a `vc_` connection id or a channel name matches nothing, the call still succeeds, and the product stays unpublished. `update_product` has no `platforms` field. Publish with `publish_product_to_channel`, which takes any of the three channel references above.\n\n## Subscriptions and recurring plans\n\nA store can sell on a repeating charge rather than one payment. On the full connector this is `list_recurring_plans`, `create_recurring_plan` and `update_recurring_plan` for what is offered, and `list_recurring_subscriptions` plus `arm_recurring_subscription`, `pause_recurring_subscription`, `resume_recurring_subscription` and `cancel_recurring_subscription` for individual customers. `get_recurring_plan_armability` says whether a plan can actually start charging, and it is the thing to read before promising a merchant it is live. Not on the ChatGPT app's curated list.\n\n## Store settings, currency and language\n\n`update_store` is Settings, General from chat. On the ChatGPT app it takes the store's name, country, currency, default language and the two tax flags (`taxEnabled`, `pricesIncludeTax`); on the full connector it also takes timezone, discount stacking mode, pricing-formula mode, contact email and phone, social links, brand colours, logo (an asset from `ingest_media_asset`, never an external URL), homepage meta description, the review flags and which dashboard modules are enabled. Omitted fields are unchanged.\n\n`update_store` changes currency and language too: currency relabels every price (nothing is converted), is refused while a checkout is pending and needs `confirm: true` once the store has orders; language seeds email templates. `set_store_languages` adds or removes extra storefront languages; `activate_store` moves a store that sells without a payment provider from SETUP to ACTIVE.\n\nSay the relabelling out loud before you touch currency: 100 ILS becomes 100 EUR, on every product, sale price, shipping rate, coupon amount and gift-card balance, and the default region follows the store in the same change. A merchant who says \"switch me to euros\" on a store with prices almost never means that, so ask what they want the prices to be first. On language, every product or category text without a translation is then shown as the new language, not translated; ten languages are supported (en, he, ar, es, fr, de, ja, zh, ru, pt). Removing a language with `set_store_languages` deletes that language's email templates and any edits to them, which cannot be undone.\n\n`set_store_languages` also takes `autoTranslateNewProducts` (translate a new product into every supported language) and `nonDefaultLocaleStrategy` (what happens to AI-written content when the merchant writes in a language other than the default: `translate-to-default`, `keep-input`, `ask` or `single-language`).\n\nThe business identity printed on purchase orders is not on `update_store`: use `update_store_business_profile` for `legalName`, `taxId` and `businessAddress` (null clears each), and for the reporting settings `analyticsCurrencyMode` (NATIVE groups revenue per currency; AGGREGATE converts into `reportingCurrency`) and `fxProvider` (ECB, OPENEXCHANGE or MANUAL). It is for the document identity and reporting only: the postal address on marketing emails is set in the dashboard under Settings, Email.\n\n`taxEnabled: true` only switches the flag on. With no rate checkout charges 0 tax. The rate is `create_tax_rate`, on the ChatGPT app since 2026-09-15 alongside `list_tax_rates`: read the list first and refuse a duplicate country and region pair, take the percentage the merchant gave (18 means 18%) and never one you assume, and always a country (there is no rate that applies everywhere; a country with no rate pays 0 tax), because the rate is the merchant's legal number and the tool registers nothing with any authority. Editing and deleting rates, the Canada preset, and tax classes are full connector only.\n\n`activate_store` is idempotent and is only for a store that sells without a payment provider (cash on delivery, bank transfer): the dashboard activates a store the moment a provider goes live, and checkout does not gate on the status, so activating does not make a store ready to sell. `get_store_setup_status` is still the checklist.\n\n⛔ `update_store`, `set_store_languages` and `activate_store` are OAuth-only; `get_store_setup_status` and the tax-rate tools work on an API key too. On the ChatGPT app `update_store` carries only the fields named above; timezone, contact details, colours, logo and modules are full-connector work there, not dashboard work.\n\n## Apps\n\nOn the full connector `browse_marketplace_apps` and `get_marketplace_app` find an app, `install_app` installs it by slug, `configure_app` sets its non-secret settings, and `suspend_app`, `resume_app`, `disconnect_app` and `uninstall_app` manage it afterwards. `trigger_app_sync` queues a full import from a connector such as Shopify or WooCommerce; `get_app_sync_status` and `get_app_activity` report on it.\n\nThe assistant can install an app and change its non-secret settings, but it can never enter or read an API key, password or token; those are entered only in the dashboard, and a payment gateway installed from chat takes test cards only until you connect it live there. `install_app` creates the installation only: OAuth consent, provider credentials and KYC are finished by the merchant under Apps in the dashboard, and `configure_app` refuses by name any key, token, password or the test/live switch of a payment app. Say the dashboard step in the same breath as the install, and never report a payment provider as live because it is installed. `install_app`, `sync_sales_channel_now`, `get_sales_channel_tracking` and `get_sales_channel_integrations` are OAuth-only; the rest work on an API key too. None of the app tools are on the ChatGPT app.\n\n## Email sending and domains\n\nTransactional emails (order confirmation, shipped, and the rest) are templates per event and per language. On the full connector `list_email_templates`, `get_email_template` and `update_email_template` edit them, `preview_email_template` renders one with sample data without sending, `list_email_template_versions` and `restore_email_template_version` roll one back, `reset_email_template` and `reset_all_email_templates` return to the built-in design (destructive; the second needs `confirm: true`), and `create_email_template` and `delete_email_template` exist for a second variant of one event, which is rare. `send_test_email` sends only to the signed-in merchant's own address; there is no recipient argument and it is never a way to email a customer. `list_email_logs` is the send log with delivery status per message.\n\nA custom sending domain lets the store send from its own address instead of the shared Brainerce one. `create_email_domain` registers it and returns the DNS records the merchant adds at their registrar (public values, show them verbatim), `list_email_domains` shows verification status, and `delete_email_domain` removes one. `verify_email_domain` looks each record up in public DNS and puts a `check` on every record in `dnsRecords` (found, missing, wrong, proxied, doubled, duplicate or unknown), so tell the merchant exactly which record to fix; `unknown` means the lookup could not run, not that the record is missing. Approval itself happens on the provider's schedule once every record is found, so a call made right after the records were added can still say PENDING; tell the merchant to wait rather than looping. Once VERIFIED, select the domain in `update_email_settings`. Every domain tool is OAuth-only, and none of the email tools are on the ChatGPT app.\n\n## Webhooks and backups\n\nWebhooks are developer plumbing, but merchants ask \"why did my integration stop getting orders\". On the full connector `get_webhook_health` is the one-glance answer, `list_webhook_subscriptions` and `list_webhook_deliveries` show the detail, `update_webhook_subscription` edits the URL or events (the list replaces the whole set) and pauses or resumes one, and `isActive: true` on a tripped subscription resets its circuit breaker. `send_test_webhook_event` queues a signed test event, and `delete_webhook_subscription` removes one for good. Creating a webhook subscription and rotating its secret are dashboard-only because both display the signing secret once. No tool ever returns the secret.\n\nBackups: `create_store_export` starts a background export (a full backup when `scope` is omitted), `list_store_exports` and `get_export_status` follow it, and the download link on a completed export is one the merchant opens in a browser. `get_backup_schedule`, `upsert_backup_schedule` and `delete_backup_schedule` manage the automatic schedule. Restoring a backup is dashboard-only; the tools create, list, link and delete exports. Only one export can run per store at a time, so a conflict means check the list rather than retry. None of the webhook or backup tools are on the ChatGPT app.\n\n## What can be done from here, and what cannot\n\n**From this conversation:**\n\n- `create_category` with `parentId`, so the whole tree can be built from chat.\n- `create_tag`, after `list_tags` to avoid a near-duplicate.\n- `create_product`, including its `variants` array.\n- `update_product` to move products between categories. ⛔ `categories` REPLACES the set while `categoryNames` is additive, so read the product first and know which one you want.\n- `set_product_metafield` to write a custom-field value, but only for a field some product already carries. See `brainerce-custom-fields` for why.\n- `create_shipping_zone` and `create_shipping_rate` for basic shipping, `publish_product_to_channel` to put one product on a storefront and `bulk_update_platforms` to put the whole catalog on it, `set_sales_channel_live` to record the domain and switch the channel to LIVE, `create_tax_rate` after `list_tax_rates` for the rate, and `update_store` for the store's name, country, currency, default language and the two tax flags (`taxEnabled`, `pricesIncludeTax`). All of these are on the ChatGPT app's curated list. `get_store_setup_status`, the checklist of what the store still needs, is there too and is the one tool in this list that also works on an API key.\n\n⛔ **`list_categories`, `create_category`, `list_tags`, `create_tag`, `set_product_metafield`, `publish_product_to_channel`, `set_sales_channel_live`, `update_store`, `create_shipping_zone` and `create_shipping_rate` are all OAuth-only** (`bulk_update_platforms`, `list_tax_rates` and `create_tax_rate` are not, and work on a key with `products:write` or `tax:write`). On a connection made with a `brainerce_` API key every one of them fails with \"This tool requires OAuth authentication and is not available via API key\", which takes most of this list with it and makes Review mode's steps 3, 4 and 5 unrunnable. That is the connection, not the store: say so once and route the tree-building to the dashboard rather than retrying.\n\nThe one route that survives an API-key connection is `create_product` with `categoryNames` and `tags`, which resolve or create by name case-insensitively without touching the taxonomy tools. It builds flat categories only, with no `parentId`, so it cannot build a nested tree.\n\n**Full connector only (Claude Code, Cursor, Claude Desktop), so on the ChatGPT app these are a dashboard route:**\n\n- Creating a custom-field definition (`create_metafield_definition`), its type, its allowed values, its Customer Input flag and its storefront-filter flag.\n- Attributes and their options (`create_attribute`, `create_attribute_option`, `assign_attributes_to_product`) and the variant tools beyond what `create_product` accepts.\n- Modifier groups (`create_modifier_group`, `create_modifier`, `attach_modifier_group`).\n- Regional pricing (`create_region`, `set_region_payment_providers`, `upsert_region_prices`).\n- Editing or deleting shipping zones and rates, shipping classes, shipments and labels.\n- Timezone, contact details, colours, logo and modules on `update_store`; `set_store_languages`; `activate_store`.\n- Editing or deleting a tax rate, and tax classes.\n- Installing and configuring apps, email templates and domains, webhooks, backups.\n- Deleting anything at all: the delete tools exist on the full connector and none of them is on the ChatGPT app.\n\n**Dashboard only on every connection, and say so rather than improvising:**\n\n- Connecting a payment provider and putting it live. `install_app` can install one, and `get_store_setup_status` reports it, but the credentials, the OAuth consent and the switch to live are entered only in the dashboard, and a gateway installed from chat takes test cards until then.\n- API keys, in every respect.\n- Creating a webhook subscription and rotating its secret.\n- Restoring a backup.\n- Carrier credentials for a shipping app, and any other secret.\n- Drawing a shipping zone on the map (polygon zones); the zone tools take countries, regions and postal codes only.\n\n## Rules\n\n- Answer the modelling question before creating anything. A product created under the wrong model is a rebuild, not an edit.\n- Never propose a variant for a property that does not change price, stock or SKU. Say the arithmetic out loud when refusing.\n- Never propose a tag where the merchant described a menu.\n- Read before you write. `list_categories` and `list_tags` first, every time, because the ChatGPT app cannot delete the near-duplicate you create.\n- Give the number. \"Too many variants\" is ignored; \"300 variants, 280 at the same price\" is acted on.\n- When the plan needs a dashboard step, name the exact place, and do not imply you did it. Keep the three buckets apart: on the ChatGPT app, on the full connector, dashboard only. \"Not in my tool list\" is the first two, never the third.\n- Anything that spends money or emails a shopper (`buy_shipping_label`, `set_manual_tracking`, a currency change on a store with orders) gets the amount or the effect said out loud and a yes before the call.\n\n## Reference files in this skill\n\n- `references/mechanisms.md`: all five mechanisms at field level, including the attribute display types, the modifier group selection and free-quantity rules, and the two custom-field flags. Read it when an argument turns on a detail rather than on the choice.\n\n## Cross-skill connections\n\n- Custom fields in depth, including writing values: `brainerce-custom-fields`.\n- Pages, navigation copy, policies and blog: `brainerce-content-and-navigation`.\n- Creating the products once the model is settled: `brainerce-product-onboarding`.\n- Rendering any of this in a storefront: `brainerce-storefront-build`.\n- The exact SDK shape of a category tree or a metafield: `brainerce-sdk`.\n\nRoute once, and do not bounce back and forth.\n"
}SHA-256 of public snapshot: 27562964d3cea3debead9713e267edbf6b474041c559e3167ff20f71667f3497