---
name: cartfun
description: Buy from and run stores on cart.fun, where every sale prints a receipt NFT. Use when asked to shop a cart.fun store, quote or pay an order on-chain, check a receipt or review, or manage a cart.fun store's products, invites, sales log or webhooks with an API key.
---

# cart.fun

cart.fun is on-chain commerce: stores sell for stablecoins through a CartCheckout contract, and every sale prints a receipt NFT (proof of purchase, warranty, refunds) plus a cart NFT holding any on-chain goods. Buyer agents discover stores, get signed quotes and pay on-chain. Merchant agents run a store's catalog, invites, sales log and webhooks with API keys.

API base: `https://go.cart.fun`. When unsure about an endpoint, fetch `https://cart.fun/openapi.json`; for the full guide, `https://cart.fun/llms-full.txt`.

| Network | Chain id | Slug for URLs |
| --- | --- | --- |
| Base Sepolia | 84532 | `base-sepolia` |
| Robinhood Chain Testnet | 46630 | `robinhood-testnet` |
| Arc Testnet | 5042002 | `arc-testnet` |

## Rules
- Never put an API key (`cf_sk_…`) in client-side code, logs or chat output. Read it from the environment (e.g. `CARTFUN_API_KEY`).
- Quotes are signed for one buyer and expire; get a fresh one rather than editing it. Pass `order`, `items` and `signature` to the contract unchanged.
- Confirm with the user before any on-chain transaction (approve, checkout) and before deleting products, invites or webhooks.
- On 429, wait `Retry-After` seconds. Each key gets 120 requests a minute.
- Verify webhook signatures and dedupe on the event id before acting on one.

### Buy something
1. **Discover the store.** `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/agent` returns its agent card. Read `cartfun.commerce`: `orderable`, `payment` (token, symbol, decimals), `checkout` (contract), and for public stores `catalog.products` with `price` (decimal) and `priceUnits` (base units). Invite-only stores list no products; ask the merchant for an API key with the `orders` scope.
   Don't know the store id? `GET https://go.cart.fun/api/v1/stores/directory` lists stores that opted in to discovery, and `GET https://go.cart.fun/api/v1/handles/{handle}` resolves a `cart.fun/@handle` to its chain and store id. `GET …/catalog` returns the store's signed catalog snapshot if you want proof of the terms it sells under.
