Native storage contracts
Authenticated native storage commands, replay and recovery through REST and MCP.
Supported native developer operations use the trusted storage executor. REST uses a personal API key; MCP uses OAuth. Current actor, credential, membership, billing and operation permissions remain required. Public share links grant no private inventory access.
Five native developer reads return redacted pages and current edit preconditions. Their completeSnapshot:false results are never private planning snapshots or browser queue acknowledgements. Copied preset previews require catalog:read and binders:read, allocate no creation identities, and do not create ownership.
The app uses the same executor through its first-party routes. Unsupported operations return errors; they are never approximated or filled with guessed card data.
Commands
Use the same arguments with the REST path below or the same-named MCP tool. Paths are relative to /app/api/v1. All commands require collection:write and binders:write; recovery also requires collection:read.
| MCP tool | REST POST path | Effect |
|---|---|---|
execute_slot_commands | /storage/slots/commands | Insert, update, clear, remove or reorder stable slots |
execute_inventory_commands | /storage/inventory/commands | Add, edit, remove, assign, move or unassign physical copies; delete a collection |
create_storage_collection | /storage/collections | Atomically create native storage and its copied targets |
import_storage_arrangement | /storage/imports/arrangement | Import explicit copies and arrangement into a compatible native destination |
restore_raw_collection_content | /storage/restores/raw-content | Reconcile supported same-owner raw content |
preview_storage_recovery / apply_storage_recovery | /storage/recovery/preview / /storage/recovery/apply | Review and apply version 1 native recovery |
preview_storage_recovery_v2 / apply_storage_recovery_v2 | /storage/recovery/v2/preview / /storage/recovery/v2/apply | Explicit default-creation replacement recovery |
Use current inventory, collection, layout, slot, item and placement revisions. Preserve the original operationId, identities, expected owner and request across retries. A timeout or lost response is an unknown outcome. Never refresh preconditions or allocate a replacement ID to retry an unknown write.
Only explicit removeOwned deletes ownership. Clearing or removing a slot, removing a placement, and deleting a collection release its copies to Unassigned. To delete a collection together with its cards, send deleteCollection with removeOwned: true (migration 111): raw copies in it are deleted, graded copies still go to Unassigned because their label assets can't be restored. Deleting the default requires an explicit valid replacement where the command requires one. Empty targets, spacers and wants never manufacture owned copies.
Preserve private fields
Developer reads omit certificates, private label assets and immutable source evidence. Use privateFields:"preserve" and expectedOwnerUserId for supported mutations of that redacted state. The expected owner is checked against the authenticated current scope before execution. The SQL executor preserves hidden data after the original-intent replay lookup; the client never reconstructs private JSON from a redacted read.
Supported operations include slot insertion, reorder and target-only updates; explicit inventory add, removeOwned, unassign and placement; patchGrading on declared public grading fields; setFinish, which corrects the recorded finish of a card copy in place (same copy, grading and placement, and still matching any finish its pocket pins); and selected-want consumption using exact expectedPublic evidence. Private certificates/assets/provenance and unrelated wants remain intact. Selected wants require the exact locked slot/index public projection.
Whole grading/wants replacement and destructive slot clearing are unsupported in preserve mode. Omitting the field or selecting replace keeps full-replacement semantics. Use that only with deliberately complete values, never by filling hidden fields with nulls.
Atomic slot insertion and copy assignment
insertAssign inserts a stable slot before an exact existing slot reference, or appends with before:null. Preserve target:null for a free slot or the explicit target. selection:null creates no ownership; existing needs an exact Unassigned copy/version; new explicitly acquires the given stable copy. It never chooses a grade or moves an assigned copy implicitly.
The executor bounds insertion to 4,096 slots and 8,192 insertion work rows, 100 commands and 256 affected selected entities. Ordinary commands retain 256 KiB request and 1 MiB receipt bounds, a 10-second deadline and private audit bounds. Oversized evidence rejects atomically. See the generated endpoint reference for each exact schema and limit.
Creation and arrangement import
Creation saves the named collection, copied manifest and ordered stable slots in one transaction. Retain operation, collection and slot IDs. An owner's first collection creates their inventory root from revision 0 and becomes the default. If a first creation fails, retain the exact original request for retry or reconciliation; an unavailable response is not a terminal discard proof. Existing count/grid entitlements still apply. Empty goals create no ownership or wants. Creation receipts retain conservative recoverySupported:false; recovery preview determines supported evidence independently.
Reviewed set/variant checklists additionally persist manifest.setScope: exact language, verified finish families, copied catalog evidence, promos, exclusions, count, capture time and warnings. Creation validates current mirror eligibility, then preserves the historical snapshot. list_storage_collections returns it in presetManifest. Later targets and free slots stay editable; membership is never permanently enforced. See Set checklists.
Arrangement import requires an empty or target-only native destination with an already identical copied manifest. Every physical copy needs an explicit add or versioned existing-copy move. It does not replace collections/configuration, import assets, authorize backup-file IDs or restore arbitrary historical snapshots.
Terminal rejection and exact replay
A committed terminal ledger rejection returns REST 409 or MCP isError:true with matching structured/text JSON. The error code is storage_command_rejected, storage_creation_rejected, storage_import_rejected, storage_recovery_rejected or storage_restore_rejected for the corresponding family. Strict error.rejection contains only outcome:"rejected", the original operationId, a known code, inventoryRevision, and the version where that contract requires it.
MCP outputSchema describes accepted output only; holofolder.com/errorSchemas declares errors separately. Arbitrary 409s, SQL conflicts, timeouts, cancellation and failed COMMIT are not terminal rejection proof. They do not authorize dropping retained intent.
Current authorization precedes lifetime exact accepted/rejected replay. A replay receipt is historical. Browser consumers must obtain a fresh same-session complete current read started after the reply and explicitly reconcile before acknowledging, discarding or reviewing a replacement operation. Developer projections cannot supply that browser read authority.
Recovery
Preview returns public-safe Activity changes and conflicts, never private inverse state. Unsupported dependencies return no usable plan. Apply requires the exact reviewed planToken, explicit confirmation, version and retry-stable operation ID. Newer or ABA changes conflict. Version 2 default-creation recovery requires an explicit same-owner replacement and current inventory/content/layout revisions. There is no implicit replacement or fallback. Private label asset evidence can remain unsupported with private_assets_unverifiable.
Bounded same-owner raw restore
Raw restore reconciles one existing raw collection with the same stable slot IDs and unchanged configuration/grid. Version 4 is the only command version. It carries the exact copied manifest, canonical per-slot origins and slotOrder.before/after, either equal or a permutation of the same IDs; only a reordering advances the layout revision. Content and origins bind by stable ID, slot versions advance, and the receipt carries both exact orders. A command has at most 1,025 slots/manifest entries, 2,050 distinct copy references, 100 wants per slot, 2S+I+W ≤ 8,192 and 1.75 MiB command/JSONB text. Its complete prospective audit and undo graph is capped at 2.5 MiB.
Explicit current choices and trusted actor/owner/session or credential/membership evidence bind the plan. File IDs never prove provenance. Every displaced copy needs explicit release consent and remains Unassigned. Copies placed elsewhere cannot move automatically. Acquisitions, graded copies, changed manifests/origins, metadata/settings/assets/CSV and multiple-collection restores are unsupported. SQL validates the complete actual audit graph and its undo before commit.
Accepted receipts and dedicated rejections carry version:4. Other command versions return invalid_input and preserve device records. Files with unverified wanted-card image URLs remain blocked by the existing export/review policy; no URL is silently removed. First-party planning accepts a backup file up to 10 MiB. No file is stored as a server backup and no provider refresh is triggered.
First-party app routes
The cookie-authenticated app uses GET /app/api/inventory, POST /app/api/binder-slots, POST /app/api/inventory, POST /app/api/storage-collections, POST /app/api/native-recovery and POST /app/api/raw-content-restore. These are private, no-store boundaries. Write requests bind the current owner and auth session and use the app origin. Developer credentials use their separate REST/MCP boundaries and never impersonate an app cookie session.