{"name":"Danni Arbok Collection API","description":"REST API for Danni's Arbok Pokémon card collection. Read endpoints are public. Write endpoints require a Bearer token.","authentication":{"type":"bearer","header":"Authorization: Bearer <API_KEY>","description":"Write endpoints require a valid API key. Read endpoints are public and require no authentication."},"endpoints":[{"method":"GET","path":"/api/collection","authentication":false,"description":"Returns the full collection with summary counts and all cards.","query_parameters":[{"name":"owned","type":"boolean","description":"Filter to only owned cards (owned=true)"},{"name":"missing","type":"boolean","description":"Filter to only missing cards (missing=true)"},{"name":"priority","type":"boolean","description":"Filter to only priority cards (priority=true)"},{"name":"set","type":"string","description":"Filter by set name (e.g. set=Base+Set)"}],"response":{"owner":"string","subject":"string","total_cards":"number","owned_count":"number","missing_count":"number","cards":["array of card objects — each includes id (string) plus name, set, number, owned, copies, etc."],"_links":{"self":"string (public URL for this endpoint; built from Host / X-Forwarded-Host or PUBLIC_BASE_URL)","docs":"string (URL to this documentation)"}}},{"method":"POST","path":"/api/collection/cards","authentication":true,"description":"Add a new card to the collection.","request_body":{"required":{"cardName":"string","setName":"string","number":"string (e.g. '14/102')","printing":"string (e.g. 'Unlimited', '1st Edition')","category":"string (e.g. 'Holo', 'Non-Holo', 'Reverse Holo')","language":"string (e.g. 'English', 'Japanese')"}},"response":{"success":true,"card":"card object with generated id"}},{"method":"PATCH","path":"/api/collection/cards/:id","authentication":true,"description":"Update collection status for a card. All fields are optional — include only what you want to change. Uses explicit values (not toggles), so it is idempotent. Returns 404 if the card id does not exist.","request_body":{"optional":{"owned":"boolean — set whether the card is owned","priority":"boolean — set whether the card is a priority/wanted card","note":"string — set a note (empty string to clear)"}},"response":{"success":true,"cardId":"string","owned":"boolean (if set)","priority":"boolean (if set)","note":"string (if set)"},"errors":{"404":{"success":false,"error":"Card not found","cardId":"string"}}},{"method":"PUT","path":"/api/collection/cards/:id","authentication":true,"description":"Update card metadata fields. Returns 404 if the card id does not exist.","request_body":{"allowed_fields":["cardName","setName","number","printing","category","language"]},"response":{"success":true,"card":"updated card object"},"errors":{"404":{"success":false,"error":"Card not found","cardId":"string"}}},{"method":"DELETE","path":"/api/collection/cards/:id","authentication":true,"description":"Permanently delete a card and its collection data (owned status, notes, copies). Returns 404 if the card id does not exist.","response":{"success":true,"message":"string"},"errors":{"404":{"success":false,"error":"Card not found","cardId":"string"}}},{"method":"PUT","path":"/api/collection/cards/:id/copies","authentication":true,"description":"Replace the copies array for a card. Used to track individual physical copies with condition, PSA grading, and optional purchase price (EUR or DKK). Each copy is either condition-graded OR PSA-graded — the two are mutually exclusive. Sending both (condition with isPSAGraded=true, or psaGrade with isPSAGraded=false) returns 400. There is no default condition value. Returns 404 if the card id does not exist. The id field is auto-generated if omitted.","request_body":{"required":{"copies":"array of copy objects (see below)"},"copy_object":{"id":"string (optional — auto-generated UUID if omitted)","condition":"one of: mint, near-mint, excellent, good, light-played, played, poor — required when isPSAGraded is false; must be omitted or null when isPSAGraded is true (stored as null for PSA-graded copies)","isPSAGraded":"boolean (default false)","psaGrade":"number 1-10 — required when isPSAGraded is true; must be omitted or null when isPSAGraded is false","paidPrice":"number >= 0 (optional — amount paid for this copy; omit or null if unknown)","paidCurrency":"EUR or DKK — required when paidPrice is set (no silent default; omitting currency with a price returns 400)"},"validation_errors":{"status":400,"mutual_exclusion":"Sending condition with isPSAGraded=true, or psaGrade with isPSAGraded=false, returns 400 with a field-level error","missing_condition":"Omitting condition when isPSAGraded is false returns 400 (no silent near-mint default)","missing_psa_grade":"Omitting psaGrade when isPSAGraded is true returns 400"}},"response":{"success":true,"cardId":"string","copies":"array (with generated ids; PSA-graded copies have condition: null)"},"errors":{"404":{"success":false,"error":"Card not found","cardId":"string"}}}],"examples":{"read_collection":"curl https://YOUR_DOMAIN/api/collection","read_missing_cards":"curl 'https://YOUR_DOMAIN/api/collection?missing=true'","mark_as_owned":"curl -X PATCH https://YOUR_DOMAIN/api/collection/cards/42 -H \"Authorization: Bearer YOUR_API_KEY\" -H \"Content-Type: application/json\" -d '{\"owned\": true}'","add_card":"curl -X POST https://YOUR_DOMAIN/api/collection/cards -H \"Authorization: Bearer YOUR_API_KEY\" -H \"Content-Type: application/json\" -d '{\"cardName\":\"Arbok\",\"setName\":\"Fossil\",\"number\":\"31/62\",\"printing\":\"Unlimited\",\"category\":\"Set\",\"language\":\"English\"}'","add_copy":"curl -X PUT https://YOUR_DOMAIN/api/collection/cards/42/copies -H \"Authorization: Bearer YOUR_API_KEY\" -H \"Content-Type: application/json\" -d '{\"copies\": [{\"condition\": \"near-mint\", \"paidPrice\": 250, \"paidCurrency\": \"DKK\"}, {\"isPSAGraded\": true, \"psaGrade\": 9, \"paidPrice\": 45, \"paidCurrency\": \"EUR\"}]}'","delete_card":"curl -X DELETE https://YOUR_DOMAIN/api/collection/cards/42 -H \"Authorization: Bearer YOUR_API_KEY\""}}