{"version":"1","description":"Finish My PC — collection item schema reference","endpoints":{"GET /api/v1/items":"List items (auth required; query params: status, limit 1–500, offset)","GET /api/v1/items/:id":"Fetch one item (auth required)","POST /api/v1/items":"Create a single collection item (auth required)","PATCH /api/v1/items/:id":"Partial update (auth required)","DELETE /api/v1/items/:id":"Delete an item (auth required)","POST /api/v1/items/bulk":"Bulk insert up to 100 items atomically (auth required)","POST /api/upload":"Upload an image as the raw request body (jpeg/png/webp/gif, max 10 MB) → { key } (auth required)","GET /api/v1/extras":"Public feed of all items listed as extras, newest first (no auth; query: username, limit 1–100, offset)","GET /api/v1/users/:username/extras":"One collector's extras (no auth; query: limit, offset)","POST /api/v1/items/:id/offers":"Make a cash and/or trade offer on another collector's extra (auth required) → 201 Offer","GET /api/v1/items/:id/offers":"All offers on one of your items (auth required, owner only)","GET /api/v1/offers":"Offers you've received (default) or sent (?direction=sent); filter ?status= (auth required)","GET /api/v1/offers/:id":"One offer you're party to (auth required)","PATCH /api/v1/offers/:id":"Respond: owner → accepted|declined, offerer → withdrawn (auth required)","GET /api/v1/schema":"This document (no auth required)","GET /api/v1/payments":"Payment/billing status"},"authentication":{"type":"Bearer","header":"Authorization","format":"Authorization: Bearer fmpc_...","note":"Create an API key at /dashboard/settings (requires an account). The plaintext key is shown once on creation. Browser sessions are also accepted."},"collectionItem":{"description":"Represents one item in a user's collection. itemType determines which field group applies: sports_card → playerName/cardYear/setName/cardNumber/variant/condition; custom → itemName/categoryLabel/tags. status: 'owned' = in collection, 'hunting' = on want list, 'extras' = duplicate available for trade/sale. imageKey is the key returned by POST /api/upload; images render at /images/<key>.","jsonSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"itemType":{"type":"string","enum":["sports_card","custom"]},"status":{"default":"owned","type":"string","enum":["owned","hunting","extras"]},"notes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"imageKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"playerName":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"cardYear":{"anyOf":[{"type":"integer","minimum":1800,"maximum":2200},{"type":"null"}]},"setName":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"cardNumber":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"condition":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}]},"itemName":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"categoryLabel":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"tags":{"anyOf":[{"maxItems":20,"type":"array","items":{"type":"string","maxLength":50}},{"type":"null"}]}},"required":["itemType","status"],"additionalProperties":false}},"extras":{"description":"An item with status 'extras' is publicly listed for trade/sale at /extras and /<username>/extras. To list several copies of the same card, bulk-insert N identical items; they may share one imageKey (images are only deleted from storage when no item references them).","example":{"items":[{"itemType":"sports_card","status":"extras","playerName":"Julio Rodríguez","cardYear":2022,"setName":"Topps Chrome","cardNumber":"#200","condition":"Raw","imageKey":"items/<userId>/<id>.jpg"},{"itemType":"sports_card","status":"extras","playerName":"Julio Rodríguez","cardYear":2022,"setName":"Topps Chrome","cardNumber":"#200","condition":"Raw","imageKey":"items/<userId>/<id>.jpg"},{"itemType":"sports_card","status":"extras","playerName":"Julio Rodríguez","cardYear":2022,"setName":"Topps Chrome","cardNumber":"#200","condition":"Raw","imageKey":"items/<userId>/<id>.jpg"}]}},"offer":{"description":"POST /api/v1/items/:id/offers body. dollars is the cash component (0 = cards only); extraItemIds are IDs of YOUR items with status 'extras'. At least one is required. One pending offer per item per offerer. Responses carry usernames, never user IDs.","jsonSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"dollars":{"default":0,"type":"number","minimum":0,"maximum":100000},"extraItemIds":{"default":[],"maxItems":50,"type":"array","items":{"type":"string","maxLength":50}},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"imageKey":{"anyOf":[{"type":"string","maxLength":200,"pattern":"^items\\/"},{"type":"null"}]}},"required":["dollars","extraItemIds"],"additionalProperties":false},"respond":{"description":"PATCH /api/v1/offers/:id body","jsonSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"status":{"type":"string","enum":["accepted","declined","withdrawn"]}},"required":["status"],"additionalProperties":false}},"statuses":["pending","accepted","declined","withdrawn"]},"bulkInsert":{"description":"Wrap an array of items in a top-level 'items' key","maxItems":100,"jsonSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"itemType":{"type":"string","enum":["sports_card","custom"]},"status":{"default":"owned","type":"string","enum":["owned","hunting","extras"]},"notes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"imageKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"playerName":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"cardYear":{"anyOf":[{"type":"integer","minimum":1800,"maximum":2200},{"type":"null"}]},"setName":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"cardNumber":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"condition":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}]},"itemName":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"categoryLabel":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"tags":{"anyOf":[{"maxItems":20,"type":"array","items":{"type":"string","maxLength":50}},{"type":"null"}]}},"required":["itemType","status"],"additionalProperties":false}}},"required":["items"],"additionalProperties":false},"example":{"items":[{"itemType":"sports_card","status":"owned","playerName":"Mike Trout","cardYear":2011,"setName":"Topps Update","cardNumber":"#US175"},{"itemType":"custom","status":"hunting","itemName":"2017 World Series Program","categoryLabel":"Memorabilia"}]}}}