{
  "openapi": "3.1.0",
  "info": {
    "title": "NOMAX Pokemon shop API",
    "version": "1.0.0",
    "summary": "Live data and checkout for pokemonpack.com, a Pokémon card shop in Kingston, Ontario, Canada.",
    "description": "Public, read-only JSON about the shop (packs, binders, Vault singles, the inventory list), an MCP server for AI assistants, and the one POST that starts a checkout. No sign-in. AI agents and programs are welcome; there is no bot check.\n\nAll prices and products are subject to change.\n\nRules: prices are in Canadian dollars. Pack-binder cards aren't sold one by one (they are picked as part of a pack), so they have no price of their own: the buyer pays the pack price. Only Vault singles have a buy price (`price_cad`, and `buy_price_cad` in the inventory list). Silver and Gold binder cards are also sold on their own as Vault singles, with a buy price; each is one card, so once it sells either way it is gone from both. The inventory download (`/inventory.csv`, `/inventory.json`) lists a reference market value (`value_cad`) for every card, to help compare. No quantities are published. Identical cards are listed once, with every id in `ids`. A checkout answers with a Stripe payment page; the order is placed when that page is paid.",
    "termsOfService": "https://pokemonpack.com/terms",
    "contact": { "name": "NOMAX Pokemon", "email": "pokemaster@pokemonpack.com", "url": "https://pokemonpack.com/" }
  },
  "externalDocs": { "description": "Everything in one Markdown file (llms-full.txt)", "url": "https://pokemonpack.com/llms-full.txt" },
  "servers": [{ "url": "https://pokemonpack.com", "description": "The shop" }],
  "tags": [
    { "name": "shop", "description": "Live shop data (JSON)" },
    { "name": "mcp", "description": "Model Context Protocol server for AI assistants" },
    { "name": "checkout", "description": "Starting a checkout (Stripe payment page)" },
    { "name": "documents", "description": "Text, XML and HTML documents for crawlers and AI assistants" }
  ],
  "paths": {
    "/api/shop": {
      "get": {
        "tags": ["shop"],
        "operationId": "getShop",
        "summary": "The whole shop, or one binder's cards",
        "description": "Without `binder`: packs, binders, Vault singles (with prices), shipping, policies, how to buy and `agent_checkout`. With `binder`: the cards in that binder (pack-binder cards are picked as part of a pack, so they have no price of their own; `vault` lists the priced Vault singles).",
        "parameters": [
          { "name": "binder", "in": "query", "required": false, "description": "A binder id.", "schema": { "$ref": "#/components/schemas/BinderId" } }
        ],
        "responses": {
          "200": { "description": "The shop, or one binder", "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/Shop" }, { "$ref": "#/components/schemas/BinderCards" }] } } } },
          "404": { "description": "No such binder", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "binders": { "type": "array", "items": { "type": "string" } } } } } } },
          "503": { "$ref": "#/components/responses/Busy" }
        }
      }
    },
    "/inventory.json": {
      "get": {
        "tags": ["shop"],
        "operationId": "getInventory",
        "summary": "Every card in stock with a reference market value in CAD",
        "parameters": [
          { "$ref": "#/components/parameters/InvBinder" },
          { "$ref": "#/components/parameters/InvSet" },
          { "$ref": "#/components/parameters/InvQ" }
        ],
        "responses": {
          "200": { "description": "The inventory list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Inventory" } } } },
          "404": { "description": "The list is switched off right now", "content": { "text/plain": { "schema": { "type": "string" } } } },
          "503": { "$ref": "#/components/responses/Busy" }
        }
      }
    },
    "/inventory.csv": {
      "get": {
        "tags": ["shop"],
        "operationId": "getInventoryCsv",
        "summary": "The inventory list as CSV (same columns as inventory.json)",
        "parameters": [
          { "$ref": "#/components/parameters/InvBinder" },
          { "$ref": "#/components/parameters/InvSet" },
          { "$ref": "#/components/parameters/InvQ" },
          { "name": "nonote", "in": "query", "required": false, "schema": { "type": "string", "enum": ["1"] }, "description": "Leave out the first line (the # note) for strict CSV readers." }
        ],
        "responses": {
          "200": { "description": "CSV. The first line is a note starting with \"# \" (All prices and products are subject to change. and what the columns mean; no commas in it) unless nonote=1; then the header row and one row per card.", "content": { "text/csv": { "schema": { "type": "string" } } } },
          "404": { "description": "The list is switched off right now", "content": { "text/plain": { "schema": { "type": "string" } } } },
          "503": { "$ref": "#/components/responses/Busy" }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": ["mcp"],
        "operationId": "mcp",
        "summary": "MCP server (JSON-RPC 2.0, Streamable HTTP transport, JSON answers)",
        "description": "Model Context Protocol server. Methods: initialize, ping, tools/list, tools/call, and notifications (answered 202 with no body). Tools (read-only): search_cards, get_card, list_packs, list_binder, get_shipping_and_policies, how_to_checkout, site_guide. No session id; no SSE stream. A batch (array) gets an array back.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/JsonRpcRequest" }, { "type": "array", "maxItems": 20, "items": { "$ref": "#/components/schemas/JsonRpcRequest" } }] },
            "example": { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "search_cards", "arguments": { "query": "charizard", "limit": 5 } } } } }
        },
        "responses": {
          "200": { "description": "The JSON-RPC answer (or an array of answers for a batch)", "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/JsonRpcResponse" }, { "type": "array", "items": { "$ref": "#/components/schemas/JsonRpcResponse" } }] } } } },
          "202": { "description": "A notification was accepted (no body)" },
          "400": { "description": "Not JSON, not JSON-RPC, or an unsupported MCP-Protocol-Version header", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcResponse" } } } },
          "413": { "description": "More than 64 KB", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcResponse" } } } }
        }
      },
      "get": {
        "tags": ["mcp"],
        "operationId": "mcpInfo",
        "summary": "No SSE stream: answers 405 with a short description of the server",
        "responses": {
          "405": { "description": "Use POST. The body describes the server.", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/functions/v1/create-checkout": {
      "servers": [{ "url": "https://phahluzseiddcugqwewu.supabase.co", "description": "The shop's checkout function" }],
      "post": {
        "tags": ["checkout"],
        "operationId": "createCheckout",
        "summary": "Start a checkout: answers with a Stripe payment page",
        "description": "The same call the shop's Pay button makes. The cards are held for 30 minutes while the payment page is open; the order is placed when the page is paid. `cardIds` must follow the pack's `slot_order`: card i comes from the binder at position i. A card can be used once per order; at most 5 packs per order. Limits per connection: 3 open checkouts at a time and 20 tries an hour. Prices are in CAD; shipping is added from `shipTo`. All sales are final. The live `agent_checkout` section of /api/shop is the source of truth.",
        "security": [{ "apikey": [] }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckoutRequest" },
            "example": { "packs": [{ "packId": "p10", "cardIds": [101, 102, 201, 202, 203, 204, 301] }], "singles": [], "shipTo": "CA" } } }
        },
        "responses": {
          "200": { "description": "The payment page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckoutAnswer" } } } },
          "400": { "description": "A bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckoutProblem" } } } },
          "409": { "description": "Cards just sold (see gone), a bad code, or another problem with the order", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckoutProblem" } } } }
        }
      }
    },
    "/search": {
      "get": {
        "tags": ["documents"],
        "operationId": "searchPage",
        "summary": "Search page (HTML): cards, items and packs in stock",
        "parameters": [{ "name": "q", "in": "query", "required": false, "schema": { "type": "string", "maxLength": 100 }, "description": "Words to find in the card name, set, number, printing or rarity." }],
        "responses": { "200": { "description": "The results page", "content": { "text/html": { "schema": { "type": "string" } } } } }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": ["documents"], "operationId": "llmsTxt", "summary": "Short guide for AI assistants (llmstxt.org)",
        "responses": { "200": { "description": "Markdown", "content": { "text/plain": { "schema": { "type": "string" } } } } }
      }
    },
    "/llms-full.txt": {
      "get": {
        "tags": ["documents"], "operationId": "llmsFullTxt", "summary": "Everything in one Markdown file, made from the live shop",
        "responses": { "200": { "description": "Markdown", "content": { "text/plain": { "schema": { "type": "string" } } } }, "503": { "$ref": "#/components/responses/Busy" } }
      }
    },
    "/sitemap.xml": {
      "get": {
        "tags": ["documents"], "operationId": "sitemap", "summary": "Sitemap of every public page",
        "responses": { "200": { "description": "sitemaps.org XML", "content": { "application/xml": { "schema": { "type": "string" } } } } }
      }
    },
    "/google-feed.xml": {
      "get": {
        "tags": ["documents"], "operationId": "googleFeed", "summary": "Packs and Vault singles as a Google Merchant Center RSS 2.0 feed",
        "responses": { "200": { "description": "RSS 2.0 with the g: namespace", "content": { "application/xml": { "schema": { "type": "string" } } } } }
      }
    },
    "/.well-known/api-catalog": {
      "get": {
        "tags": ["documents"], "operationId": "apiCatalog", "summary": "RFC 9727 API catalog (a linkset)",
        "responses": { "200": { "description": "Linkset", "content": { "application/linkset+json": { "schema": { "type": "object" } } } } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apikey": { "type": "apiKey", "in": "header", "name": "apikey", "description": "The shop's public (publishable) key, as given in /api/shop agent_checkout.headers.apikey. It is not a secret." }
    },
    "parameters": {
      "InvBinder": { "name": "binder", "in": "query", "required": false, "schema": { "$ref": "#/components/schemas/BinderId" }, "description": "Only this binder." },
      "InvSet": { "name": "set", "in": "query", "required": false, "schema": { "type": "string", "maxLength": 100 }, "description": "Only sets whose name contains this text." },
      "InvQ": { "name": "q", "in": "query", "required": false, "schema": { "type": "string", "maxLength": 100 }, "description": "Only cards whose name contains this text." }
    },
    "responses": {
      "Busy": { "description": "The shop's data can't be loaded right now; try again in a minute (Retry-After: 60)" }
    },
    "schemas": {
      "BinderId": { "type": "string", "enum": ["blue", "green", "amber", "red", "purple", "silver", "gold", "vault"] },
      "Ids": { "type": "array", "items": { "type": "integer" }, "description": "Present when identical cards are listed together: every id of them (any works, each once per order). Not a count of copies." },
      "Photos": { "type": "array", "minItems": 1, "maxItems": 2, "items": { "type": "string", "format": "uri" }, "description": "Photos of the actual card (front, then back). Without it, the image is a stock image." },
      "VaultCard": {
        "type": "object",
        "required": ["id", "name", "condition", "language", "price_cad", "price_usd", "url", "buy_url"],
        "properties": {
          "id": { "type": "integer" }, "ids": { "$ref": "#/components/schemas/Ids" },
          "name": { "type": "string" }, "kind": { "type": ["string", "null"], "enum": ["Pokémon", "Trainer", "Energy", "Item", null] },
          "set": { "type": ["string", "null"] }, "number": { "type": ["string", "null"] }, "printing": { "type": ["string", "null"] },
          "condition": { "type": "string" }, "language": { "type": "string" }, "rarity": { "type": ["string", "null"] },
          "image": { "type": ["string", "null"], "format": "uri" }, "photos": { "$ref": "#/components/schemas/Photos" },
          "price_cad": { "type": "number" }, "price_usd": { "type": "number" },
          "sale": { "type": ["object", "null"], "properties": { "was_cad": { "type": "number" }, "was_usd": { "type": "number" }, "pct_off": { "type": "number" }, "ends": { "type": ["string", "null"], "format": "date-time" } } },
          "url": { "type": "string", "format": "uri", "description": "The card's page" }, "buy_url": { "type": "string", "format": "uri", "description": "Opens the card in the shop (Add to cart)" }
        }
      },
      "PackCard": {
        "type": "object",
        "description": "A card in a pack binder. It isn't sold one by one (it is picked as part of a pack), so it has no price of its own; the inventory list gives a reference market value (value_cad).",
        "required": ["id", "name", "condition", "language", "pick_url"],
        "not": { "anyOf": [{ "required": ["price_cad"] }, { "required": ["price_usd"] }] },
        "properties": {
          "id": { "type": "integer" }, "ids": { "$ref": "#/components/schemas/Ids" },
          "name": { "type": "string" }, "set": { "type": ["string", "null"] }, "number": { "type": ["string", "null"] }, "printing": { "type": ["string", "null"] },
          "condition": { "type": "string" }, "language": { "type": "string" }, "rarity": { "type": ["string", "null"] },
          "types": { "type": "array", "items": { "type": "string" } }, "kind": { "type": ["string", "null"], "enum": ["Pokémon", "Trainer", "Energy", "Item", null] },
          "image": { "type": ["string", "null"], "format": "uri" }, "photos": { "$ref": "#/components/schemas/Photos" },
          "pick_url": { "type": "string", "format": "uri", "description": "Opens the card in the shop (Pick this card)" }
        }
      },
      "Pack": {
        "type": "object",
        "required": ["id", "name", "price_cad", "cards", "slots", "slot_order", "in_stock", "build_url"],
        "properties": {
          "id": { "type": "string" }, "name": { "type": "string" }, "price_cad": { "type": "number" }, "price_usd_approx": { "type": "number" },
          "cards": { "type": "integer", "description": "Cards in the pack (7)" },
          "slots": { "type": "array", "items": { "type": "object", "properties": { "binder": { "$ref": "#/components/schemas/BinderId" }, "picks": { "type": "integer" } } } },
          "slot_order": { "type": "array", "items": { "$ref": "#/components/schemas/BinderId" }, "description": "The binder of each slot, in the order cardIds must follow" },
          "in_stock": { "type": "boolean" }, "url": { "type": "string", "format": "uri" }, "build_url": { "type": "string", "format": "uri" }
        }
      },
      "MysteryPack": {
        "type": "object",
        "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "price_cad": { "type": "number" }, "image": { "type": ["string", "null"], "format": "uri" }, "about": { "type": "string" }, "buy_url": { "type": "string", "format": "uri" } }
      },
      "Binder": {
        "type": "object",
        "properties": { "id": { "$ref": "#/components/schemas/BinderId" }, "name": { "type": "string" }, "cards_to_choose_from": { "type": "integer" }, "cards_url": { "type": "string", "format": "uri" } }
      },
      "AgentCheckout": {
        "type": "object",
        "description": "How to start a checkout (the createCheckout operation), with the live rules.",
        "properties": {
          "about": { "type": "string" }, "method": { "const": "POST" }, "url": { "type": "string", "format": "uri" },
          "headers": { "type": "object", "properties": { "apikey": { "type": "string" }, "Content-Type": { "const": "application/json" } } },
          "body": { "type": "object" }, "answer": { "type": "object" }, "notes": { "type": "array", "items": { "type": "string" } }
        }
      },
      "Shop": {
        "type": "object",
        "required": ["packs", "binders", "vault", "how_to_buy", "agent_checkout", "shipping", "policies"],
        "properties": {
          "notice": { "const": "All prices and products are subject to change." },
          "about": { "type": "string" }, "site": { "type": "string", "format": "uri" }, "shop_name": { "type": "string" }, "currency": { "const": "CAD" },
          "fx": { "type": "object", "properties": { "cad_per_usd": { "type": "number" }, "date": { "type": ["string", "null"] } } },
          "how_to_buy": { "type": "array", "items": { "type": "string" } },
          "agent_checkout": { "$ref": "#/components/schemas/AgentCheckout" },
          "rules": { "type": "array", "items": { "type": "string" } },
          "shipping": { "type": "array", "items": { "type": "object", "properties": {
            "zone": { "type": "string", "enum": ["CA", "US", "INTL"] }, "name": { "type": "string" }, "cost": { "type": "string" },
            "cost_cad": { "type": "object", "properties": { "one_pack": { "type": ["number", "null"] }, "two_packs": { "type": ["number", "null"] }, "three_to_five_packs": { "type": ["number", "null"] } } } } } },
          "policies": { "type": "object", "additionalProperties": { "type": "string", "format": "uri" } },
          "packs": { "type": "array", "items": { "$ref": "#/components/schemas/Pack" } },
          "mystery_packs": { "type": "array", "items": { "$ref": "#/components/schemas/MysteryPack" } },
          "binders": { "type": "array", "items": { "$ref": "#/components/schemas/Binder" } },
          "vault": { "type": "array", "items": { "$ref": "#/components/schemas/VaultCard" } },
          "sell": { "type": "object" }, "pack_wheel": { "type": "object" },
          "mcp": { "type": "object", "properties": { "url": { "type": "string", "format": "uri" }, "protocol": { "type": "string" }, "read_only": { "type": "boolean" }, "tools": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "about": { "type": "string" } } } } } },
          "openapi": { "type": "string", "format": "uri" }, "search": { "type": "string" },
          "feeds": { "type": "object", "additionalProperties": { "type": "string", "format": "uri" } },
          "updated": { "type": "string", "format": "date-time" }
        }
      },
      "BinderCards": {
        "type": "object",
        "required": ["binder", "cards"],
        "properties": {
          "notice": { "const": "All prices and products are subject to change." },
          "binder": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/BinderId" }, "name": { "type": "string" }, "cards_to_choose_from": { "type": "integer" } } },
          "note": { "type": "string" },
          "packs_using_it": { "type": "array", "items": { "type": "string" } },
          "cards": { "type": "array", "items": { "oneOf": [{ "$ref": "#/components/schemas/PackCard" }, { "$ref": "#/components/schemas/VaultCard" }] } }
        }
      },
      "InventoryRow": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" }, "ids": { "type": "array", "items": { "type": "integer" }, "description": "Every id of this row's identical cards (at least the id itself)" },
          "name": { "type": "string" }, "set": { "type": ["string", "null"] }, "number": { "type": ["string", "null"] }, "printing": { "type": "string" },
          "condition": { "type": "string" }, "language": { "type": "string" }, "rarity": { "type": ["string", "null"] }, "kind": { "type": ["string", "null"] },
          "types": { "type": "array", "items": { "type": "string" } }, "binder": { "type": "string" },
          "value_cad": { "type": ["number", "null"], "description": "A reference market value in CAD for every card, to help compare (it also sorts the binders). Not a price: pack-binder cards are picked as part of a pack and only Vault singles are sold one by one" },
          "buy_price_cad": { "type": ["number", "null"], "description": "The buy price; Vault singles only (null for pack-binder cards)" }, "sale_percent": { "type": ["number", "null"] },
          "how_to_buy": { "type": "string" }, "image": { "type": ["string", "null"] }, "shop_link": { "type": "string", "format": "uri" }
        }
      },
      "Inventory": {
        "type": "object",
        "properties": {
          "notice": { "const": "All prices and products are subject to change." },
          "about": { "type": "string" }, "notes": { "type": "array", "items": { "type": "string" } }, "updated": { "type": "string", "format": "date-time" },
          "currency": { "const": "CAD" }, "count": { "type": "integer", "description": "Rows in this answer" }, "filters": { "type": "object" },
          "columns": { "type": "array", "items": { "type": "string" } }, "cards": { "type": "array", "items": { "$ref": "#/components/schemas/InventoryRow" } }
        }
      },
      "CheckoutRequest": {
        "type": "object",
        "required": ["shipTo"],
        "properties": {
          "packs": { "type": "array", "maxItems": 5, "items": { "type": "object", "required": ["packId", "cardIds"], "properties": {
            "packId": { "type": "string", "examples": ["p10", "p25", "p50"] },
            "cardIds": { "type": "array", "minItems": 7, "maxItems": 7, "items": { "type": "integer" }, "description": "One card id per slot, in the pack's slot_order" } } } },
          "singles": { "type": "array", "maxItems": 25, "items": { "type": "integer" }, "description": "Vault card ids" },
          "mystery": { "type": "array", "maxItems": 5, "items": { "type": "string" }, "description": "Mystery pack ids from mystery_packs (optional)" },
          "shipTo": { "type": "string", "enum": ["CA", "US", "INTL"], "description": "INTL = everywhere else; the country is picked on the payment page" },
          "code": { "type": "string", "maxLength": 40, "description": "Optional one-time code, like K7QM-4XPD" }
        }
      },
      "CheckoutAnswer": {
        "type": "object",
        "properties": { "ok": { "const": true }, "url": { "type": "string", "format": "uri", "description": "The Stripe payment page" }, "order_no": { "type": "integer" } }
      },
      "CheckoutProblem": {
        "type": "object",
        "properties": { "ok": { "const": false }, "error": { "type": "string" }, "gone": { "type": "array", "items": { "type": "integer" }, "description": "Card ids that just sold: pick others" } }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": { "const": "2.0" }, "id": { "type": ["string", "integer"] },
          "method": { "type": "string", "examples": ["initialize", "ping", "tools/list", "tools/call", "notifications/initialized"] },
          "params": { "type": "object" }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": ["jsonrpc"],
        "properties": {
          "jsonrpc": { "const": "2.0" }, "id": { "type": ["string", "integer", "null"] }, "result": { "type": "object" },
          "error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" }, "data": {} } }
        }
      }
    }
  }
}
