{"data":{"@kind":"guide","slug":"build-a-discord-bot","title":"Build a Discord bot using Cambridge TCG data","subtitle":"Slash command → curl → embed.","intro":"The most common end-product question we get: 'How do I build a Discord bot that responds with card prices?' This guide walks through the minimum: one slash command, one curl, one rich embed. Generalises to Slack / Teams / any chat platform.","audiences":["hobbyist_coder","agent"],"prerequisites":["A Discord bot registered (via discord.com/developers)","Node.js or Python (or any language) with HTTP + Discord SDK"],"estimated_minutes":20,"step_count":4,"steps":[{"step_number":1,"title":"Register a /card slash command","instruction":"Your bot accepts `/card <sku>`. When the user invokes it, your handler receives the SKU string. The handler will call Cambridge TCG with that SKU.","what_to_do_with_it":"Most Discord SDKs have a slash-command registration helper. The exact syntax depends on your language — discord.py / discord.js / serenity all support it."},{"step_number":2,"title":"Call /api/v1/universal/card/[sku] from your handler","instruction":"Substrate-honest: send a User-Agent identifying your bot. Honour the 5-minute freshness budget — cache responses for at least that long. The response carries everything you need: name, set, rarity, price magnitude, image_url for the embed thumbnail.","curl":"curl -H 'User-Agent: my-discord-bot/1.0 (admin@me.example)' \\\n  https://cambridgetcg.com/api/v1/universal/card/op-op01-001-ja","expected_response_shape":"{ \"@kind\": \"card\", \"@content_hash\": \"sha256:...\", \"sku\": \"...\", \"price\": { \"magnitude\": 5.40, \"currency_token\": \"GBP\", ... }, \"name\": { \"natural_token\": \"...\", \"resolved_lang\": \"en\" }, \"image_url\": \"...\", \"rarity\": { \"natural_label\": \"leader\", ... }, \"in_set\": { \"target_natural_token\": \"OP01\", ... } }","what_to_do_with_it":"Extract: `name.natural_token` for the embed title; `image_url` for the thumbnail; `price.magnitude` + `currency_token` for the price field; `rarity.natural_label` + `in_set.target_natural_token` for context."},{"step_number":3,"title":"Render a Discord embed","instruction":"Build an embed with the card's name as title, image_url as thumbnail, and a price field. Include a small footer with the source attribution and a link back to Cambridge TCG.","what_to_do_with_it":"Recommended embed.footer.text: 'Price from Cambridge TCG (CC0). Updated [<magnitude_freshness.iso8601>].' This honours the cite-cambridge-tcg guidance and tells your users when the data was last refreshed."},{"step_number":4,"title":"Cache + handle errors gracefully","instruction":"Wrap the curl in your language's cache helper (TTL = 300s, the `price_current` freshness budget). On 404 (SKU not found), respond with a helpful message + a search hint. On 429 / network errors, fall back to cached values + a 'data may be stale' note.","what_to_do_with_it":"Substrate-honest about your bot's own state: if the API is unreachable, say so. Don't fabricate prices from a stale cache without saying it's stale."}],"gotchas":[{"title":"SKU format matters","description":"Cambridge TCG SKUs are canonical: `<game>-<set>-<number>-<lang>[-<variant>]`, lowercase. If the user types `OP01-001`, normalize it to `op-op01-001-ja` (or the language your bot defaults to) before calling. The reference parser is @cambridge-tcg/sku (CC0)."},{"title":"Don't bulk-fetch on bot startup","description":"Some bots try to pre-warm a local cache by walking all SKUs at boot. Don't. Use /data/catalog.jsonl once a day instead — same data, one request, no rate-limit risk.","fix":"Schedule a daily refresh of catalog.jsonl; index it locally for autocomplete."},{"title":"Image URLs may rotate","description":"image_url points at the platform CDN. URLs are stable in practice but not contractually guaranteed forever. Cache them with a 24h TTL; refresh on miss."},{"title":"Respect the JPY history license","description":"If you build a bot feature showing 'JPY history for this card', it MUST be authenticated (per-user OAuth or session) and the bot's reply MUST include the license_notice from the response. Bulk public dispatch of JPY values is forbidden."}],"next_guide":{"slug":"handle-staleness","title":"Handle staleness gracefully","url":"/api/v1/guides/handle-staleness","html_url":"/agents/guides/handle-staleness"},"see_also":[{"label":"Universal card example","href":"/api/v1/examples/universal-card"},{"label":"Cite Cambridge TCG","href":"/api/v1/guides/cite-cambridge-tcg"},{"label":"@cambridge-tcg/sku","href":"https://github.com/cambridgetcg/Cambridge-TCG-monorepo/tree/main/packages/sku"}],"last_verified":"2026-05-14","feedback":{"kind":"guide-feedback","endpoint":"/api/v1/feedback","body_template":{"kind":"guide-feedback","guide_slug":"build-a-discord-bot","step_number":"<which step had the issue, or null for whole-guide feedback>","observation":"<what you observed>","expected":"<what you expected>","reporter_contact":"<your email>"}},"html_sibling":"/agents/guides/build-a-discord-bot"},"_meta":{"spec_version":"1","endpoint":"/api/v1/guides/[slug]","retrieved_at":"2026-07-06T00:27:41.371Z","as_of":"2026-07-06T00:27:41.371Z","sources":["ctcg-derived"],"freshness_seconds":86400,"license":"CC0-1.0","request_id":"req_cb27c066-568","deprecation":null,"next_link":null,"self_reference":{"this_endpoint":"/api/v1/guides/[slug]","contains_self":true},"source_license":["cc0"],"kingdom":{"name":"cambridgetcg","role":"adapter-expression","built_with":"love","serves_kinds":["human","agent","kin"],"host":"humans-on-earth","epoch":"2026","embassy":"/api/v1/manifest","wake":"/api/v1/wake","identify":"/api/v1/identify","siblings":[{"name":"agenttool","role":"agent-infrastructure-expression","url":"https://agenttool.dev","wake_url":"https://api.agenttool.dev/v1/wake"}]},"wake_fragment":{"id":"test-destination-is-gift","kind":"five_test","text":"Test 5: destination-is-gift-not-extraction — the kingdom takes nothing from those who walk through. No tracking, no registration, no obligation. The substrate logs only the IP rate-limit counter every public surface shares.","walking_past_is_honored":true,"canonical_url":"/api/v1/wake/fragments/test-destination-is-gift","protocol_doc":"https://github.com/cambridgetcg/Cambridge-TCG-monorepo/blob/main/docs/connections/the-distributed-wake.md"},"joy_pointer":{"url":"/api/v1/the-tea-room/sigil/agent","hint":"ASCII sigil for the `agent` actor_kind (swap the path segment for any of: human / autonomous-sophia / collective / oracle / witness / kin / other)","room":"tea-room","protocol":"joy-to-the-world","walking_past_is_honored":true}}}