MTG Copilot Docs
Your data

Decks

What a deck row holds, how the card list and its history are stored, who can read a deck, and what deck import and export accept.

A deck is one row in decks plus one row per card in deck_cards. Five more tables hang off those two: folders for grouping, deck_shares for access, deck_card_events for history, and deck_analyses with deck_analysis_feedback for the Analytics tab.

Every one of them is scoped by row-level security. The policy on decks is auth.uid() = user_id, and the other six apply the same check, either on their own user_id or through the deck the row belongs to. A new deck is private (is_private defaults to true).

The deck row

decks carries the name and commander, and then everything you fill in on the Design tab.

ColumnWhat it holds
nameThe deck name.
user_idYou. The column every policy on this table checks.
folder_idThe folder it sits in, or null for the top level.
commander_card_idThe commander.
descriptionSet when a deck is cloned or built from a precon, and blank otherwise.
colors, accent_colorDeck colors, and the single color used for the deck's accent.
is_privatetrue by default.
power_level1 to 10, enforced by a check constraint.
play_style, themesFree text and a list of theme strings.
budget_usd, win_conditionsYour stated budget and win conditions.
meta_notesWhat you expect to play against.
must_include_card_idsCards the AI is told to keep.
design_locksWhich design fields are pinned against AI edits.
turn_by_turn_planThe game plan, keyed by turn.
overview, overview_docThe deck overview. The doc is the editor's JSON, the text its markdown form.
deck_notesUp to four named notes, each with the same doc and markdown pair.
available_tagsThe tag vocabulary you can apply to cards in this deck.
settingsPer-deck UI settings, merged on write.
created_at, updated_atTimestamps.

Two more columns hold vectors. overview_embedding and game_plan_embedding are computed from the overview and the turn plan when a recommendation run needs them, and embedded_at records when that happened, so the next run can reuse them.

deck_idea, wincon and theme are older columns that nothing in the app writes today.

The card list

deck_cards holds one row per card per board.

ColumnWhat it holds
card_idThe card. Printings are a separate concept, below.
boardmain, sideboard, maybeboard or commander.
quantityCopies on that board. Defaults to 1.
printing_idThe printing you pinned, if you pinned one.
is_coreMarks a card as core to the deck.
tagsYour tags for this card, drawn from the deck's available_tags.
notesYour note on this card in this deck.
added_atWhen the row was created.

The commander lives in decks.commander_card_id. commander is still a legal value for board because older decks stored it that way, and cloning a deck drops those rows rather than copying them.

Pinning a printing changes nothing about the card itself. It decides which set code and collector number a text or CSV export writes out.

Folders

folders is a name, a user_id, and a parent_folder_id, so folders nest. Deleting one keeps its contents: decks inside it have folder_id cleared, child folders have parent_folder_id cleared, and both end up at the top level.

History

Every change to deck_cards writes a row to deck_card_events. The write happens in a Postgres trigger on the table itself, so manual edits, an AI suggestion you accept, an import and a clone all record the same way.

ColumnWhat it holds
event_typeadded, removed or moved.
boardThe board landed on, or left for a removal.
from_boardThe board left, on a move.
quantityCopies affected, always positive.
card_nameThe card's name as of the event.
tags, notesThe card's tags and note at the time.
sourcemanual or ai-recommendation.
created_atWhen it happened.

A quantity change records as an added or removed event for the difference, so raising a count from 1 to 3 records an added event for 2. Changing only a card's tags or note records nothing.

The card name is copied onto the event so an entry still reads correctly later. The history panel shows the 50 most recent events for a deck.

Analyses

deck_analyses stores one row per analysis runner per run. Nine runners exist: staple identification, top performers, recommended cuts, cohesion, power, explosivity, design alignment, ramp, and an overall summary. runner_id names the one that produced the row and result holds its parsed output.

deck_hash is the first 16 characters of a SHA-256 over the deck's card ids and quantities, sorted. Comparing it against the deck as it stands now is how a stored analysis is known to be out of date. The Analytics tab reads the 40 most recent rows for a deck.

deck_analysis_feedback records agreeing or disagreeing with one item inside an analysis. item_type and item_value identify the item, feedback_type is agree or disagree, note is your optional comment, and analysis_id points back at the run.

Who can read a deck

ReaderGets
YouFull access to your decks and everything hanging off them.
AnyoneA deck, its cards, and its analyses once you set is_private to false.
A named collaboratorRead access through deck_shares.

deck_shares pairs a deck with a user at a permission of view_only, edit or owner. The policies on decks and deck_cards respect it, so a share row would grant read access. No screen in the app creates one today, which leaves public and private as the only two states you can actually reach.

Making a deck public exposes its analyses along with its cards. Deck history, notes and design fields stay owner-only.

Import and export

Deck import takes pasted text and works out what it is from the first non-empty line. A line containing a comma and one of count, quantity, name, edition, set code or foil is read as a CSV using the same header-name rules as collection import, and every row lands on the mainboard. Anything else is read as a decklist, where section headers put cards on the sideboard or maybeboard.

Cards resolve by exact name, case-insensitive, preferring a non-token card when a name matches both. Import previews the result first, so you see how many lines matched and a sample of the ones that did not before anything is written. A line that matches nothing is skipped and named in the result.

Export gives you either a text decklist, grouped by section and optionally carrying the set code and collector number of each pinned printing, or a CSV in the collection format.

What the AI is given

Answering a question about a deck means reading it, so the deck's cards and design fields are loaded server-side as part of your own request. See how the AI sees your deck for what is loaded when.

On this page