# API conventions

Source: https://holofolder.com/app/docs/contracts

Permissions, pagination, errors and image availability.

## Card, copy and species identity

`cardId` identifies a catalog printing, `inventoryItemId` a physical copy, `slotId` a stable storage slot and `collectionId` a storage collection. Repeated copies keep distinct identities. Existing versioned commands retain their `itemId` fields for physical copies; do not rewrite queued commands or backups to match Activity's `inventoryItemId` query filter.

Catalog card, set and series responses declare `game: "pokemon"`. Only Pokémon TCG is supported today, including Trainers and Energy without National Dex links. No game selector is offered. Internal IDs, canonical IDs and reviewed aliases are globally unique opaque application IDs across games and sources. Keep existing IDs unchanged and pass them back exactly; do not parse them or use a bare provider-local product number as `cardId`.

`externalIds` contains provider-local evidence keyed by source within the record's game. For example, synthetic `tcgplayer:["42"]` and `cardmarket:["42"]` on a `game:"pokemon"` record are different identifiers. Neither is application ID `42`. Future ingestion must allocate collision-free global card/set/series IDs and keep external IDs scoped by game and source before joining or inserting; adding a game field to copies cannot fix a catalog primary-key collision. No other game or provider integration is implied.

`{kind:"pokemon",pokemonId}` deliberately identifies a species. `list_pokemon` and `get_pokemon` remain dedicated species endpoints. `get_pokemon_set_slots` (`GET /pokemon/sets/{id}/slots`) returns dex-grouped Pokémon species, not stable storage slots. Use `list_cards` with a set filter for all set printings, and `list_storage_slots` for actual stable slots.

The public `list_collection`, `save_collection_item` and `delete_collection_item` species-row operations have been removed without aliases. Use [native reads](https://holofolder.com/app/docs/markdown/native-reads) and [native commands](https://holofolder.com/app/docs/markdown/native-storage) for copies, slots and wants. Every binder is native, so `get_legacy_pokemon_analytics` and `resolve_import` have been removed too.

## Pagination

List and search operations use bounded pages with a maximum of 100 records. Follow the returned cursor for the next page. A page is not the entire dataset, and a cursor belongs to the query that created it.

## Limits

The initial budget is 60 reads and 20 writes per minute per account, shared across its credentials. Provider refreshes and long-running comparisons have their own stricter limits. A `429` response includes `Retry-After`.

## Errors

| Status | Meaning                                              |
| ------ | ---------------------------------------------------- |
| 401    | Invalid, expired or revoked credential               |
| 402    | Premium or lifetime access required                  |
| 403    | Required permission missing                          |
| 404    | Record unavailable to this account                   |
| 409    | Conflict or confirmation required                    |
| 413    | Request body too large                               |
| 422    | This operation is unsupported for the requested data |
| 429    | Rate limit reached                                   |
| 503    | A required service is unavailable                    |

Errors include a stable code, a message and a request ID. Private responses are not cacheable.

Starting a sold-comps comparison job, or refreshing price checks from a source other than eBay, is app-only and returns `403` with code `ui_only`. Saved comparisons and price checks stay readable through the API.

## Images and prices

Image fields refer only to verified R2 objects. Missing images remain missing; the API does not fall back to another image host. Public CDN image links can still be accessed by someone who already has the URL.

Prices include available source, currency, confidence and freshness evidence. Missing amounts and uncertain prices are not invented. Price signals require medium or high confidence.

Tracked background refreshes cover owned physical copies, including Unassigned ones, and explicit wanted cards, including Trainers and Energy. Empty targets are excluded. Repeated copies share a card/language request; a missing language stays unknown and no finish is guessed. An incomplete read stops selection before any provider request. Manual and scheduled refresh rules are unchanged, and the selection adds no REST/MCP permission.

Private uploaded binder artwork lives in a separate private R2 bucket. Upload preparation returns a five-minute signed PUT URL, required headers and an `artworkRef` for the appearance design. Upload the exact declared bytes, then save the appearance revision. Downloads use five-minute signed GET URLs.

## Safe writes

Collection and storage commands must protect against overwriting a newer edit. Duplicate retries of creation, purchase and job operations must not create duplicate effects. See the API reference for each request schema. Binder creation requires an `Idempotency-Key`, sent as a header or in the body; when both are present they must match. Settings replacement requires a `baseFingerprint` from the previous read. Native storage commands use revisions and a retry-stable `operationId` instead (see [native storage](https://holofolder.com/app/docs/markdown/native-storage)). Retry keys are retained for 24 hours. Cursors do not freeze the dataset while other clients make changes.

## Native storage

[Native storage contracts](https://holofolder.com/app/docs/markdown/native-storage) use authenticated execution with exact revision and operation-ID semantics and their own bounds. They do not use the 24-hour `Idempotency-Key` retention. Native developer reads expose a separate redacted projection. Undo over REST and MCP uses the native storage recovery operations.

## Native reads

[Native developer reads](https://holofolder.com/app/docs/markdown/native-reads) declare a private redacted copy/storage/slot projection with signed scope/filter/revision-bound cursors. A response page is never a complete planning snapshot or a fresh-read queue acknowledgement.

[Copied preset previews](https://holofolder.com/app/docs/markdown/preset-previews) provide authenticated discovery and bounded read-only manifests through the same registry/dispatcher. Preset entry IDs are provenance, not slot/collection/operation identities. No private planning state, ownership, autosync or durable set/master-set scope is implied.