{"openapi":"3.1.0","info":{"title":"Tier 4 API","version":"1.0.0","description":"Public API for structured trading-card catalog data and optimized image records."},"servers":[{"url":"https://api.tier4.club","description":"Production"}],"tags":[{"name":"System"},{"name":"Catalog"},{"name":"Images"}],"paths":{"/health":{"get":{"tags":["System"],"summary":"Health check","responses":{"200":{"description":"Service health","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/ready":{"get":{"tags":["System"],"summary":"Readiness check","responses":{"200":{"description":"Service is ready to serve database-backed image routes","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Readiness"}}}},"503":{"description":"Service is alive but missing required runtime dependencies","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Readiness"}}}}}}},"/v1/sets":{"get":{"tags":["Catalog"],"summary":"List catalog sets","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"responses":{"200":{"description":"Set list","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Set"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/stats":{"get":{"tags":["System"],"summary":"Get catalog and import statistics","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"responses":{"200":{"description":"Catalog statistics","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Stats"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/quality":{"get":{"tags":["System"],"summary":"Get catalog data-quality checks","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"responses":{"200":{"description":"Catalog quality report","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/QualityReport"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/meta":{"get":{"tags":["System"],"summary":"Get API capability metadata","description":"Returns safe runtime metadata for clients and Codex, including auth mode, rate-limit policy, cache TTLs, docs links, and image rendition defaults.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"responses":{"200":{"description":"API metadata","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Meta"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/cards":{"get":{"tags":["Catalog"],"summary":"List cards","description":"Supports broad text search, filters, offset pagination, and cursor pagination. Prefer cursor pagination for repeated crawling.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"set","in":"query","schema":{"type":"string"}},{"name":"rarity","in":"query","schema":{"type":"string"}},{"name":"character","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"images","in":"query","description":"Set to true or 1 to include image manifests on each card summary.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Paginated cards","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CardSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/cards/{idOrSlug}":{"get":{"tags":["Catalog"],"summary":"Get one card by ID or slug","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Card detail with image records","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CardDetail"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/images/{imageId}":{"get":{"tags":["Images"],"summary":"Redirect to the default image rendition URL","description":"Redirects to IMAGE_DEFAULT_RENDITION by default. Use w/width, dpr, rendition, Width, and DPR hints to select the smallest useful prebuilt WebP rendition. HEAD and conditional requests are supported for cache validation. Redirect routes are browser-loadable without an API key by default so img tags can render protected catalog results.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"parameters":[{"name":"imageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"w","in":"query","schema":{"type":"integer","minimum":1,"maximum":2400},"description":"Desired CSS pixel width."},{"name":"width","in":"query","schema":{"type":"integer","minimum":1,"maximum":2400},"description":"Alias for w."},{"name":"dpr","in":"query","schema":{"type":"number","minimum":1,"maximum":3},"description":"Device pixel ratio multiplier."},{"name":"rendition","in":"query","schema":{"type":"string","enum":["optimized","preview","thumbnail"]},"description":"Explicit rendition override."},{"name":"If-None-Match","in":"header","schema":{"type":"string"}},{"name":"If-Modified-Since","in":"header","schema":{"type":"string"}},{"name":"Width","in":"header","schema":{"type":"integer","minimum":1,"maximum":2400},"description":"Client Hints width header."},{"name":"DPR","in":"header","schema":{"type":"number","minimum":1,"maximum":3},"description":"Client Hints DPR header."}],"responses":{"302":{"description":"Image URL redirect","headers":{"Location":{"schema":{"type":"string","format":"uri"}},"X-Image-Rendition":{"schema":{"type":"string","enum":["optimized","preview","thumbnail"]},"description":"The rendition selected for the redirect."},"X-Image-Mime-Type":{"schema":{"type":"string"},"description":"MIME type of the selected rendition."},"X-Image-Width":{"schema":{"type":"integer"},"description":"Pixel width of the selected rendition."},"X-Image-Height":{"schema":{"type":"integer"},"description":"Pixel height of the selected rendition."},"X-Image-Bytes":{"schema":{"type":"integer"},"description":"Byte size of the selected rendition."},"ETag":{"schema":{"type":"string"},"description":"Stable validator for the selected image rendition."},"Last-Modified":{"schema":{"type":"string"},"description":"HTTP date for the image record."},"Link":{"schema":{"type":"string"},"description":"Related manifest link for the image."},"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}}},"304":{"description":"Cached redirect is still fresh","headers":{"X-Image-Rendition":{"schema":{"type":"string","enum":["optimized","preview","thumbnail"]},"description":"The rendition selected for the redirect."},"X-Image-Mime-Type":{"schema":{"type":"string"},"description":"MIME type of the selected rendition."},"X-Image-Width":{"schema":{"type":"integer"},"description":"Pixel width of the selected rendition."},"X-Image-Height":{"schema":{"type":"integer"},"description":"Pixel height of the selected rendition."},"X-Image-Bytes":{"schema":{"type":"integer"},"description":"Byte size of the selected rendition."},"ETag":{"schema":{"type":"string"},"description":"Stable validator for the selected image rendition."},"Last-Modified":{"schema":{"type":"string"},"description":"HTTP date for the image record."},"Link":{"schema":{"type":"string"},"description":"Related manifest link for the image."},"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/images":{"get":{"tags":["Images"],"summary":"Get image manifests in batches","description":"Returns up to 100 image manifests for plugin/front-end preloading. Preserve request order with missing IDs reported separately.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"parameters":[{"name":"ids","in":"query","schema":{"type":"string"},"description":"Comma-separated image UUIDs."},{"name":"id","in":"query","schema":{"type":"array","items":{"type":"string","format":"uuid"}},"style":"form","explode":true,"description":"Repeated image UUIDs."}],"responses":{"200":{"description":"Image manifests","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ImageManifest"}},"missingIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["data","missingIds"]}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/images/{imageId}/manifest":{"get":{"tags":["Images"],"summary":"Get one image manifest","description":"Returns responsive image URLs, dimensions, bytes, aspect ratio, cache policy, and rendition metadata without following a redirect.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"parameters":[{"name":"imageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Image manifest","headers":{"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ImageManifest"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/images/{imageId}/{rendition}":{"get":{"tags":["Images"],"summary":"Redirect to a signed image rendition URL","description":"Redirects to an explicit prebuilt WebP rendition. HEAD and conditional requests are supported for cache validation. Redirect routes are browser-loadable without an API key by default so img tags can render protected catalog results.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]},{}],"parameters":[{"name":"imageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"rendition","in":"path","required":true,"schema":{"type":"string","enum":["optimized","preview","thumbnail"]}},{"name":"If-None-Match","in":"header","schema":{"type":"string"}},{"name":"If-Modified-Since","in":"header","schema":{"type":"string"}}],"responses":{"302":{"description":"Image rendition URL redirect","headers":{"Location":{"schema":{"type":"string","format":"uri"}},"X-Image-Rendition":{"schema":{"type":"string","enum":["optimized","preview","thumbnail"]},"description":"The rendition selected for the redirect."},"X-Image-Mime-Type":{"schema":{"type":"string"},"description":"MIME type of the selected rendition."},"X-Image-Width":{"schema":{"type":"integer"},"description":"Pixel width of the selected rendition."},"X-Image-Height":{"schema":{"type":"integer"},"description":"Pixel height of the selected rendition."},"X-Image-Bytes":{"schema":{"type":"integer"},"description":"Byte size of the selected rendition."},"ETag":{"schema":{"type":"string"},"description":"Stable validator for the selected image rendition."},"Last-Modified":{"schema":{"type":"string"},"description":"HTTP date for the image record."},"Link":{"schema":{"type":"string"},"description":"Related manifest link for the image."},"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}}},"304":{"description":"Cached redirect is still fresh","headers":{"X-Image-Rendition":{"schema":{"type":"string","enum":["optimized","preview","thumbnail"]},"description":"The rendition selected for the redirect."},"X-Image-Mime-Type":{"schema":{"type":"string"},"description":"MIME type of the selected rendition."},"X-Image-Width":{"schema":{"type":"integer"},"description":"Pixel width of the selected rendition."},"X-Image-Height":{"schema":{"type":"integer"},"description":"Pixel height of the selected rendition."},"X-Image-Bytes":{"schema":{"type":"integer"},"description":"Byte size of the selected rendition."},"ETag":{"schema":{"type":"string"},"description":"Stable validator for the selected image rendition."},"Last-Modified":{"schema":{"type":"string"},"description":"HTTP date for the image record."},"Link":{"schema":{"type":"string"},"description":"Related manifest link for the image."},"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Optional. Required on /v1 routes only when API_KEY_HASHES or legacy API_KEYS is configured."},"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Optional. Required on /v1 routes only when API_KEY_HASHES or legacy API_KEYS is configured."}},"responses":{"InvalidRequest":{"description":"Request validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"A valid API key is required when API_KEY_HASHES or legacy API_KEYS is configured.","headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"Bearer authentication challenge."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"MethodNotAllowed":{"description":"HTTP method is not supported for this endpoint","headers":{"Allow":{"schema":{"type":"string"},"description":"Comma-separated list of supported methods."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"RateLimited":{"description":"Too many requests","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until another request should be attempted."},"X-Request-ID":{"schema":{"type":"string"},"description":"Request correlation ID."},"X-Cache":{"schema":{"type":"string","enum":["BYPASS","HIT","MISS"]},"description":"In-process API response-cache status when response caching applies."},"RateLimit":{"schema":{"type":"string"},"description":"Current quota hint for the route bucket, using the modern RateLimit field syntax."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests allowed in the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in the current window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current rate-limit window resets."},"RateLimit-Policy":{"schema":{"type":"string"},"description":"Rate-limit policy for the current route bucket."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"Health":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"status":{"type":"string"}},"required":["ok","service","status"]},"Readiness":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"status":{"type":"string"},"checks":{"type":"object","additionalProperties":{"type":"string","enum":["ok","missing","error"]}}},"required":["ok","service","status","checks"]},"Set":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"show":{"type":["string","null"],"description":"Show, franchise, or IP for the set, such as My Little Pony."},"code":{"type":["string","null"]},"series":{"type":["string","null"]},"releasedAt":{"type":["string","null"],"format":"date"}},"required":["id","slug","name"]},"CardSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"cardNumber":{"type":["string","null"]},"cardNumberShort":{"type":["string","null"],"description":"Display-friendly numeric segment, such as 001."},"character":{"type":["string","null"]},"rarity":{"type":["string","null"]},"set":{"$ref":"#/components/schemas/Set"},"images":{"type":"array","items":{"$ref":"#/components/schemas/CardImage"},"description":"Present only when /v1/cards is requested with images=1."}},"required":["id","slug","name"]},"CardDetail":{"allOf":[{"$ref":"#/components/schemas/CardSummary"},{"type":"object","properties":{"sourceId":{"type":["string","null"]},"language":{"type":["string","null"]},"productType":{"type":["string","null"]},"metadata":{"type":"object","additionalProperties":true},"images":{"type":"array","items":{"$ref":"#/components/schemas/CardImage"}}},"required":["images"]}]},"CardImage":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sourceFilename":{"type":["string","null"]},"side":{"type":"string"},"variant":{"type":"string"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"aspectRatio":{"type":["number","null"]},"mimeType":{"type":"string"},"bytes":{"type":["integer","null"]},"contentHash":{"type":["string","null"]},"url":{"type":"string"},"defaultRendition":{"type":["string","null"],"enum":["optimized","preview","thumbnail",null]},"optimizedUrl":{"type":"string"},"previewUrl":{"type":"string"},"thumbnailUrl":{"type":"string"},"srcSet":{"type":["string","null"]},"sizes":{"type":["string","null"]},"cache":{"$ref":"#/components/schemas/ImageCachePolicy"},"renditions":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ImageRendition"}}},"required":["id","side","variant","mimeType","url","defaultRendition"]},"ImageManifest":{"allOf":[{"$ref":"#/components/schemas/CardImage"}]},"ImageRendition":{"type":"object","properties":{"name":{"type":"string","enum":["optimized","preview","thumbnail"]},"path":{"type":"string"},"url":{"type":"string"},"mimeType":{"type":"string"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"bytes":{"type":["integer","null"]},"maxWidth":{"type":"integer"}},"required":["name","path","url","mimeType","width","height","bytes","maxWidth"]},"ImageCachePolicy":{"type":"object","properties":{"public":{"type":"boolean"},"cacheControl":{"type":"string"},"maxAgeSeconds":{"type":"integer"},"immutable":{"type":"boolean"}},"required":["public","cacheControl","maxAgeSeconds","immutable"]},"Pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":["integer","null"]},"cursor":{"type":["string","null"]},"nextCursor":{"type":["string","null"]},"hasMore":{"type":"boolean"}},"required":["limit","offset","cursor","nextCursor","hasMore"]},"Meta":{"type":"object","properties":{"service":{"type":"string"},"version":{"type":"string"},"apiVersion":{"type":"string"},"generatedAt":{"type":"string","format":"date-time"},"docs":{"type":"object","properties":{"human":{"type":"string"},"openapi":{"type":"string"},"llms":{"type":"string"}},"required":["human","openapi","llms"]},"auth":{"type":"object","properties":{"required":{"type":"boolean"},"schemes":{"type":"array","items":{"type":"string"}}},"required":["required","schemes"]},"rateLimit":{"type":"object","properties":{"windowSeconds":{"type":"integer"},"anonymousMaxRequests":{"type":"integer"},"authenticatedMaxRequests":{"type":"integer"},"imageMaxRequests":{"type":"integer"},"headers":{"type":"array","items":{"type":"string"}}},"required":["windowSeconds","anonymousMaxRequests","authenticatedMaxRequests","imageMaxRequests","headers"]},"images":{"type":"object","properties":{"defaultRendition":{"type":"string","enum":["optimized","preview","thumbnail"]},"signedUrlTtlSeconds":{"type":"integer"},"publicBaseUrlConfigured":{"type":"boolean"},"redirectsRequireApiKey":{"type":"boolean"},"browserLoadableRedirects":{"type":"boolean"},"batchMaxIds":{"type":"integer"},"manifestPathTemplate":{"type":"string"},"redirectPathTemplate":{"type":"string"},"renditionPathTemplate":{"type":"string"},"queryHints":{"type":"array","items":{"type":"string"}},"requestHeaders":{"type":"array","items":{"type":"string"}},"responseHeaders":{"type":"array","items":{"type":"string"}},"renditions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","enum":["optimized","preview","thumbnail"]},"maxWidth":{"type":"integer"},"quality":{"type":"integer"},"mimeType":{"type":"string"}},"required":["name","maxWidth","quality","mimeType"]}}},"required":["defaultRendition","signedUrlTtlSeconds","publicBaseUrlConfigured","redirectsRequireApiKey","browserLoadableRedirects","batchMaxIds","manifestPathTemplate","redirectPathTemplate","renditionPathTemplate","queryHints","requestHeaders","responseHeaders","renditions"]},"cache":{"type":"object","properties":{"apiTtlSeconds":{"type":"integer"},"cardListTtlSeconds":{"type":"integer"}},"required":["apiTtlSeconds","cardListTtlSeconds"]}},"required":["service","version","apiVersion","generatedAt","docs","auth","rateLimit","images","cache"]},"Stats":{"type":"object","properties":{"sets":{"type":"integer"},"cards":{"type":"integer"},"cardImages":{"type":"integer"},"uniqueSourceImages":{"type":"integer"},"missingFronts":{"type":"integer"},"storage":{"type":"object","properties":{"optimizedBytes":{"type":"integer"},"originalBytes":{"type":"integer"}},"required":["optimizedBytes","originalBytes"]},"bySet":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"cards":{"type":"integer"}},"required":["slug","name","cards"]}},"latestImport":{"type":["object","null"],"additionalProperties":true}},"required":["sets","cards","cardImages","uniqueSourceImages","missingFronts","storage","bySet","latestImport"]},"QualityReport":{"type":"object","properties":{"ok":{"type":"boolean"},"generatedAt":{"type":"string","format":"date-time"},"checks":{"type":"object","properties":{"missingFronts":{"type":"integer"},"cardsWithoutImages":{"type":"integer"},"duplicateSourceHashes":{"type":"integer"},"latestImportUnmatchedImages":{"type":"integer"}},"required":["missingFronts","cardsWithoutImages","duplicateSourceHashes","latestImportUnmatchedImages"]},"bySet":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"cards":{"type":"integer"},"images":{"type":"integer"},"missingFronts":{"type":"integer"}},"required":["slug","name","cards","images","missingFronts"]}},"latestImport":{"type":["object","null"],"additionalProperties":true}},"required":["ok","generatedAt","checks","bySet","latestImport"]},"ErrorResponse":{"type":"object","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer","minimum":400,"maximum":599},"detail":{"type":"string"},"instance":{"type":"string"},"requestId":{"type":"string"},"issues":{"type":"array","items":{"type":"object","additionalProperties":true}},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"requestId":{"type":"string"},"issues":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message","requestId"]}},"required":["type","title","status","detail","instance","requestId","error"]}}}}