{"name":"One Piece TCG — card catalog + rules API","purpose":"Card catalog + official rules corpus for the One Piece Card Game, designed for LLM tool use","version":"1.0.0","docs":"/docs","endpoints":[{"method":"GET","path":"/health","description":"Liveness probe: {status:'ok'} plus a SELECT 1 database ping and a per-source data-freshness map from the continuous scraper (scrape_runs)."},{"method":"GET","path":"/v1/cards","description":"Granular card search: q, colors (ALL of), card_type, rarity, set_id, keyword, counter, power/cost ranges, attribute, trait, has_trigger, is_parallel, include_reprints; paginated compact rows."},{"method":"GET","path":"/v1/cards/by-name/{name}","description":"Look cards up by name (case-insensitive exact, else substring) — returns compact rows for every printing."},{"method":"GET","path":"/v1/cards/{card_id}","description":"Full card detail: all columns + colors/traits/attributes/keywords + variants + listings + image metadata + FAQ entries + errata + active restriction status."},{"method":"GET","path":"/v1/cards/{card_id}/image","description":"Full-resolution card art PNG (sha256-verified against card_images)."},{"method":"GET","path":"/v1/sets","description":"All 59 official EN series with kind, EN release date and live entry counts."},{"method":"GET","path":"/v1/sets/{set_id}","description":"One set plus its paginated compact cards (via card_listings provenance)."},{"method":"GET","path":"/v1/facets","description":"Discovery: distinct colors/card_types/rarities/attributes/keywords/traits with card counts — valid values for /v1/cards filters."},{"method":"GET","path":"/v1/rules/documents","description":"Official rules sources (Comprehensive Rules, manuals, per-product FAQs, errata, restrictions) with section/entry counts."},{"method":"GET","path":"/v1/rules/sections","description":"Search rules sections by document, exact section_num, or body text; paginated in official reading order."},{"method":"GET","path":"/v1/rules/sections/{id}","description":"One rules section plus prev_id/next_id for context walking."},{"method":"GET","path":"/v1/faq","description":"Search 1399 official FAQ Q&A entries by card_id, card_number, text, or document."},{"method":"GET","path":"/v1/errata","description":"Official errata (announced date + before/after text), filter by card_id or card_number."},{"method":"GET","path":"/v1/restrictions","description":"Banned/restricted list: active entries by default (?active=false adds history); banned_pair rows share a pair_group."},{"method":"GET","path":"/v1/legality/standard","description":"Standard Regulation (Block Number rotation) legality: the current sliding block window, the official exemption lists (SPR always-legal, designated Block N, reprinted updated blocks), and per-card verdicts — a separate system from the ban list."},{"method":"GET","path":"/v1/meta/tournaments","description":"Metagame: search competitive tournaments (official + unofficial, 2023→today) by name/format/set_group/event_type/source/date range/min players/leader placed; paginated, newest first."},{"method":"GET","path":"/v1/meta/tournaments/{tournament_id}","description":"One tournament plus every reported result (placement, player, leader, colors, parsed-list flag), ordered by placement; aliases list the same event across corpus sources."},{"method":"GET","path":"/v1/meta/decklists","description":"Decklist search: every reported result in the metagame corpus, filterable by LEADER (a catalog id such as 'OP01-001' — any parallel/reprint variant folds onto the base card — or a fuzzy name like 'luffy' / 'red luffy'), tournament name, source, colors, event_type, deck_format, placement_max and date range; paginated, newest tournament first. leader_matches[] echoes which Leaders the term resolved to; an unresolvable term returns total 0, never the unfiltered corpus. Powers the site's Decklists tab."},{"method":"GET","path":"/v1/meta/decklists/{decklist_id}","description":"Full decklist: result + tournament context + every card row with quantities, joined to the catalog when the number is cataloged."},{"method":"GET","path":"/v1/meta/sources","description":"Metagame corpus source registry (egman/dotgg/limitless enabled, gumgum disabled per ToS) with live tournament/decklist counts per source."},{"method":"GET","path":"/v1/meta/leaders","description":"Metagame leader leaderboard: appearances, wins, top-8s, best/avg placement per leader over filtered tournaments — 'what is the meta?' in one call; all sources combined unless source= is given."},{"method":"GET","path":"/v1/meta/popular-decks","description":"Popular tournament decklists over a rolling window (?days=1..365, default 90; ?limit=1..50, default 12): leaders ranked by reported results with top-4s/best placement/latest date, each with ONE representative parsed decklist (best-placed, most recent on ties; null when no parsed list exists). Powers the site's Decks tab."},{"method":"GET","path":"/v1/news","description":"Forward-looking news / leaks corpus: set releases with street dates + tournament-legality lags, spoiler cards, announced bans, format changes, promos and rules docs, from the Bandai EN/JP feeds and curated trackers. Filter by confidence (official|official_jp|community_curated|leak_unconfirmed — a REQUIRED field on every row, never a boolean is_official), type, locale, set_code, card_id, q, upcoming, exclude_leak_unconfirmed, before/after, sort; paginated."},{"method":"GET","path":"/v1/news/facets","description":"News corpus shape for filter chips: counts per confidence class (all four always present, zero-filled), per type (all seven), per locale, a confidence_x_type cross-tab, upcoming + upcoming_sets counts, the leak_unconfirmed count (never dropped), and freshness stamps."},{"method":"GET","path":"/v1/news/meta-impact","description":"The upcoming-meta picture in one call for the site's News tab: upcoming sets (one row per set_code, highest-confidence row winning, legal_from = street date + lag), spoiler cards, and announced bans joined against the CURRENT restricted_cards list — each section pre-split into announced_official vs reported_unconfirmed because the UI must render those in SEPARATE tables. Carries honesty notes incl. why a leak never drives a legality claim."},{"method":"GET","path":"/v1/news/upcoming-sets","description":"AGENT TOOL get_upcoming_sets (docs/news.md S4): upcoming set releases, ONE ROW PER set_code with the highest-confidence announcement winning. MAY return all four confidence classes, but every row carries its own confidence + the verbatim confidence_label + source_url, plus has_official_release (whether a Bandai official row states a street date for that set_code) — so a community/leak date is never the release date of record. Params: set_code, include_past_releases, limit, offset."},{"method":"GET","path":"/v1/news/spoiler-cards","description":"AGENT TOOL get_spoiler_cards (docs/news.md S4): spoiler-card reveals. Default include_unconfirmed=false returns ONLY the official / official_jp classes; opting in adds community_curated and leak_unconfirmed rows, each still carrying its confidence and the verbatim confidence_label ('Unconfirmed leak') to quote unchanged. confidence_classes_included echoes the whitelist actually applied. Params: set_code, card_id, include_unconfirmed, limit, offset."},{"method":"GET","path":"/v1/news/announced-bans","description":"AGENT TOOL get_announced_bans (docs/news.md S4): announced ban/restriction changes, OFFICIAL Bandai EN rows ONLY and ONLY those stating an effective_at (announced != effective — live pages showed announced 2026-09-24 vs effective 2026-10-12). Both are SQL literals no query param can widen, so a tracker row or a rumour can never change legality here; an empty result means 'no official announcement', never 'no ban was rumoured'. Params: card_id, upcoming_only, limit, offset."},{"method":"GET","path":"/v1/news/{news_id}","description":"One announcement in full: body text, verbatim feed_item, raw-dump provenance (S3 key/sha256/bytes), change-detection stamps, and BOTH directions of the supersede chain (the row it confirms and the rows confirming it) — confirmation is a new row, never an in-place edit of an official one."},{"method":"POST","path":"/v1/chat","description":"Ask the deckpose assistant: a DeepSeek-backed agent loop over the 17 read-only optcg_agent tools (card/rules/ban-list/meta grounding). Body {message, history?, session_id?}; token spend is capped per request and per rolling 24h (chat_usage). Status 'not_configured' until the owner adds DEEPSEEK_API_KEY."},{"method":"POST","path":"/v1/combat-logs","description":"Upload one OPTCGSim combat log (.txt text): parsed, card-ids resolved against the catalog (unknowns quarantined), stored private-by-default; exact re-uploads dedupe by sha256. Body {log_text, played_at?} -> {game_id, summary}."},{"method":"GET","path":"/v1/combat-logs/{game_id}","description":"One stored combat game by its unguessable public uuid: metadata, both players, the full parsed reconstruction (turns/events/stats), quarantined card ids. raw_log is never served."},{"method":"GET","path":"/v1/combat-logs/{game_id}/analysis","description":"Deterministic compact digest of one stored game: leaders resolved (name/colors), per-turn table (DON drawn, deploys, attacks, key events), notable moments, stats — the shape the chat gameplay tool grounds on."},{"method":"POST","path":"/v1/auth/register","description":"Create an account {email, password (min 8), display_name?} -> 201 {user, token}. 409 duplicate email. Bearer token (30-day) goes in the Authorization header for /v1/me/* and /v1/auth/me|logout."},{"method":"POST","path":"/v1/auth/login","description":"Log in {email, password} -> {user, token}. 401 with a generic message on bad credentials (no account enumeration)."},{"method":"POST","path":"/v1/auth/logout","description":"Revoke the bearer token used for the call (Bearer auth required)."},{"method":"GET","path":"/v1/auth/me","description":"The authenticated user's profile (Bearer auth required)."},{"method":"POST","path":"/v1/me/sessions","description":"Heartbeat a chat session the caller owns {session_id, title?}: upserts chat_sessions, bumps last_active_at + turns. Called by the site after each completed chat turn so logged-in users' history syncs across devices."},{"method":"POST","path":"/v1/me/sessions/claim","description":"Adopt ownerless chat sessions {session_ids: [...]} (<=100) — browser-local sessions are linked to the account on first login. Rows already owned by someone else are never touched."},{"method":"GET","path":"/v1/me/sessions","description":"The authenticated user's chat sessions, most recently active first (limit 100): session_id, title, created_at, last_active_at, turns. Transcript content is read via GET /v1/chat/transcripts/{session_id}."},{"method":"GET","path":"/v1/me/decks","description":"The authenticated user's saved decks (Bearer auth required), most recently updated first (limit 100): id, name, leader_card_id, card_count, legal flag from the stored validation, timestamps."},{"method":"POST","path":"/v1/me/decks","description":"Save a pasted decklist {name, deck_text} (Bearer auth required). The list runs through the official construction validator (the same validate_deck as POST /v1/decks/validate) and the verdict is stored — illegal lists save with legal=false so they can be fixed later. 201 with the stored row; 409 duplicate name; 422 at the 50-deck account cap."},{"method":"GET","path":"/v1/me/decks/{deck_id}","description":"One saved deck (owner only): full decklist text + stored validation verdict. 404 for unknown AND other users' ids (no existence leak)."},{"method":"PUT","path":"/v1/me/decks/{deck_id}","description":"Rename a saved deck and/or replace its list {name?, deck_text?} (owner only); a new deck_text is re-validated. 409 on a name clash; 404 unknown/foreign id."},{"method":"DELETE","path":"/v1/me/decks/{deck_id}","description":"Delete a saved deck (owner only) -> {ok: true}; 404 unknown/foreign id."}]}