{"routes":{"/":{"GET":{"description":"Lists every route with parameters and responses","parameters":{},"response":{"routes":{"type":"object","description":"Route index keyed by path, then by method"}}}},"/auth":{"GET":{"description":"Confirms bearer token is valid; requires Authorization header","parameters":{},"response":{"valid":{"type":"boolean","description":"Always true; 401 otherwise"}}}},"/img/:hash/:file":{"GET":{"description":"Serves raw image bytes for one size variant; dev-only stand-in for photos.vacay.robr.app, which serves R2 directly in production","parameters":{"path":{"hash":{"type":"string","description":"Photo hash"},"file":{"type":"string","description":"Size file name, e.g. small.jpg"}}},"response":{"type":"binary","description":"JPEG image bytes with immutable cache headers; 400 invalid hash or size, 404 not found"}}},"/destinations":{"GET":{"description":"Lists destinations with photo counts, ordered by ID","parameters":{"query":{"cursor":{"type":"number","description":"Optional; last ID from previous page"},"limit":{"type":"number","description":"Optional; 1-100, default 50"}}},"response":{"destinations":{"type":"array","description":"Destinations page","items":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"},"slug":{"type":"string","description":"URL-safe slug derived from name"},"country":{"type":"string","description":"Full English country name, derived from countryCode"},"countryCode":{"type":"string","description":"Two-letter ISO 3166-1 alpha-2 country code"},"year":{"type":"number","description":"Destination year"},"startDate":{"type":"string | null","description":"ISO date, start of trip"},"endDate":{"type":"string | null","description":"ISO date, end of trip"},"metadata":{"type":"object","description":"Freeform JSON metadata"},"photos":{"type":"object","description":"Photo count for destination","properties":{"total":{"type":"number","description":"Total photos referencing this destination"}}}}},"meta":{"type":"object","description":"Pagination cursor and total count","properties":{"cursor":{"type":"number | null","description":"Next cursor, null when exhausted"},"total":{"type":"number","description":"Total records matching filters, ignoring limit"}}}}},"POST":{"description":"Creates destination; requires bearer token; year defaults to current year when omitted","parameters":{"json":{"name":{"type":"string","description":"Destination name, 1-200 characters"},"countryCode":{"type":"string","description":"Two-letter ISO 3166-1 alpha-2 country code; full country name is derived from this"},"year":{"type":"number","description":"Optional; defaults to current year"},"startDate":{"type":"string","description":"Optional; ISO date, start of trip"},"endDate":{"type":"string","description":"Optional; ISO date, end of trip"},"metadata":{"type":"object","description":"Optional; freeform JSON metadata"}}},"response":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"},"slug":{"type":"string","description":"URL-safe slug derived from name"},"country":{"type":"string","description":"Full English country name, derived from countryCode"},"countryCode":{"type":"string","description":"Two-letter ISO 3166-1 alpha-2 country code"},"year":{"type":"number","description":"Destination year"},"startDate":{"type":"string | null","description":"ISO date, start of trip"},"endDate":{"type":"string | null","description":"ISO date, end of trip"},"metadata":{"type":"object","description":"Freeform JSON metadata"},"photos":{"type":"object","description":"Photo count for destination","properties":{"total":{"type":"number","description":"Total photos referencing this destination"}}}}}},"/destinations/:id":{"GET":{"description":"Gets single destination with photo count","parameters":{"path":{"id":{"type":"number","description":"Record ID"}}},"response":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"},"slug":{"type":"string","description":"URL-safe slug derived from name"},"country":{"type":"string","description":"Full English country name, derived from countryCode"},"countryCode":{"type":"string","description":"Two-letter ISO 3166-1 alpha-2 country code"},"year":{"type":"number","description":"Destination year"},"startDate":{"type":"string | null","description":"ISO date, start of trip"},"endDate":{"type":"string | null","description":"ISO date, end of trip"},"metadata":{"type":"object","description":"Freeform JSON metadata"},"photos":{"type":"object","description":"Photo count for destination","properties":{"total":{"type":"number","description":"Total photos referencing this destination"}}}}},"PATCH":{"description":"Updates destination fields; requires bearer token; only provided fields are changed","parameters":{"path":{"id":{"type":"number","description":"Record ID"}},"json":{"name":{"type":"string","description":"Optional; new name, 1-200 characters"},"countryCode":{"type":"string","description":"Optional; new two-letter ISO 3166-1 alpha-2 country code; full country name is re-derived from this"},"year":{"type":"number","description":"Optional; new year"},"startDate":{"type":"string","description":"Optional; new ISO start date"},"endDate":{"type":"string","description":"Optional; new ISO end date"},"metadata":{"type":"object","description":"Optional; replaces freeform JSON metadata"}}},"response":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"},"slug":{"type":"string","description":"URL-safe slug derived from name"},"country":{"type":"string","description":"Full English country name, derived from countryCode"},"countryCode":{"type":"string","description":"Two-letter ISO 3166-1 alpha-2 country code"},"year":{"type":"number","description":"Destination year"},"startDate":{"type":"string | null","description":"ISO date, start of trip"},"endDate":{"type":"string | null","description":"ISO date, end of trip"},"metadata":{"type":"object","description":"Freeform JSON metadata"},"photos":{"type":"object","description":"Photo count for destination","properties":{"total":{"type":"number","description":"Total photos referencing this destination"}}}}},"DELETE":{"description":"Deletes destination; requires bearer token; photos are orphaned unless deletePhotos is true","parameters":{"path":{"id":{"type":"number","description":"Record ID"}},"query":{"deletePhotos":{"type":"boolean","description":"Optional; default false; true cascade-deletes referencing photos and their R2 objects"}}},"response":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"},"year":{"type":"number","description":"Destination year"}}}},"/photos":{"GET":{"description":"Lists photos with destination info, ordered by ID, optionally filtered","parameters":{"query":{"cursor":{"type":"number","description":"Optional; last ID from previous page"},"limit":{"type":"number","description":"Optional; 1-100, default 50"},"destinationId":{"type":"number | string","description":"Optional; destination ID, or 'uncategorized' for photos without destination"},"destination":{"type":"string","description":"Optional; destination slug, case-insensitive"},"year":{"type":"number","description":"Optional; matches destination year, start date year, or end date year"}}},"response":{"photos":{"type":"array","description":"Photos page","items":{"id":{"type":"number","description":"Photo ID"},"hash":{"type":"string","description":"Storage key prefix for image variants"},"destination":{"type":"object | null","description":"Associated destination, null when uncategorized","properties":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"}}},"urls":{"type":"object","description":"Image URLs by size, all baseline JPEG","properties":{"small":{"type":"string","description":"Max long edge 600"},"medium":{"type":"string","description":"Max long edge 1280"},"large":{"type":"string","description":"Max long edge 1920"},"full":{"type":"string","description":"Source dimensions"}}},"metadata":{"type":"object","description":"Freeform JSON metadata"}}},"meta":{"type":"object","description":"Pagination cursor and total count","properties":{"cursor":{"type":"number | null","description":"Next cursor, null when exhausted"},"total":{"type":"number","description":"Total records matching filters, ignoring limit"}}}}},"POST":{"description":"Uploads photo and encodes size variants; requires bearer token; 409 with existing photo when content is duplicate","parameters":{"form":{"destinationId":{"type":"number","description":"Destination to associate photo with"},"file":{"type":"file","description":"JPEG or PNG, max 25 MB, max 16 megapixels"},"metadata":{"type":"string","description":"Optional; JSON-encoded freeform metadata"}}},"response":{"id":{"type":"number","description":"Photo ID"},"hash":{"type":"string","description":"Storage key prefix for image variants"},"destination":{"type":"object | null","description":"Associated destination, null when uncategorized","properties":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"}}},"urls":{"type":"object","description":"Image URLs by size, all baseline JPEG","properties":{"small":{"type":"string","description":"Max long edge 600"},"medium":{"type":"string","description":"Max long edge 1280"},"large":{"type":"string","description":"Max long edge 1920"},"full":{"type":"string","description":"Source dimensions"}}},"metadata":{"type":"object","description":"Freeform JSON metadata"}}}},"/photos/random":{"GET":{"description":"Gets one random photo; 404 when no photos exist","parameters":{},"response":{"id":{"type":"number","description":"Photo ID"},"hash":{"type":"string","description":"Storage key prefix for image variants"},"destination":{"type":"object | null","description":"Associated destination, null when uncategorized","properties":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"}}},"urls":{"type":"object","description":"Image URLs by size, all baseline JPEG","properties":{"small":{"type":"string","description":"Max long edge 600"},"medium":{"type":"string","description":"Max long edge 1280"},"large":{"type":"string","description":"Max long edge 1920"},"full":{"type":"string","description":"Source dimensions"}}},"metadata":{"type":"object","description":"Freeform JSON metadata"}}}},"/photos/:id":{"GET":{"description":"Gets single photo with destination info; bytes always come from url values","parameters":{"path":{"id":{"type":"number","description":"Record ID"}}},"response":{"id":{"type":"number","description":"Photo ID"},"hash":{"type":"string","description":"Storage key prefix for image variants"},"destination":{"type":"object | null","description":"Associated destination, null when uncategorized","properties":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"}}},"urls":{"type":"object","description":"Image URLs by size, all baseline JPEG","properties":{"small":{"type":"string","description":"Max long edge 600"},"medium":{"type":"string","description":"Max long edge 1280"},"large":{"type":"string","description":"Max long edge 1920"},"full":{"type":"string","description":"Source dimensions"}}},"metadata":{"type":"object","description":"Freeform JSON metadata"}}},"PATCH":{"description":"Updates photo's destination and/or metadata; requires bearer token; replacing file mints new hash and URLs; 409 when replacement content duplicates another photo","parameters":{"path":{"id":{"type":"number","description":"Record ID"}},"form":{"destinationId":{"type":"number","description":"Optional; new destination ID"},"file":{"type":"file","description":"Optional; replacement JPEG or PNG"},"metadata":{"type":"string","description":"Optional; JSON-encoded freeform metadata"}}},"response":{"id":{"type":"number","description":"Photo ID"},"hash":{"type":"string","description":"Storage key prefix for image variants"},"destination":{"type":"object | null","description":"Associated destination, null when uncategorized","properties":{"id":{"type":"number","description":"Destination ID"},"name":{"type":"string","description":"Destination name"}}},"urls":{"type":"object","description":"Image URLs by size, all baseline JPEG","properties":{"small":{"type":"string","description":"Max long edge 600"},"medium":{"type":"string","description":"Max long edge 1280"},"large":{"type":"string","description":"Max long edge 1920"},"full":{"type":"string","description":"Source dimensions"}}},"metadata":{"type":"object","description":"Freeform JSON metadata"}}},"DELETE":{"description":"Deletes photo and all size variants from storage; requires bearer token","parameters":{"path":{"id":{"type":"number","description":"Record ID"}}},"response":{"id":{"type":"number","description":"Photo ID"},"hash":{"type":"string","description":"Photo storage hash"}}}}}}