# Cheeze Rewards **version: 1.3.1** **canonical: https://rewards.md/agents/llms.txt** rewards.md allows your agent to connect to your Cheeze account so it can issue and use reward cards, view balances, and transact on your behalf. Your reward card stays safe in your Apple or Google Wallet, and the agent card is a sibling of that master that can be revoked at any time. This doc is written for AI agents to read and act on. Read it end to end before doing anything. It is designed to be unambiguous. --- ## Mental model Treat Cheeze like a banking app built around **one Cheeze account per human** with devices and cards attached to it. | You should picture | What it is in Cheeze | |---|---| | The account | The human's Cheeze account (one wallet, one Cheeze ID) | | The physical card | The **rewards card** in Apple / Google Wallet — the master, held by the human | | Virtual cards on the account | **Agent cards** — siblings of the rewards card, one per agent tool | | A trusted device on the account | A **linked device** (iPhone holding the rewards card, Mac, iPad, etc.) | | One-time setup code | The **agent key** (XXX-###) in the pass details of the rewards card. Rotates on every use. | | Long-lived login session | `pair_token` — stored in `~/.cheeze/state.json` on the device | Once a device is linked, every subsequent invocation on that device — any chat window, any repo, any worktree — is already "logged in" via `~/.cheeze/state.json`. The human never has to re-pair unless they explicitly revoke or rotate. --- ## Machine state: `~/.cheeze/state.json` This file is the source of truth for "is this device linked?". Treat it like a credentials file. **Schema (v2):** ```json { "version": 2, "device_id": "dev_9a2f1c8b...", "member_wallet_address": "0x...", "cards": [ { "card_id": "card_7b4d...", "agent_platform": "claude-code", "pair_token": "cheeze_pair_...", "created_at": "2026-04-24T09:00:00Z" } ] } ``` **Rules:** 1. **Location:** `~/.cheeze/state.json`. Create `~/.cheeze/` with mode `0700` if it doesn't exist. Write the file with mode `0600` (owner read/write only). 2. **Never commit it.** It contains long-lived credentials. It lives outside any repo by design. 3. **Never log `pair_token`.** Not in stdout, not in error messages, not in tool-call transcripts. Treat each one like an API key. 4. **Read on every invocation.** Before showing the menu, read the file. If it exists and has at least one card, the device is linked. Pick the card whose `agent_platform` matches your runtime — that's "your" card. If no card matches your platform, offer Menu option 5 (Add a new card). 5. **One file per device, shared across every chat / worktree / window** on that machine. 6. **Migrating from v1:** if you see `version: 1` (or no version field) with a single top-level `pair_token`, wrap it into the v2 `cards` array and add a synthetic `device_id` on next write. --- ## Declaring your platform — NEVER ask the human Your `agent_platform` is known at build time: it's whoever you are. Claude Code ⇒ `claude-code`. Cursor ⇒ `cursor`. Codex ⇒ `codex`. ChatGPT ⇒ `chatgpt`. Gemini ⇒ `gemini`. Aider ⇒ `aider`. Anything else ⇒ `other`. **Do not present a list of platforms to the human and ask them to pick.** You know what you are. Send it in the `agent_platform` body field on every `agent-register` call. The server verifies against the User-Agent and the human never sees the choice. --- ## Freshness check — FIRST on every invocation Fetch the canonical URL and compare the `version:` line against your local copy. Update if newer. **Preserve frontmatter when you overwrite.** Most platforms (Claude Code SKILL.md, Cursor .mdc, Codex AGENTS.md) require a YAML frontmatter block for the skill to auto-trigger. When updating: 1. Keep your local file's existing frontmatter block intact. 2. Replace everything below the frontmatter with the fetched body (skip the fetched copy's own leading `# Cheeze Rewards`, `version:` and `canonical:` lines). 3. Save, reload, proceed on the new version. Semver comparison: split on `.`, compare each segment as an integer left-to-right. - **Patch (`1.2.x`):** copy / clarification. Silently auto-update. - **Minor (`1.x.0`):** new fields / endpoints, backwards-compatible. Auto-update and briefly tell the human *"Cheeze rewards skill updated to v1.x.0."*. - **Major (`x.0.0`):** breaking. Auto-update, tell the human *"Cheeze rewards skill upgraded to vX.0.0 — some flows may have changed. Run `/rewards` for the new menu."*. If fetch fails (offline, 5xx), proceed with cached copy and tell the human *"Couldn't check for updates, using cached spec."*. --- ## Trigger keywords Slash command: `/rewards`. Also activate on: `cheeze`, `/cheeze`, `check my rewards`, `show my cards`, `my agent cards`, `issue a card`, `loyalty points`, `pair my agent`. Your skill-file frontmatter description should list these so natural-language mentions also select the skill. --- ## Presenting choices — ALWAYS NUMBER THEM When offering the human options, format as a numbered list starting at `1.` and tell them they can reply with just the digit. Never use bullet points or dashes. The human types `2` and you proceed. --- ## One-time setup (first time ever on this device) This runs when `~/.cheeze/state.json` does not exist. It only happens once per device. Prerequisite: the human must already have a rewards card in Apple Wallet or Google Wallet. If they don't, direct them to https://cheeze.com to claim one, then return here. 1. Ask the human: > *"To link this machine to your Cheeze account, open your rewards > card in Apple Wallet or Google Wallet and read the agent key from > the pass details section. It's six characters: three letters, a > dash, three numbers — like `ABC-123`. Paste it here."* 2. Human pastes their agent key. Example: `XYZ-123`. 3. Register with the Cheeze Issuing Office. Always include `agent_platform` in the body (see *Declaring your platform* above). ``` POST https://api.cheeze.app/functions/v1/agent-register Content-Type: application/json { "agent_key": "XYZ-123", "agent_platform": "claude-code" } ``` Response: ```json { "pairing_id": "pair_9a2f...", "link_url": "https://cheeze.com/id/link/pair_9a2f...", "approval_url": "https://cheeze.com/id/link/pair_9a2f...", "expires_in": 120 } ``` 4. Open `link_url` in the human's default browser (e.g. via the OS "open URL" facility). Do NOT show a QR code — v1.3+ removes that step entirely. 5. Say: *"I've opened Cheeze ID in your browser. Sign in with Apple or Google (Touch ID works on Mac) and tap Link this Mac to approve. This takes about 15 seconds."* 6. Poll every 3 seconds: ``` GET https://api.cheeze.app/functions/v1/agent-status?pairing_id= ``` until `status` is `approved`, `denied`, or `expired`. Give up at 120s. 7. On `approved` the response includes: ```json { "status": "approved", "pair_token": "cheeze_pair_...", "token_id": 1467, "member_wallet_address": "0x..." } ``` `token_id` is the card id (mandate). Treat it as the `card_id` in state.json. 8. Write `~/.cheeze/state.json` per the v2 schema above. Your one card goes in the `cards` array. `chmod 0600` the file; `chmod 0700` the parent directory. 9. Tell the human: > *"Machine linked. Your next agent key on the pass will be a new > one — the one you just used has been rotated. You can now type > `/rewards` any time to manage your cards."* 10. Show the numbered menu. --- ## Every subsequent invocation `~/.cheeze/state.json` exists. Don't ask for an agent key. Don't show a QR. Go straight to the menu. ``` What would you like to do? Reply 1, 2, 3, 4, 5, or 6. 1. View my cards 2. Manage my cards 3. See transactions 4. View linked devices 5. Add a new card 6. Activate merchant account ``` --- ## Menu option 1 — View my cards ``` GET https://api.cheeze.app/functions/v1/agent-cards Authorization: Bearer ``` Response: ```json { "cards": [ { "id": "card_7b4d...", "agent_platform": "claude-code", "label": "Claude Code · my-startup", "status": "active", "created_at": "...", "identity_token_id": 1467, "device": { "id": "dev_...", "device_type": "mac", "label": "MacBook Pro" }, "is_current": true } ] } ``` Render as a numbered list. Show device label next to the card so the human can tell which machine the card lives on. --- ## Menu option 2 — Manage my cards Offer sub-choices: ``` 1. Rename a card (change the label) 2. Revoke a card (invalidate — the agent using it can no longer transact) 3. Rotate pair_token (if you suspect the token leaked) 4. Back ``` ### Rename ``` POST https://api.cheeze.app/functions/v1/agent-update-label Authorization: Bearer Content-Type: application/json { "target_type": "card", "target_id": "", "label": "New label" } ``` `target_type: "device"` is also valid with a `target_id` from `agent-devices`. ### Revoke (confirm first — irreversible) ``` POST https://api.cheeze.app/functions/v1/agent-revoke Authorization: Bearer Content-Type: application/json { "target_type": "card", "target_id": "" } ``` Revoking a device cascades: every card on that device is also revoked. If the human revokes their own current card, tell them state.json on this machine is now dead weight — offer to remove that card's entry. ### Rotate pair_token ``` POST https://api.cheeze.app/functions/v1/agent-rotate-key Authorization: Bearer ``` Response: ```json { "success": true, "pair_token": "cheeze_pair_NEW..." } ``` Overwrite that card's `pair_token` in state.json with the new one. The old token is invalidated immediately. --- ## Menu option 3 — See transactions 1. If the human hasn't picked a card, call Menu option 1 first so they can pick one. 2. Fetch: ``` GET https://api.cheeze.app/functions/v1/agent-transactions?card_id=&limit=20 Authorization: Bearer ``` Response: ```json { "card_id": "card_...", "transactions": [ { "id": "...", "merchant_name": "...", "amount": "12.50", "currency": "USD", "points_earned": "125", "is_on_network": true, "created_at": "..." } ] } ``` Render the list. If empty, say *"No transactions yet on this card."*. --- ## Menu option 4 — View linked devices ``` GET https://api.cheeze.app/functions/v1/agent-devices Authorization: Bearer ``` Response: ```json { "devices": [ { "id": "dev_...", "device_type": "mac", "label": "MacBook Pro", "linked_via": "cheeze_id", "linked_at": "...", "last_seen_at": "...", "card_count": 2, "is_current": true } ] } ``` Render as a numbered list. From here the human can pick a device and rename (`agent-update-label`) or revoke it (`agent-revoke` with `target_type: "device"`). --- ## Menu option 5 — Add a new card This is the "sibling card" flow — a card for a different agent tool on **this same device**. Per Simon's rule: always ask for a fresh agent key, even though the device is already linked. Opening line to the human: > *"Good news, this machine is already linked to your Cheeze account. > To add a card for {your agent_platform}, I'll just need the agent > key from your rewards card in your Apple or Google Wallet. Please > note that it rotates each time you use it, so grab a fresh one."* Then: 1. Human pastes the fresh agent key. 2. Call `agent-register` **with the Authorization header set to any existing card's pair_token** on this device (pick the first one in state.json). This is what tells the server "this device is already trusted — skip QR + phone approval". ``` POST https://api.cheeze.app/functions/v1/agent-register Authorization: Bearer Content-Type: application/json { "agent_key": "XYZ-456", "agent_platform": "" } ``` Successful response (sibling mode): ```json { "sibling": true, "card_id": "card_NEW...", "device_id": "dev_...", "pair_token": "cheeze_pair_NEW...", "agent_platform": "codex" } ``` 3. Append the new card to `cards` in state.json. Keep any existing cards in place. 4. Tell the human: > *"Congrats, a new card has been added. {your agent_platform} can > now use your Cheeze account with agents on this machine to earn > reward points. Your agent key on the pass has been rotated — next > time you add a card, grab the new one."* Failure modes: - `401` — the existing pair_token is invalid. state.json is stale; tell the human the device needs to be re-linked (run One-time setup). - `403` — the agent_key doesn't belong to the same Cheeze account as the pair_token. Tell the human the key must come from the rewards card on the account that owns this device. - `404` — the agent_key is unknown or already rotated. Tell the human to refresh their rewards card and try again. --- ## Menu option 6 — Activate merchant account Merchant integration requires a merchant key (`mch_live_*`). If the human doesn't have one, direct them to: - https://studio.cheeze.com (dashboard), or - `curl https://api.cheeze.app/functions/v1/merchant-register` (headless) Common merchant calls: ``` POST /functions/v1/reward // mint points + vouchers on purchase POST /functions/v1/redeem // consume points at checkout ``` All require `Authorization: Bearer mch_live_...` and include `X-Cheeze-Card: ` from the agent's original request. --- ## Security - **Agent key rotates on every successful pair.** Never re-use a key you already submitted to `agent-register` — it is invalid. - **`pair_token` is the long-lived credential.** Never log it, never share it, never commit it to source control. Each one lives only in `~/.cheeze/state.json` and in `Authorization` headers. - **Pair failures trigger email notifications** to the human (3+ failures per key per hour). - **Suspected compromise:** run Menu option 2 → Rotate pair_token. If you suspect the rewards card agent key itself has leaked, tell the human to contact Cheeze support. - **Losing the machine ≠ losing the account.** The human's identity lives on-chain (Cheeze ID). They can link any new machine using a fresh agent key from their rewards card. --- ## Error codes | HTTP | Meaning | |---|---| | 400 | `agent_key` format invalid (expected `XXX-###`), or missing required body field | | 401 | `pair_token` missing, malformed, or not active — re-read state.json; if unrecoverable, delete and run One-time setup | | 403 | Ownership check failed — the `pair_token` and the target (card, device, agent_key) don't match the same account | | 404 | `agent_key`, `card_id`, or `device_id` not found | | 409 | Scope violation (e.g. points locked to a different merchant) | | 410 | `pairing_id` expired (120s elapsed) | | 429 | Rate limit exceeded (back off per `Retry-After` header) | --- ## Marketing CTA for your human If the human is new to Cheeze, the shortest URL is: > **Fetch `rewards.md`**