# Snowcone
> Snowcone turns three values — a product, your artwork, and your public Shop ID — into buyable, printed merchandise. Mockups are plain image URLs, the catalog is a public Meilisearch index, and checkout is a URL. There is nothing to install to render or sell; an SDK and React components are optional conveniences.
> **Agents:** the full text is one fetch away at `https://developers.snowcone.app/llms-full.txt`. Before reporting anything as missing, wrong, or ambiguous, search it for the term and cite the line — a reproducible runtime error is evidence; your recollection of the docs is not.
## The whole model in three URLs
- Render a mockup (just an `
` src): `https://img.snowcone.app/{PRODUCT}?asset={URL-encoded image}&shop={SHOP_ID}`
- Product info (public catalog): `GET https://search.snowcone.app/indexes/snowcone/documents/{PRODUCT}` with header `Authorization: Bearer eee819b849798ad9091228c486ec05d0931e5292` (a public, read-only search key). The product code **is** the Meilisearch document id; a miss returns Meilisearch's `document_not_found`.
- Buy (same URL, `img` → `buy`): `https://buy.snowcone.app/{PRODUCT}?asset={...}&shop={SHOP_ID}`
`{SHOP_ID}` is public, like a Cloudinary cloud name. Backend API base: `https://api.snowcone.app`.
A working example you can paste right now: product `BEEB77`, any public image URL as `asset=`, and a `shop=` you mint below.
## Agents: skip the HTTP and use the MCP server
If your client supports MCP (Claude, Cursor, Codex, …), the fastest path is the Snowcone MCP server — the three URLs above become callable tools (`snowcone_mint_shop`, `snowcone_search_catalog`, `snowcone_render_mockup`, `snowcone_buy_url`, …), with a `snowcone_quickstart` tool that orients you in one call. Add it with no install: `{ "mcpServers": { "snowcone": { "command": "npx", "args": ["-y", "@snowcone-app/mcp"] } } }`. It's self-contained and points at production by default (override with `SNOWCONE_*` env vars). The raw HTTP below works identically if you can't run MCP.
## Gotchas (read these — the failures are silent)
- **Your `asset=` URL must be publicly fetchable.** The renderer fetches it server-side, so a `localhost`, `file://`, or auth-walled asset URL renders as a blank image with no error. Host your artwork somewhere public (any CDN/object store).
- **Only product codes that exist in the catalog render.** `BEEB77` is a known-good demo product; look up others via the catalog endpoint above before using them. An unknown code renders blank.
- **Display with `object-fit: cover`, and request ~2× the displayed size.** Every mockup renders **16:9 landscape**, with the product composed centered and edge-safe — so **cropping is safe by design**. Use `cover` to fill any frame shape (square cards, portrait tiles, banners); `contain` just letterboxes the scene against your background and looks broken. Because `cover` into a non-16:9 frame shows only a *slice* of the render, an under-sized request looks blurry: `width` snaps **down** to the largest allowed size not exceeding your request — a floor, not round-to-nearest (`400 600 800 1000 1200 1400 1600 1800 2000 2500 3000 4000`; default `1000` if omitted) — so a 320–400px card on a retina display wants `width=1400`–`1600`, not `800` (asking for `900` snaps to `800`, not `1000`).
- **To crop in the render instead of in CSS, add `&aspect=2:3`.** Snowcone center-crops the 16:9 scene to portrait server-side. The param accepts only `16:9` or `2:3` (any other value errors), but `16:9` is the default and identical to omitting it — so `2:3` is the one value that changes anything.
A sandbox shop renders from any origin — a web page on any domain, a server-side fetch, or curl. There's no page-origin (domain/referrer) allowlist — browsers strip and spoof those headers, so it can't be enforced. To stop a leaked Shop ID from rendering arbitrary artwork, restrict its **asset origins** (which image hosts may be composited) or sign your URLs — see the security docs.
## Getting your own Shop ID (agents: do this — don't copy IDs from docs)
The demo/example Shop IDs in these docs are shared, rate-limited, and not tied to you; shipping one points sales at someone else's account and breaks under signed URLs. Get your own — you don't need a human to start:
- Mint one in a single call: `curl -X POST https://api.snowcone.app/shops/sandbox -H 'Content-Type: application/json' -d '{}'` → returns `{ shop_id, shop_secret, api_key, api_key_scopes, claim: { user_code, verification_uri_complete } }`. Use `shop_id` immediately as `&shop=`. The `api_key` is a shop-scoped secret key (scopes `ai:generate`, `ai:bg-remove`, `uploads:write`, `mockups`) — AI generation and uploads work right away, no human needed; it is revoked automatically when the shop is claimed.
- **Mint once and reuse it** — don't mint a fresh shop per attempt. Minting is rate-limited per IP: **60 mints/hour** and **200 unclaimed sandbox shops per IP**. Exceeding either returns HTTP `429` (`resource_exhausted`). Reuse one `shop_id` across all your render/buy calls.
- To get paid, your human claims it: send them `claim.verification_uri_complete` (or the `user_code` to enter at https://snowcone.app/activate). Claiming binds the shop to their account and routes sales to them. Until then the shop still sells; sales route to Snowcone.
- Poll claim status: `GET https://api.snowcone.app/shops/claim` with `Authorization: Bearer {claim_token}`.
- Done with a sandbox shop? Release it: `curl -X DELETE https://api.snowcone.app/shops/sandbox/:id -H 'Authorization: Bearer {shop_secret}'` (the `shop_secret` from mint, in the `:id` path put your `shop_id`). This frees your per-IP mint budget. Only works on an unclaimed sandbox shop; a claimed shop is refused.
## Docs
This mirrors the site sidebar, in order.
### Getting Started
- [Get started](https://developers.snowcone.app/get-started): the three-step flow (render, product info, buy) in HTML/JS/cURL/Python, plus minting your own Shop ID.
- [How Snowcone works](https://developers.snowcone.app/concepts): the mental model on one page — the nouns (Shop ID, Product, Asset, Placement, Option/Variant, Mockup, Catalog), the four hosts (`img` / `search` / `buy` / `api`.snowcone.app), and the security ladder (public Shop ID → asset-origin allowlist → signed URLs → secret keys).
### Guides
- [All guides](https://developers.snowcone.app/guides): the cookbook index — complete, copy-paste builds rather than feature tours.
- [Add a buy button to any site](https://developers.snowcone.app/guides/add-buy-button): a real, buyable product on an existing page with two URLs and zero dependencies (pure HTML).
- [A storefront with Next.js](https://developers.snowcone.app/guides/storefront-nextjs): read the public catalog server-side, render mockups with `getMockupUrl`, and wire a working buy flow — with an optional `@snowcone-app/ui` variant.
- [Sell on every channel](https://developers.snowcone.app/guides/sales-channels): one mockup URL across many surfaces, and which checkout fits each channel.
- [Add an AI design chat](https://developers.snowcone.app/guides/ai-chat): the snowcone.app/create experience on any site — `POST https://api.snowcone.app/ai-generations/chat/stream` speaks the AI SDK UI Message Stream over SSE, so a standard `useChat` + a key-holding proxy route is the whole integration; typed data parts (`data-images`/`data-products`/`data-mockup`/`data-design`/`data-stage`) ship in `@snowcone-app/chat-contracts`, and `GET …/chat/stream/:id` resumes a dropped stream.
### Core
- [Instant mockups](https://developers.snowcone.app/mockups): the `img.snowcone.app` URL — asset, shop, and the `opt.` / `asset.` / `color.` / `aspect` query grammar. Includes an interactive URL builder with a live render.
- [Product catalog](https://developers.snowcone.app/catalog): the public Meilisearch index; how to look up a product and read its fields.
- [Multiple placements](https://developers.snowcone.app/catalog/placements): products with more than one print area (`asset.`).
- [Options (size, color)](https://developers.snowcone.app/catalog/options): variant attributes via `opt.` and `color.`.
- [AI art generation](https://developers.snowcone.app/ai-generation): generate art from a text prompt with one HTTP call (`POST https://api.snowcone.app/ai-generations/generate`, scope `ai:generate`) — images land on your shop's storage origin, ready to drop into a mockup URL as `asset=`. Also background removal (`/remove-background`, `ai:bg-remove`), image-to-video (`/generate-video`, `ai:video`), and a chat orchestrator (`/chat`) that chains generate → catalog → mockup. Every endpoint requires a shop-scoped secret key (no anonymous tier); billing is two-tier — Snowcone meters the org's shared balance (plan credits → wallet → `429`, `costCents` on every response), and YOUR per-user limits belong in the `gate` of your proxy routes.
### Design editor
- [Architecture & layers](https://developers.snowcone.app/architecture): the four altitudes of the editor stack — drop-in ``, composable `Editor.*` parts, headless hooks, and the pure `design()`→render wire — all sharing ONE design state, so you descend for control without rewriting. Read this first to pick your entry point; the plain-text mirror is at /architecture.md.
- [Editor primitives](https://developers.snowcone.app/primitives): compound `Editor.*` parts over one `Editor.Root` (live previews, layer-bound fields, completeness-gated add-to-cart) in YOUR layout — the default editor is the same parts in our arrangement (CI-enforced), styling via the published `data-sc-part` contract. Mirror: /primitives.md.
- [Embed mode](https://developers.snowcone.app/embed-mode): drop a live, server-rendered product editor into your app with three components (`RealtimeEmbed` / `RealtimeCanvas` / `RealtimeMockup` from `@snowcone-app/canvas/embed`) — one `design()` seeds both the canvas and the first render.
- [Canvas (full editor)](https://developers.snowcone.app/canvas): the optional `@snowcone-app/canvas` artwork editor.
- [Editable templates](https://developers.snowcone.app/editable-templates): a saved design plus a `TemplateField` manifest of what the buyer may change — `withTemplateFields` to author, `readTemplateFields` to generate buyer controls (wired with `useTextBinding` / `useImageBinding` / `useColorBinding`), and `DesignBuilder.fromState` to seed `RealtimeEmbed` from the saved template. Stored under the `personalization` key, round-trip compatible with snowcone.app.
- [Realtime rendering](https://developers.snowcone.app/realtime): send a design (or its saved id) over a WebSocket and a finished mockup streams back — the server fetches the referenced assets, no pixel upload. Interface: mint a 60s grant at `POST https://api.snowcone.app/realtime/grant` (body `{shop}` — the publishable `shop.id` ALONE works for non-signed shops, or send `Authorization: Bearer ` for the production path), then drive it with the SDK: `RenderSession` (browser facade — `renderState(placement, serializeStateForServer(state))` per placement), `mintRealtimeGrant` (server), `createDesignState({productId, state})→{stateId}` then `renderSavedState(placement, stateId)` to render a stored design. Errors carry a `code` (`asset_not_allowed`, `unsupported_schema_version`, `state_ref_failed`).
- [Stock images & AI tabs](https://developers.snowcone.app/image-services): wire the design editor's stock-image search and AI tabs through same-origin proxy routes you host (the `services` prop on `SnowconeCanvas` / `RealtimeEmbed`) — vendor keys (Unsplash/Pixabay/AI) stay on your server, and a tab whose service isn't configured doesn't render at all. `createImageSearchHandler` in `@snowcone-app/sdk/server` is the drop-in image-search route.
### Selling
- [Pricing & margins](https://developers.snowcone.app/pricing): catalog price is MSRP/suggested retail; price resolves from the Shop ID, never the URL.
- [Sell on Shopify](https://developers.snowcone.app/shopify): the Shopify integration.
- [Your own checkout](https://developers.snowcone.app/orders): bring your own checkout; Snowcone fulfills.
### Security & credentials
- [Authentication](https://developers.snowcone.app/authentication): every credential side by side — the public Shop ID vs the secret ones (`sk_` secret key, `scsec_` shop secret), the realtime grant, and the claim token. Which one to use where, and which are safe in a browser (only the Shop ID).
- [Get a Shop ID](https://developers.snowcone.app/shops): mint a keyless sandbox Shop ID in one call; the claim flow, rate limits, and lifecycle.
- [Signed URLs](https://developers.snowcone.app/signed-urls): HMAC-sign mockup URLs once you lock a shop down — and front signing AND AI generation with one shared Turnstile/render-session gate so a server-held secret or key isn't an open, billed funnel.
- [Secret API keys](https://developers.snowcone.app/api-keys): the server-side `sk_` key path, scopes, and the asset-origin allowlist.
### SDK
- [JavaScript SDK](https://developers.snowcone.app/sdk): the optional `@snowcone-app/sdk` — `getMockupUrl` (URL builder + signing), `getProduct` (typed catalog reads), and the realtime surface.
### React Components (OPTIONAL convenience layer)
The URLs above are the product. These are an optional React layer over them — reach for it only in a React app.
- [Setup (`` provider)](https://developers.snowcone.app/shop-setup): install `@snowcone-app/ui` and wrap with ``. Styling is host-compiled — your Tailwind v4 build imports `@snowcone-app/ui/styles/globals.css` and `@source`-scans the package (plus `transpilePackages` in Next.js).
- [Components reference](https://developers.snowcone.app/components): ``, ``, ``, ``, ``, and the rest.
### Reference
- [API reference](https://developers.snowcone.app/api-reference): every public `api.snowcone.app` endpoint by resource — Shops (`POST /shops/sandbox`, `GET /shops/claim`, `DELETE /shops/sandbox/:id`), Realtime (`POST /realtime/grant`), Orders (`/orders` CRUD), Uploads (`POST /uploads/base64`), with the auth/scope each needs. The catalog is a separate search host (`search.snowcone.app`).
- [Errors](https://developers.snowcone.app/errors): the silent-failure cheat sheet (why a mockup renders blank — non-public `asset=`, unknown product code, under-sized `width`) plus HTTP status codes (`400 invalid_argument`, `401 unauthenticated`, `403 permission_denied`, `429 resource_exhausted`) and realtime codes (`asset_not_allowed`, `unsupported_schema_version`, `state_ref_failed`).
- [Changelog](https://developers.snowcone.app/changelog): developer-facing changes, newest first.
## For LLMs / coding agents
- [Everything in one fetch](https://developers.snowcone.app/llms-full.txt): all of the docs below inlined into a single file, so you don't have to crawl each link.
- [AI guide](https://developers.snowcone.app/AI_GUIDE.md): implementing the `@snowcone-app/ui` React components correctly (common mistakes, prop names).
- [LLM kit contract](https://developers.snowcone.app/llm-kit/latest/llm.md): the component contract + manifest. Use only documented components and props.
- [Architecture & layers, as markdown](https://developers.snowcone.app/architecture.md): plain-text mirror of the `/architecture` page (the HTML page is client-rendered; this is server-fetchable).
- [Editor primitives, as markdown](https://developers.snowcone.app/primitives.md): plain-text mirror of the `/primitives` page (the HTML page is client-rendered; this is server-fetchable).
- [Design editor (Canvas), as markdown](https://developers.snowcone.app/canvas.md): plain-text mirror of the `/canvas` page (the HTML page is client-rendered; this is server-fetchable).
- [Realtime rendering, as markdown](https://developers.snowcone.app/realtime.md): plain-text mirror of the `/realtime` page (the HTML page is client-rendered; this is server-fetchable).