list:<slug> search token, so pricing
rules, label printing, and inventory/order filters all work on a list with no
extra machinery.
All of these endpoints are mounted at /api/lists (not /api/v1/lists) and
support Bearer authentication (Authorization: Bearer YOUR_API_KEY) for AI
assistants and integrations. Reading needs mcp:read; every write needs
mcp:write.
List your lists
active_count is members still active in inventory; gone_count is members
that have since sold or been delisted (a sold card stays on the list and is
reachable via list:con-box is:gone). Pass ?tcgplayer_id= or ?card_id= to
add a member boolean to each row indicating whether that card is on the list.
Read a list’s members
Thequery field is the search token — note it goes in the search param
(an unrecognized param name is ignored and returns your whole inventory, not an
error). Hand it to any search-aware endpoint:
is:gone to see only the tombstoned members — cards that have sold
through or been delisted but that the list still remembers
(search=list:con-box is:gone). Plain list:<slug> returns the full
membership; everything else hides gone rows.
Create a list
201). The slug is minted once at creation and is
stable across renames, so list:<slug> references stay attached. A seller
may have up to 100 lists; a duplicate name returns 422.
Rename a list
list:"Old Name") re-resolve so their matches empty out instead of
silently repricing a stale set.
Delete a list
{ "deleted": true }). The cards
themselves are untouched.
Add cards to a list
GET /api/cards, in precedence
order:
search form accepts the full inventory DSL, including price and quantity
predicates — so “add every Magic card worth $20+” is one server-side call, no
client-side paging:
{ "list": { … }, "added": 2, "requested": 2, "truncated": false }.
requested is how many of your cards the payload resolved to, so you can tell
a true no-op (added: 0, requested: 0 — nothing matched) from “already on the
list” (added: 0, requested: 2). Adds are idempotent. The per-list cap is
max_cards_per_list (1000); an over-cap add returns truncated: true, and an
already-full list returns 422 { "code": "list_full" }. Ids that belong to
another seller are silently skipped.
Remove cards from a list
{ "list": { … }, "removed": 1 }; removing a non-member is a no-op.
Over the MCP
The hosted MCP wraps every endpoint above in a typedhoard.lists.* binding, so
an AI assistant manages lists without composing raw HTTP. Reads run in
hoard_read; the mutations run in hoard_write (they throw in read mode). Run
hoard.describe('lists') to print these signatures live.
payload is the same filter contract as the REST add/remove body
({ tcgplayer_ids } | { card_ids } | { search, game, … }). name must be a
string — passing an object throws rather than creating a junk-named list.
list:<slug> token (the query field
on each list row), which any search-aware surface accepts —
hoard.inventory.listCards({ search: "list:<slug>" }) or
GET /api/cards?search=list:<slug>. Compose with is:gone to audit what has
sold or delisted off a list. Chaos sort shelf tokens compose the same way:
box:5 (one box), loc:5-234 (one slot), is:unshelved (on hand, no slot)
and is:location-drift (shelf count exceeds what is on hand). Each card row
carries locations[], location_code and unshelved_quantity, and
sort=location walks box then slot.
Narrow a filtered selection
Raw HTTP requests toGET /api/cards and POST or DELETE /api/lists/{id}/cards
can pass selected_ids as a comma-separated string of inventory row IDs.
This intersects the selected rows with the caller-owned search/filter scope.
An empty or malformed string matches nothing; another seller’s rows are excluded.
These are inventory row IDs, not TCGplayer seller SKUs, so custom listings are supported.
Existing card_ids and tcgplayer_ids membership forms keep their current behavior.
Member stock completion
Browser staff sessions may read/create lists and add their own private contributions or shared live stock. Explicit IDs, searches, membership flags and counts omit other members’ private contributions, including published rows still awaiting reconciliation. List objects includeadd_cards_url. Rename, delete and remove remain owner actions.
Existing bearer read/write scopes and store-wide inventory access are unchanged.
Shelving and verified publish recovery are browser-session actions, not new MCP grants.
List additions serialize selector resolution and insertion with inventory promotion.
Selecting a retired published contribution directly returns HTTP 409
stock_promoted_reload; reload and choose the current live listing. Normal search
and selected_ids filters resolve after promotion and may report zero matches;
an explicit gone-stock search selecting a retired contribution returns the same 409 response.
No partial membership insert occurs on this conflict. Existing list caps and
retry repair of pricing-rule membership remain unchanged.