Move cards to a sub-account

POST /api/v1/cards/sub-account write:sub_account | admin

Re-files cards between your own inventory and one of your sub-accounts. This changes a label and nothing else: the cards never leave their bins, handling, insurance, billing and custody are untouched, and the move itself is not charged. Pass sub_account: null (or "main") to file cards back into your own inventory. The call is all-or-nothing — if any unique_id is unknown or belongs to another account the whole request is rejected and not one card moves. A unique_id is matched across both raw cards and graded slabs: you know a card by its CARD-RIP… id and should not have to know which table we keep it in. GET /cards can now list slabs too — pass ?type=graded or ?type=all — so a sub-account you file them into is visible there as well.

All-or-nothing on purpose: a partner is never told "moved 4" and left to work out which of five failed. The destination slug is resolved before a single card row is read, and the ownership check and the update share one transaction, so a rejected call leaves your inventory exactly as it was.

This endpoint needs its own write:sub_account scope; no existing scope is accepted as a substitute. write:transfer is deliberately NOT enough: that scope only appends a row to your transfer log and never mutates a card, whereas this one UPDATEs up to 500 rows, so lending it here would have quietly widened every transfer key you already hold. Ask your account manager to add write:sub_account when your sub-accounts are set up — the same moment, so it is not a separate errand.

Nothing physical happens and nothing is billed. Moving a card between sub-accounts does not move it in the warehouse, does not re-open a holding period, and does not appear as a line on your invoice — it is a metadata change to how your inventory is presented back to you.

The "Try it" body names "docs-demo", which is a RESERVED slug no account can ever own, so pressing Run cannot re-file a real card no matter what is in the sidebar: the destination is resolved before a single card row is read, and the call comes back as a 400 that lists the slugs your account does accept. Send a real destination from your own client, not from this page.

Try it POST /api/v1/cards/sub-account write

These inputs are shared across all docs pages, so an id entered here carries over.

request body
object · 2 keys
{
  "unique_ids": [
    "CARD-EXAMPLE-1"
  ],
  "sub_account": "docs-demo"
}
response

Not run yet. Press Run to make a live call against https://service.rip.fun (through this demo's server-side proxy; the API key never reaches the browser).

curl (tracks the inputs above)
curl -X POST 'https://service.rip.fun/api/v1/cards/sub-account' \
  -H 'X-API-Key: rip_…' \
  -H 'Content-Type: application/json' \
  -d '{"unique_ids":["CARD-EXAMPLE-1"],"sub_account":"docs-demo"}'

Request fields

FieldTypeRequiredDescription
unique_idsstring[]yesCards to re-file, at most 500 per call. A singular unique_id string is accepted instead, mirroring POST /withdrawals. Duplicates and blanks are collapsed before the cap is applied
sub_accountstring | nullyesDestination slug (matched case-insensitively), or null / "main" / "none" / "" for your own inventory — note the empty string counts, so a <select> whose default option has value="" un-files rather than erroring. The key must be PRESENT: omitting it entirely is a 400, never an implicit un-filing. "all" is rejected — meaningful as a filter, meaningless as a destination

Response fields (data)

FieldDescription
movedHow many distinct cards were actually re-filed, raw cards and graded slabs together. Normally this equals the distinct ids you sent, since an unknown id is rejected outright rather than partially applied; it is counted from the rows the write really changed, so a card moved out from under the call by a concurrent request is not counted as moved
sub_accountWhere they landed: { slug, name }, or null when the cards were filed back into your own inventory

Errors

StatusCodeWhen
400sub_account is requiredthe sub_account key is absent from the body, or is neither a string nor null
400unique_ids is requiredunique_ids (or unique_id) missing, or empty once blanks are dropped
400too many cardsmore than 500 distinct unique_ids in one call — split the batch
400unknown sub_accountthe destination slug does not exist on your account; the message lists the valid values
400sub-account is retiredthe destination exists but has been retired — it keeps the cards already filed under it, but cannot receive more
400invalid destination"all" was passed as the destination
403Tenant API key requiredkey is not tenant-scoped
403Insufficient permissionskey is tenant-scoped but carries neither write:sub_account nor admin; the body names the required scopes
404not foundany unique_id is not in your inventory — the message names up to ten of them. Nothing moved

See Errors for the response envelope and the full code list.