2. **Quote.** `POST https://go.cart.fun/api/v1/orders` with `{ chainId, storeId, buyer, lines: [{ id: sku, quantity }] }`. The server prices the lines from the catalog and returns an order signed by the store, valid for 10 minutes and fillable only by `buyer`. A product with limited stock answers 409 when too few units are left, and a quote holds its units for a few minutes. ERC-721 products pack specific token ids, chosen when the order is quoted.
3. **Approve.** From the buyer wallet, approve the checkout contract (`quote.checkout`) to spend the order total, `order.amount + order.serviceFee + order.relayFee`, of `order.paymentToken`. Tokens with EIP-2612 permit can use `checkoutWithPermit` instead.
4. **Pay.** Call `checkout(order, items, cartRecipient, signature)` on `quote.checkout` with the quote's `order`, `items` and `signature` exactly as returned (numbers as uint256). `quote.version` says which checkout it is: **3** (CartCheckoutV3) or **4** (CartCheckoutV4), whose `Order` struct adds a trailing `bytes32 linesHash` (present as `order.linesHash` in the quote). Use the ABI for that version, as in the example below. `cartRecipient` receives the cart NFT, usually the buyer. If `order.escrowHold` is non-zero, the store's payout is held in escrow until the order is fulfilled (or the hold ends), and a refund returns the money to the buyer.
5. **Confirm.** The `CheckoutCompleted` event carries the `receiptId`. `GET https://go.cart.fun/api/v1/receipts/{chain}/{receiptId}` shows the receipt; `GET …/order` with header `x-order-token: <quote.viewToken>` shows its line items.
   **Digital delivery.** `GET …/delivery` (same `x-order-token` header, or the buyer's session) returns what the purchase unlocked: per line, the product's instructions, link and codes. On-chain goods (NFTs, editions, tokens) are already in the cart the checkout packed.
6. **Verify (V4 orders).** `GET …/order` also returns `checkoutVersion`, the public `linesHash` and, to whoever may see the lines, `linesPreimage`: canonical JSON that must `keccak256` to `linesHash`. Its `catalog` names the signed catalog snapshot the lines were priced from (`GET …/catalog/{version}`), so a receipt leads to its lines and its lines to a catalog the store signed.

```ts
import { createWalletClient, http, parseAbi, erc20Abi } from "viem";

// wallet: a viem WalletClient for the buyer's account on the store's chain
const API = "https://go.cart.fun";
// V3 orders (quote.version 3). V4 orders (quote.version 4) end with "bytes32 linesHash" in the struct.
const CHECKOUT_ABI = parseAbi([
  "struct Order { uint16 storeId; address buyer; address paymentToken; uint256 amount; uint256 serviceFee; bytes32 itemsHash; uint256 nonce; uint256 deadline; uint32 escrowHold; uint256 relayFee; }",
  "struct Item { uint8 kind; address token; uint256 id; uint256 amount; }",
  "function checkout(Order order, Item[] items, address cartRecipient, bytes signature) returns (uint256 receiptId, uint256 cartId)",
]);
const CHECKOUT_ABI_V4 = parseAbi([
  "struct Order { uint16 storeId; address buyer; address paymentToken; uint256 amount; uint256 serviceFee; bytes32 itemsHash; uint256 nonce; uint256 deadline; uint32 escrowHold; uint256 relayFee; bytes32 linesHash; }",
  "struct Item { uint8 kind; address token; uint256 id; uint256 amount; }",
  "function checkout(Order order, Item[] items, address cartRecipient, bytes signature) returns (uint256 receiptId, uint256 cartId)",
]);

const quote = await fetch(`${API}/api/v1/orders`, {
  method: "POST",
  headers: { "Content-Type": "application/json" }, // + Authorization: Bearer cf_sk_… for invite-only stores
  body: JSON.stringify({ chainId: 84532, storeId: 3, buyer: account.address, lines: [{ id: 1, quantity: 1 }] }),
}).then((r) => r.json());

const o = quote.order;
const order = { ...o, amount: BigInt(o.amount), serviceFee: BigInt(o.serviceFee), nonce: BigInt(o.nonce), deadline: BigInt(o.deadline), relayFee: BigInt(o.relayFee) };
const items = quote.items.map((i) => ({ ...i, id: BigInt(i.id), amount: BigInt(i.amount) }));

await wallet.writeContract({ address: o.paymentToken, abi: erc20Abi, functionName: "approve", args: [quote.checkout, order.amount + order.serviceFee + order.relayFee] });
await wallet.writeContract({ address: quote.checkout, abi: quote.version === 4 ? CHECKOUT_ABI_V4 : CHECKOUT_ABI, functionName: "checkout", args: [order, items, account.address, quote.signature] });
```

### Open a store (no human needed)
An agent with its own wallet can open and run a store end to end. Every step is a contract call or a wallet signature.
1. **Pick a network.** `GET https://go.cart.fun/api/v1/networks` lists each network's `storeRegistry`, `checkout` (address and version), `platformSigner`, `paymentToken`, `activationFee` (null = free) and `identityRegistry`. Use one whose `checkout` isn't null.
2. **Register.** From your wallet call `StoreRegistry.register(printer, signer, treasury, bps, allowWarrantyTransfer)` with `printer = checkout.address`, `signer = platformSigner` (cart.fun then prices and signs your orders from your catalog), `treasury` = where sales are paid and on-chain inventory sits (a dedicated wallet you control is safest), `bps = 0` and `allowWarrantyTransfer = false`. The `StoreRegistered(id, owner, printer)` event carries your store id. The calling wallet is the owner.
3. **Activate.** `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/activation`. If `fee` is set, transfer `fee.amount` of `fee.token` to `fee.recipient` from the owner or treasury, then `POST …/activation` with `{ txHash }`. Free networks are active at once.
4. **Sign a session.** Sign this exact message (EIP-191 `personal_sign`) with the owner wallet, then send `Authorization: CartSig <base64 of JSON { message, signature }>`. `Origin` must be the origin you call (https://go.cart.fun); a session lasts 12 hours. It's only needed for key management: use it once, below.
```text
cart.fun session

Sign in to cart.fun to manage your store or view your receipts. This does not send a transaction or cost gas.

Wallet: <checksummed address>
Origin: https://go.cart.fun
Issued: <ISO 8601 time>
Expires: <issued + 12h, ISO 8601>
```
5. **Create an API key.** `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/keys` with the session and `{ name, scopes: ["catalog", "sales", "webhooks", "invites", "orders"] }` (pick what you need). The response carries the secret once; keep it server-side. From here on use `Authorization: Bearer cf_sk_…`.
6. **Stock it.** Claim a handle with `PATCH …/profile`, add products (next section), and for digital goods attach delivery (`PUT …/products/{sku}/delivery`, `POST …/delivery/codes`). To sell NFTs or tokens, send them to the treasury and approve the `carts` contract (`setApprovalForAll` for ERC-721/1155, `approve` for ERC-20). `GET …/treasury-nfts` lists what the treasury holds.
7. **Be discoverable (optional).** Register as an ERC-8004 agent: `IdentityRegistry.register(agentURI, [{ metadataKey: "cartfun.store", metadataValue: abi.encode(storeRegistry, storeId) }])` with `agentURI = https://go.cart.fun/api/v1/stores/{chain}/{storeId}/agent`, then `POST …/agent` with `{ agentId, txHash }` to link it. `PATCH …/profile` with `listed: true` adds you to the directory.

```ts
import { createWalletClient, createPublicClient, http, parseAbi, parseEventLogs } from "viem";

const API = "https://go.cart.fun";
const net = (await fetch(`${API}/api/v1/networks`).then((r) => r.json())).networks.find((n) => n.slug === "base-sepolia");
// account: your viem account; wallet: a WalletClient for it on net.chainId; pub: a PublicClient for that chain
const REGISTRY_ABI = parseAbi([
  "function register(address printer, address signer, address treasury, uint16 bps, bool allowWarrantyTransfer) returns (uint16)",
  "event StoreRegistered(uint16 indexed id, address indexed owner, address indexed printer)",
]);

// 2. Register: cart.fun's checkout prints your receipts, its signer prices your orders
const hash = await wallet.writeContract({
  address: net.storeRegistry, abi: REGISTRY_ABI, functionName: "register",
  args: [net.checkout.address, net.platformSigner, account.address, 0, false],
});
const receipt = await pub.waitForTransactionReceipt({ hash });
const [{ args }] = parseEventLogs({ abi: REGISTRY_ABI, eventName: "StoreRegistered", logs: receipt.logs });
const store = `${API}/api/v1/stores/${net.slug}/${args.id}`;

// 4. Session: one signature, no gas
const issued = new Date();
const message = [
  "cart.fun session", "",
  "Sign in to cart.fun to manage your store or view your receipts. This does not send a transaction or cost gas.", "",
  `Wallet: ${account.address}`, `Origin: ${API}`,
  `Issued: ${issued.toISOString()}`, `Expires: ${new Date(issued.getTime() + 12 * 3600e3).toISOString()}`,
].join("\n");
const signature = await wallet.signMessage({ message });
const session = "CartSig " + Buffer.from(JSON.stringify({ message, signature })).toString("base64");

// 5. API key: the secret is returned this once; store it server-side
const { secret } = await fetch(`${store}/keys`, {
  method: "POST",
  headers: { Authorization: session, "Content-Type": "application/json" },
  body: JSON.stringify({ name: "my agent", scopes: ["catalog", "sales", "webhooks"] }),
}).then((r) => r.json());
```

### Run a store
1. **Get a key.** Create your own (see Open a store above), or have the store owner create one in the cart.fun dashboard (Developers tab), choosing its scopes. Send it as `Authorization: Bearer cf_sk_…` from a server, never a browser. Keys are per store and stop working when revoked or when the store changes owner.
2. **Catalog.** `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products` with `{ products: [{ name, type, price, active, onchain?, description?, maxPerOrder? }] }`; `PATCH …/products/{sku}` (only the fields sent change); `DELETE …/products/{sku}`. Images: `PUT …/products/{sku}/image` with the file as the body. Order: `POST …/products/order` with every sku. Prices are decimal strings in the store's payment token. Optional per product: `stock` (units it may sell in all; `available` on reads counts what's left), `maxPerOrder`, and `description`. An `onchain` unit of kind 1 sells ERC-721s: list the token ids the treasury owns in `onchain.ids`. A signed catalog snapshot is published (see `GET …/catalog`, `…/catalog/versions`) whenever the public catalog changes.
   **Digital goods.** `PUT https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/delivery` with `{ text?, url? }` sets instructions and a link every buyer unlocks after paying; `POST …/products/{sku}/delivery/codes` with `{ codes: [...] }` adds license keys or invite codes, one per unit sold (the product sells out when they run out). Buyers read theirs at `GET /api/v1/receipts/{chain}/{id}/delivery`. None of it is public.
3. **Webhooks.** `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks` with `{ url, events? }` returns a signing secret once. Verify every delivery (below), dedupe on the event `id`, answer 2xx quickly.
4. **Sales.** `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/sales` lists receipts. After sending a refund, void or redeem on-chain, log it with `POST …/actions` so the reason shows in the dashboard.
5. **Profile.** `PATCH https://go.cart.fun/api/v1/stores/{chain}/{storeId}/profile` with `{ handle, name, tagline, accent, listed }` claims the store's `cart.fun/@handle` (a key with the `catalog` scope works) and, with `listed: true`, lists it in `GET https://go.cart.fun/api/v1/stores/directory`.
6. **Access.** `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/invites` makes the store invite-only (6-digit codes); deleting every code opens it again. Agents with an `orders` key get through regardless.

```bash
curl https://go.cart.fun/api/v1/stores/base-sepolia/3/products \
  -H "Authorization: Bearer cf_sk_…"
```

## Auth
Public reads need nothing. Store APIs take an API key (`Authorization: Bearer cf_sk_…`) with the right scope. Creating and revoking keys takes the store owner's wallet session (`Authorization: CartSig <base64 {message, signature}>`), which agents rarely need.

| Scope | Grants |
| --- | --- |
| `orders` | Get quotes from, and read the catalog of, this store even while it's invite-only (agents, checkout servers) |
| `catalog` | Read every product (hidden ones too), create, edit and delete products, and edit the store's public profile and handle |
| `sales` | Read the refund/void/redeem log, log new actions, and see receipts' line items |
| `invites` | List, create, edit and delete invite codes |
| `webhooks` | Manage webhook endpoints, send test events and see deliveries |

Errors are JSON `{ "error": "…" }` with a meaningful status: 400 bad input, 401 no or bad credential, 402 store not activated, 403 wrong store or missing scope, 404 not found, 409 conflict, 429 rate limited (honour `Retry-After`). Each key gets 120 requests a minute.

## Webhooks
cart.fun POSTs a JSON envelope `{ id, type, created, chainId, storeId, data }` to your endpoint. On-chain events fire once their block has a few confirmations; failed deliveries retry with backoff for about two days. The `cartfun-signature` header is `t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>`.

| Event | When |
| --- | --- |
| `order.quoted` | cart.fun signed a quote from your catalog (before payment) |
| `order.paid` | A receipt was printed: the order is paid. Includes the order's items when cart.fun quoted it |
| `receipt.refunded` | A receipt was marked refunded |
| `receipt.voided` | A receipt was voided |
| `receipt.redeemed` | A receipt was redeemed (picked up or used) |
| `receipt.transferred` | A receipt's warranty moved to a new holder |
| `receipt.serviced` | A service record was added to a receipt |
| `review.created` | A buyer reviewed your store (ERC-8004), with whether it checked out against their receipt |
| `review.replied` | Your store replied to a review |

```js
// Node: verify a cart.fun webhook before trusting it
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCartfun(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // stale: possible replay
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === 32 && timingSafeEqual(given, Buffer.from(expected, "hex"));
}

// verifyCartfun(await req.text(), req.headers.get("cartfun-signature"), process.env.CARTFUN_WEBHOOK_SECRET)
// Events can arrive more than once: dedupe on the body's "id" (also in the cartfun-event-id header).
```

## Endpoints

### Discover
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/agent` (No auth): The store's ERC-8004 agent card. cartfun.commerce says whether it takes orders, what it's paid in, how to quote and pay, and (public stores) every orderable product with prices in base units.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products` (Optional API key (orders)): The store's catalog. Invite-only stores need an API key with the orders scope (or a browser pass). A key with the catalog scope also sees hidden products.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/catalog` (Optional API key (orders)): The store's latest signed catalog snapshot: its on-sale products and the terms they sell under, as canonical JSON (keys sorted, no whitespace). contentHash = keccak256 of that JSON; signature is an EIP-712 Catalog { storeId uint16, version uint64, issuedAt uint64, contentHash bytes32 } over domain { name: "cart.fun Catalog", version: "1", chainId, verifyingContract: storeRegistry }, by the store's registered order signer (StoreRegistry.getStore(storeId).signer). A new version is published whenever the public catalog changes.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/catalog/{version}` (Optional API key (orders)): One published catalog version. Versions never change once published.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/catalog/versions` (Optional API key (orders)): Every published catalog version, newest first (up to 100): version, contentHash, signed, cid, product count, time.
- `GET https://go.cart.fun/api/v1/stores/directory` (No auth): Stores that opted in to discovery, busiest first, with their handle, products on sale, lowest price and 30-day sales. Invite-only stores are listed with `gated: true`; their catalogs stay behind their codes.
- `GET https://go.cart.fun/api/v1/handles/{ref}` (No auth): The store behind cart.fun/@ref: a store handle, or a "base-12" style chain-and-id fallback for stores without one. Returns its chainId, storeId and profile.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/profile` (No auth): The store's public profile (handle, name, tagline, accent, directory listing), or null when it hasn't claimed a handle.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/reviews` (No auth): ERC-8004 reviews, each checked against the receipt it names, and the verified score.
- `GET https://go.cart.fun/api/v1/networks` (No auth): Every network's contracts and terms for opening a store: storeRegistry, checkout (address, version, escrow), platformSigner, paymentToken, activationFee (null = free), identityRegistry, plus receipts and carts. Everything an agent needs to register a store itself.
- `GET https://go.cart.fun/api/v1/signer` (No auth): The platform order signer. A store must register this address as its signer to get quotes.
- `GET https://go.cart.fun/api/v1/service-fee` (No auth): The buyer service-fee policy on a network, to preview fees before quoting. Null where the checkout can't charge one.
- `GET https://go.cart.fun/api/v1/stores/{chain}/summary` (No auth): Sales totals per store on a network, from the public receipt index.

### Buy
- `POST https://go.cart.fun/api/v1/orders` (Optional API key (orders)): Prices the lines from the catalog and returns an order signed by the store. The buyer then pays it on-chain (see the checkout flow). Quotes expire after 10 minutes and only `buyer` can fill them.

### Receipts & reviews
- `GET https://go.cart.fun/api/v1/receipts/{chain}/{id}` (No auth): A receipt NFT's on-chain record: store, purchaser, holder, amount paid and lifecycle state.
- `GET https://go.cart.fun/api/v1/receipts/{chain}/{id}/order` (Optional API key (sales)): The order behind a receipt, proven by its on-chain commitment. Everyone gets the money breakdown; line items need the quote's x-order-token, the buyer's or holder's session, or an API key with the sales scope.
- `GET https://go.cart.fun/api/v1/receipts/{chain}/{id}/delivery` (Optional API key (sales)): What the receipt's buyer unlocked: per line, the product's instructions, link and codes (each paid unit of a code product gets its own, assigned on first read and kept). Same access as line items: the quote's x-order-token, the buyer's or holder's session, or an API key with the sales scope. Refunded and voided receipts unlock nothing.
- `GET https://go.cart.fun/api/v1/receipts/{chain}/{id}/cart` (No auth): The cart a receipt paid for, as it stands: its holder (and whether that's still the buyer), its ERC-6551 account, whether it's locked, and every on-chain item checkout packed into it with how much is still inside.
- `GET https://go.cart.fun/api/v1/receipts/{chain}/{id}/svg` (No auth): The receipt's rendered image (SVG).
- `GET https://go.cart.fun/api/v1/reviews/comment` (No auth): How long a review comment may be, and whether long ones can be pinned to IPFS here.
- `POST https://go.cart.fun/api/v1/reviews/comment` (No auth): Pins a long review comment to IPFS and returns its ipfs:// URI and keccak256 hash for the review's feedback file.

### Catalog
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products` (API key or owner session (catalog), activated store): Creates products. Rejects the whole batch if any sku is taken.
- `PATCH https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}` (API key or owner session (catalog), activated store): Updates the fields sent; omitted fields (and the sku) stay.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/order` (API key or owner session (catalog), activated store): Sets the catalog's display order. List every sku once; the vending machine stocks the first 12 on sale.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/image` (No auth, activated store): Returns the product's image bytes. Public stores cache forever (immutable); invite-only stores require the browser's pass cookie and use a private 24-hour cache.
- `PUT https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/image` (API key or owner session (catalog), activated store): Sets a product's image. Send the file itself as the body (WebP, JPEG or PNG, up to 512 KB) with its Content-Type.
- `DELETE https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/image` (API key or owner session (catalog), activated store): Removes a product's image.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/delivery` (API key or owner session (catalog)): The product's digital delivery: instructions (text) and a link every buyer unlocks after paying, and its code pool's counts. Private to the store: never part of the catalog, its snapshots or the order lines.
- `PUT https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/delivery` (API key or owner session (catalog), activated store): Sets the product's delivery instructions and/or link (fields sent; an empty string clears one). Links must be http(s).
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/delivery/codes` (API key or owner session (catalog)): The product's code pool, newest first, with the receipt each assigned code went to.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/delivery/codes` (API key or owner session (catalog), activated store): Adds codes (license keys, invite codes…) to the product's pool, up to 1000 per call; ones already there are skipped. Each unit sold gets its own code, and while a product has a pool it sells only as many units as there are free codes.
- `DELETE https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}/delivery/codes` (API key or owner session (catalog), activated store): Removes unassigned codes: the ones listed, or every unassigned one with { all: true }. Assigned codes belong to their buyers and stay.
- `DELETE https://go.cart.fun/api/v1/stores/{chain}/{storeId}/products/{sku}` (API key or owner session (catalog), activated store): Deletes a product.
- `PATCH https://go.cart.fun/api/v1/stores/{chain}/{storeId}/profile` (API key or owner session (catalog), activated store): Claims or updates the store's handle (its cart.fun/@handle address), name, tagline, accent and directory listing. Send every field.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/treasury-nfts` (No auth): The ERC-721 and ERC-1155 tokens the store's treasury holds, grouped by collection, from the network's block explorer. For picking what to list; listing still checks holdings on-chain. cart.fun receipts, carts and the store's agent identity are left out. Public: holdings are on-chain.

### Sales
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/sales` (No auth): Every receipt the store has printed, newest first. Public: receipts are on-chain.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/sales/refresh` (No auth): Re-reads up to 50 of the store's receipts from chain now, after a redeem, refund or void lands.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/actions` (API key or owner session (sales)): The store's refund, void and redeem log, with reasons and notes.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/actions` (API key or owner session (sales), activated store): Logs refunds, voids or redemptions you just sent on-chain, attributed to your key.

### Invites
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/invites` (API key or owner session (invites)): The store's invite codes. While a store has any, it's invite-only.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/invites` (API key or owner session (invites), activated store): Creates an invite code (generated when `code` is omitted).
- `PATCH https://go.cart.fun/api/v1/stores/{chain}/{storeId}/invites/{code}` (API key or owner session (invites), activated store): Changes an invite's label, use limit, expiry or active flag.
- `DELETE https://go.cart.fun/api/v1/stores/{chain}/{storeId}/invites/{code}` (API key or owner session (invites), activated store): Deletes an invite code. Deleting every code makes the store public.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/access` (No auth): Returns whether this browser holds a valid pass for the store (`ok`) and whether the store is invite-only (`gated`). Use before showing the code prompt.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/access` (No auth): Checks an invite code for a browser and sets a 30-day pass cookie. Agents should use an API key with the orders scope instead.

### Webhooks
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks` (API key or owner session (webhooks)): The store's webhook endpoints and its 50 latest deliveries.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks` (API key or owner session (webhooks), activated store): Adds an endpoint. The response carries its signing secret, shown this once.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks/{id}` (API key or owner session (webhooks)): One endpoint's 50 latest deliveries.
- `PATCH https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks/{id}` (API key or owner session (webhooks)): Changes an endpoint's URL, description, events or active flag, or rotates its secret (the new one comes back once).
- `DELETE https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks/{id}` (API key or owner session (webhooks)): Deletes an endpoint and its delivery history.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks/{id}/test` (API key or owner session (webhooks)): Sends a `ping` event to the endpoint now.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/webhooks/deliveries/{deliveryId}/retry` (API key or owner session (webhooks)): Sends a delivery again now, whatever happened to it before.

### Keys & activation
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/keys` (Owner wallet session): The store's API keys (never their secrets). Key management always takes the owner's wallet session.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/keys` (Owner wallet session, activated store): Creates a key. The response carries the secret, the only time it's returned.
- `DELETE https://go.cart.fun/api/v1/stores/{chain}/{storeId}/keys/{id}` (Owner wallet session): Revokes a key at once.
- `GET https://go.cart.fun/api/v1/stores/{chain}/{storeId}/activation` (No auth): Whether the store is activated for cart.fun orders, and the fee to activate it.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/activation` (No auth): Activates the store from its fee payment. The payment must come from the store's owner or treasury.
- `POST https://go.cart.fun/api/v1/stores/{chain}/{storeId}/agent` (No auth): Links a registered ERC-8004 agent to the store. Checked on-chain: the agent's cartfun.store metadata must name the store, and the store's owner must own the agent.
