{"openapi":"3.1.0","info":{"title":"PaletteSense API","version":"1.0.0","description":"Read-only color tooling API for PaletteSense: WCAG contrast checks, gradient contrast analysis, CSS gradient generation, 12-step color scale generation, named-color search, and encode/decode of shareable deep links. Pure functions over color math — same inputs always yield the same output. No authentication required. The identical capabilities are also available as an MCP server at https://palettesense.com/api/mcp. Requests are rate-limited per IP (~150/min); every response carries `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers, and an over-limit request returns HTTP 429 with a `Retry-After` header — back off and retry.","contact":{"url":"https://palettesense.com/ai-agent-guide"},"license":{"name":"Free to use"}},"servers":[{"url":"https://palettesense.com","description":"Production"}],"paths":{"/api/contrast":{"get":{"operationId":"checkContrast","summary":"Check WCAG contrast between two colors","description":"Compute the WCAG 2.x contrast ratio between a foreground and background color and return pass/fail for the requested context/level PLUS a full matrix of verdicts for normalText, largeText, and uiElement at AA and AAA. Use this to decide if a text/background pair is accessible. `uiElement` has no AAA threshold, so that verdict is `null`.","parameters":[{"name":"fg","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Foreground / text color."},{"name":"bg","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Background color."},{"name":"context","in":"query","required":false,"schema":{"type":"string","enum":["normalText","largeText","uiElement"],"default":"normalText"},"description":"Usage context for the `requested` verdict."},{"name":"level","in":"query","required":false,"schema":{"type":"string","enum":["AA","AAA"],"default":"AA"},"description":"Conformance level for the `requested` verdict."}],"responses":{"200":{"description":"Contrast ratio and WCAG verdicts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContrastResult"},"example":{"foreground":"#1a1a1a","background":"#ffffff","ratio":17.4,"requested":{"context":"normalText","level":"AA","threshold":4.5,"passes":true},"wcag":{"normalText":{"AA":true,"AAA":true},"largeText":{"AA":true,"AAA":true},"uiElement":{"AA":true,"AAA":null}}}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/contrast/batch":{"post":{"operationId":"checkContrastBatch","summary":"Check WCAG contrast for many pairs","description":"Check up to 50 foreground/background pairs in one request. Each pair takes the same fields as /api/contrast (fg, bg, optional context/level); the response mirrors that result per pair. Use to validate a whole palette in a single call.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pairs"],"properties":{"pairs":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","required":["fg","bg"],"properties":{"fg":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"bg":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"context":{"type":"string","enum":["normalText","largeText","uiElement"],"default":"normalText"},"level":{"type":"string","enum":["AA","AAA"],"default":"AA"}}}}}},"example":{"pairs":[{"fg":"#1a1a1a","bg":"#ffffff"},{"fg":"#777777","bg":"#ffffff","level":"AAA"}]}}}},"responses":{"200":{"description":"Per-pair contrast results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContrastBatchResult"}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/colors/search":{"get":{"operationId":"searchColors","summary":"Search named colors by name, hex, or nearest match","description":"Search a large named-color dataset. `q` can be a color-name prefix (\"teal\"), a full hex value (\"#3a7f9c\") to find the nearest named colors, or a hex prefix (\"#3a\"). The `source` field on each result says how it matched (name | hex | nearest).","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","maxLength":80},"description":"Color name prefix, hex value, or hex prefix."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":12,"default":8},"description":"Max results to return for any search mode."}],"responses":{"200":{"description":"Matching named colors.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColorSearchResult"},"example":{"query":"#3a7f9c","count":2,"results":[{"hex":"#3b83a0","name":"Teal blue","source":"nearest"},{"hex":"#357ca5","name":"Lochmara","source":"nearest"}]}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/optimize-contrast":{"get":{"operationId":"optimizeContrast","summary":"Adjust a color to meet a WCAG contrast target","description":"Given a foreground and background, nudge one color's lightness in OKLCH — keeping its hue — until the pair meets the target context/level, and return the adjusted hex. Use to make a brand color readable without changing its character. If the target is unreachable, returns the best achievable color with `passes: false`.","parameters":[{"name":"fg","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Foreground / text color."},{"name":"bg","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Background color."},{"name":"context","in":"query","required":false,"schema":{"type":"string","enum":["normalText","largeText","uiElement"],"default":"normalText"}},{"name":"level","in":"query","required":false,"schema":{"type":"string","enum":["AA","AAA"],"default":"AA"}},{"name":"adjust","in":"query","required":false,"schema":{"type":"string","enum":["foreground","background"],"default":"foreground"},"description":"Which color to change; the other is held fixed."}],"responses":{"200":{"description":"The adjusted color and before/after ratios.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OptimizeContrastResult"},"example":{"adjust":"foreground","target":{"context":"normalText","level":"AA","threshold":4.5},"foreground":"#595959","background":"#ffffff","original":{"hex":"#7a7a7a","ratio":3.48},"adjusted":{"hex":"#595959","ratio":4.54},"passes":true,"changed":true}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/palette/suggest":{"get":{"operationId":"suggestPalette","summary":"Suggest a harmonious palette from a base color","description":"Build a color scheme around a base color using OKLCH and score each color's contrast against a background. Schemes: complementary, analogous, triadic, tetradic, monochromatic. Each color includes its nearest named color and an aa/ui/fail status.","parameters":[{"name":"base","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Base / seed color."},{"name":"scheme","in":"query","required":false,"schema":{"type":"string","enum":["complementary","analogous","triadic","tetradic","monochromatic"],"default":"analogous"}},{"name":"count","in":"query","required":false,"schema":{"type":"integer","minimum":3,"maximum":9,"default":5},"description":"Number of colors for analogous / monochromatic schemes."},{"name":"background","in":"query","required":false,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional.","default":"#ffffff"},"description":"Background for contrast scoring."}],"responses":{"200":{"description":"Suggested colors with contrast scores.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaletteSuggestResult"},"example":{"base":"#1392e6","scheme":"complementary","background":"#ffffff","colors":[{"hex":"#1392e6","role":"base","hueDeg":246,"name":"Cerulean","contrastOnBackground":{"ratio":2.8,"status":"fail"}},{"hex":"#c98a2b","role":"complement","hueDeg":66,"name":"Ochre","contrastOnBackground":{"ratio":2.4,"status":"fail"}}]}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/scales":{"get":{"operationId":"generateScales","summary":"Generate 12-step color scales from an accent color","description":"Build Radix-style 12-step accent and gray scales, for both light and dark appearances, from one accent color. Scales are generated in OKLCH so lightness steps stay perceptually even; the accent itself is step 9 of the light accent scale. Each step carries a solid hex and an 8-digit alpha variant that reproduces it over the mode backdrop. Text steps (11-12) are enforced against WCAG ratios and the verdicts are reported in `checks`. The response includes a paste-ready CSS custom-properties block (`css`) and a `shareUrl` that opens the scales in the UI. Step semantics: 1-2 backgrounds, 3-5 interactive states, 6-8 borders, 9-10 solid fills, 11-12 text (see `stepUsage`).","parameters":[{"name":"accent","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Accent / brand color the scales are built around."}],"responses":{"200":{"description":"Light and dark scale sets (accent + gray, 12 steps each with alpha variants), WCAG checks, step usage labels, CSS variables, and a share URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScalesResult"}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/text-color":{"get":{"operationId":"accessibleText","summary":"Pick readable text color for a background","description":"Given a background color, return the best text color (black or white), its contrast ratio, WCAG status, and whether it passes the target level, plus the alternative.","parameters":[{"name":"bg","in":"query","required":true,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"description":"Background color."},{"name":"context","in":"query","required":false,"schema":{"type":"string","enum":["normalText","largeText","uiElement"],"default":"normalText"}},{"name":"level","in":"query","required":false,"schema":{"type":"string","enum":["AA","AAA"],"default":"AA"}}],"responses":{"200":{"description":"Recommended text color.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextColorResult"},"example":{"background":"#1392e6","target":{"context":"normalText","level":"AA","threshold":4.5},"text":{"hex":"#000000","ratio":6.28,"status":"aa","passes":true},"alternative":{"hex":"#ffffff","ratio":3.34}}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/gradient/contrast":{"post":{"operationId":"analyzeGradientContrast","summary":"Analyze text contrast across a gradient","description":"Sample contrast of a text color along an entire gradient and report the minimum ratio, an aa/ui/fail status, where it fails (failingRanges), the worst point, and a suggested scrim (overlay color + opacity) that would bring it to AA. The full per-position `samples` array is omitted unless `includeSamples` is true.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gradient":{"type":"object","required":["stops"],"description":"A gradient definition. Provide this OR the `g` encoded string from a share URL.","properties":{"type":{"type":"string","enum":["linear","radial","angular"],"default":"linear"},"angle":{"type":"number","default":90,"description":"Angle in degrees (linear/angular only)."},"interpolation":{"type":"string","enum":["srgb","oklch","hsl-longer"],"default":"srgb","description":"Color interpolation space."},"stops":{"type":"array","minItems":2,"maxItems":20,"items":{"type":"object","required":["hex","position"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"opacity":{"type":"number","minimum":0,"maximum":100,"default":100,"description":"Stop opacity (0-100)."},"position":{"type":"number","minimum":0,"maximum":100,"description":"Stop position along the gradient (0-100 percent)."}}}}}},"g":{"type":"string","description":"Encoded gradient string from a share URL (alternative to `gradient`)."},"textHex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional.","default":"#ffffff"},"backdropHex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional.","default":"#ffffff"},"includeSamples":{"type":"boolean","default":false}}},"example":{"gradient":{"type":"linear","angle":90,"interpolation":"oklch","stops":[{"hex":"#1392e6","opacity":100,"position":0},{"hex":"#32d7ae","opacity":100,"position":100}]},"textHex":"#ffffff"}}}},"responses":{"200":{"description":"Gradient contrast analysis.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GradientContrastResult"},"example":{"min":1.62,"status":"fail","worstAt":62.5,"worstColor":"#32d7ae","failingRanges":[{"start":0,"end":100}],"scrim":{"color":"#000000","opacity":0.4,"ratio":4.53},"recommendedText":{"hex":"#000000","minRatio":6.28,"status":"aa"},"sampleCount":161}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}},"get":{"operationId":"analyzeGradientContrastByUrl","summary":"Analyze gradient contrast from an encoded `g` string","description":"Convenience GET variant of analyzeGradientContrast that takes a `g` encoded gradient (from a /gradients/custom?editor=1&g=... share URL) instead of a JSON body.","parameters":[{"name":"g","in":"query","required":true,"schema":{"type":"string"},"description":"Encoded gradient string."},{"name":"textHex","in":"query","required":false,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional.","default":"#ffffff"}},{"name":"backdropHex","in":"query","required":false,"schema":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional.","default":"#ffffff"}},{"name":"includeSamples","in":"query","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Gradient contrast analysis.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GradientContrastResult"}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/gradient/css":{"post":{"operationId":"generateGradientCss","summary":"Generate CSS for a gradient","description":"Turn a gradient definition into ready-to-paste CSS: the `background-image` value, a CSS custom property, a Tailwind v4 `@theme` block, the compact encoded form, and a shareable URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gradient":{"type":"object","required":["stops"],"description":"A gradient definition. Provide this OR the `g` encoded string from a share URL.","properties":{"type":{"type":"string","enum":["linear","radial","angular"],"default":"linear"},"angle":{"type":"number","default":90,"description":"Angle in degrees (linear/angular only)."},"interpolation":{"type":"string","enum":["srgb","oklch","hsl-longer"],"default":"srgb","description":"Color interpolation space."},"stops":{"type":"array","minItems":2,"maxItems":20,"items":{"type":"object","required":["hex","position"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"opacity":{"type":"number","minimum":0,"maximum":100,"default":100,"description":"Stop opacity (0-100)."},"position":{"type":"number","minimum":0,"maximum":100,"description":"Stop position along the gradient (0-100 percent)."}}}}}},"g":{"type":"string","description":"Encoded gradient string (alternative to `gradient`)."}}},"example":{"gradient":{"type":"linear","angle":90,"interpolation":"srgb","stops":[{"hex":"#1392e6","opacity":100,"position":0},{"hex":"#32d7ae","opacity":100,"position":100}]}}}}},"responses":{"200":{"description":"Generated CSS and share link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GradientCssResult"},"example":{"css":"linear-gradient(90deg, rgba(19, 146, 230, 1) 0%, rgba(50, 215, 174, 1) 100%)","customProperty":"--palette-gradient: linear-gradient(90deg, rgba(19, 146, 230, 1) 0%, rgba(50, 215, 174, 1) 100%);","tailwindTheme":"@theme {\n  --background-image-palette-gradient: linear-gradient(90deg, rgba(19, 146, 230, 1) 0%, rgba(50, 215, 174, 1) 100%);\n}","encoded":"linear-90-srgb-1392e6.100.0-32d7ae.100.100","shareUrl":"https://palettesense.com/gradients/custom?editor=1&g=linear-90-srgb-1392e6.100.0-32d7ae.100.100"}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}},"get":{"operationId":"generateGradientCssByUrl","summary":"Generate gradient CSS from an encoded `g` string","parameters":[{"name":"g","in":"query","required":true,"schema":{"type":"string"},"description":"Encoded gradient string."}],"responses":{"200":{"description":"Generated CSS and share link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GradientCssResult"}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/share/decode":{"get":{"operationId":"decodeShareUrl","summary":"Decode a PaletteSense share URL into structured state","description":"Read the state encoded in a PaletteSense share URL. Pass a full `url` and its `g` (gradient), `ps` (contrast page), and `cart` (favorites) params are parsed automatically — or pass those params directly. Params that fail to decode are listed in `warnings` rather than throwing.","parameters":[{"name":"url","in":"query","required":false,"schema":{"type":"string","format":"uri"},"description":"Full share URL."},{"name":"g","in":"query","required":false,"schema":{"type":"string"},"description":"Encoded gradient param."},{"name":"ps","in":"query","required":false,"schema":{"type":"string"},"description":"Encoded contrast-page param."},{"name":"cart","in":"query","required":false,"schema":{"type":"string"},"description":"Encoded favorites param."}],"responses":{"200":{"description":"Decoded state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareDecodeResult"},"example":{"gradient":{"type":"linear","angle":90,"interpolation":"srgb","stops":[{"hex":"#1392e6","opacity":100,"position":0},{"hex":"#32d7ae","opacity":100,"position":100}]}}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}},"/api/share/encode":{"post":{"operationId":"encodeShareUrl","summary":"Encode a gradient and/or favorites into shareable URLs","description":"Produce shareable PaletteSense deep links. Provide a `gradient` to get a /gradients/custom?editor=1&g=... gradient URL and/or a `cart` array of contrast pairs to get a /app?cart=... favorites URL that opens pre-populated for the user.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gradient":{"type":"object","required":["stops"],"description":"A gradient definition. Provide this OR the `g` encoded string from a share URL.","properties":{"type":{"type":"string","enum":["linear","radial","angular"],"default":"linear"},"angle":{"type":"number","default":90,"description":"Angle in degrees (linear/angular only)."},"interpolation":{"type":"string","enum":["srgb","oklch","hsl-longer"],"default":"srgb","description":"Color interpolation space."},"stops":{"type":"array","minItems":2,"maxItems":20,"items":{"type":"object","required":["hex","position"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"opacity":{"type":"number","minimum":0,"maximum":100,"default":100,"description":"Stop opacity (0-100)."},"position":{"type":"number","minimum":0,"maximum":100,"description":"Stop position along the gradient (0-100 percent)."}}}}}},"cart":{"type":"array","maxItems":50,"items":{"type":"object","required":["foregroundHex","foregroundName","backgroundHex","backgroundName","ratio","status"],"properties":{"foregroundHex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"foregroundName":{"type":"string"},"backgroundHex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"backgroundName":{"type":"string"},"ratio":{"type":"number"},"status":{"type":"string","enum":["aa","ui","fail"]},"backgroundCss":{"type":"string"},"backgroundLabel":{"type":"string"}}}}}},"example":{"gradient":{"type":"linear","angle":90,"interpolation":"srgb","stops":[{"hex":"#1392e6","opacity":100,"position":0},{"hex":"#32d7ae","opacity":100,"position":100}]}}}}},"responses":{"200":{"description":"Shareable URLs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareEncodeResult"},"example":{"gradient":{"g":"linear-90-srgb-1392e6.100.0-32d7ae.100.100","url":"https://palettesense.com/gradients/custom?editor=1&g=linear-90-srgb-1392e6.100.0-32d7ae.100.100"}}}}},"400":{"description":"Validation or processing error with a structured body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_color","message":"`fg` must be a 3- or 6-digit hex color (e.g. \"#1a2b3c\").","field":"fg","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}},"429":{"description":"Rate limit exceeded. Requests are limited per IP (~150/min). Honor the `RateLimit-*` headers and `Retry-After`, then retry.","headers":{"RateLimit-Limit":{"description":"Request quota for the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Unix epoch seconds when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after the window resets.","documentation_url":"https://palettesense.com/ai-agent-guide"}}}}}}}}},"components":{"schemas":{"Hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"Stop":{"type":"object","required":["hex","position"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"opacity":{"type":"number","minimum":0,"maximum":100,"default":100,"description":"Stop opacity (0-100)."},"position":{"type":"number","minimum":0,"maximum":100,"description":"Stop position along the gradient (0-100 percent)."}}},"GradientState":{"type":"object","required":["stops"],"description":"A gradient definition. Provide this OR the `g` encoded string from a share URL.","properties":{"type":{"type":"string","enum":["linear","radial","angular"],"default":"linear"},"angle":{"type":"number","default":90,"description":"Angle in degrees (linear/angular only)."},"interpolation":{"type":"string","enum":["srgb","oklch","hsl-longer"],"default":"srgb","description":"Color interpolation space."},"stops":{"type":"array","minItems":2,"maxItems":20,"items":{"type":"object","required":["hex","position"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"opacity":{"type":"number","minimum":0,"maximum":100,"default":100,"description":"Stop opacity (0-100)."},"position":{"type":"number","minimum":0,"maximum":100,"description":"Stop position along the gradient (0-100 percent)."}}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","documentation_url"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code.","examples":["invalid_color","invalid_gradient","missing_gradient"]},"message":{"type":"string"},"field":{"type":"string","description":"The offending input field, when applicable."},"documentation_url":{"type":"string","format":"uri"}}}}},"ContrastResult":{"type":"object","required":["foreground","background","ratio","requested","wcag"],"properties":{"foreground":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"background":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"ratio":{"type":"number","description":"WCAG contrast ratio (1-21)."},"requested":{"type":"object","required":["context","level","threshold","passes"],"properties":{"context":{"type":"string","enum":["normalText","largeText","uiElement"]},"level":{"type":"string","enum":["AA","AAA"]},"threshold":{"type":"number"},"passes":{"type":"boolean"}}},"wcag":{"type":"object","required":["normalText","largeText","uiElement"],"properties":{"normalText":{"type":"object","required":["AA","AAA"],"properties":{"AA":{"type":"boolean"},"AAA":{"type":"boolean"}}},"largeText":{"type":"object","required":["AA","AAA"],"properties":{"AA":{"type":"boolean"},"AAA":{"type":"boolean"}}},"uiElement":{"type":"object","required":["AA","AAA"],"properties":{"AA":{"type":"boolean"},"AAA":{"type":["boolean","null"],"description":"null means not applicable — WCAG defines no AAA bar for UI components."}}}}}}},"ContrastBatchResult":{"type":"object","required":["count","results"],"properties":{"count":{"type":"integer","description":"Number of pairs checked."},"results":{"type":"array","items":{"$ref":"#/components/schemas/ContrastResult"}}}},"ColorSearchResult":{"type":"object","required":["query","count","results"],"properties":{"query":{"type":"string"},"count":{"type":"integer","description":"Number of results returned (equals results.length; capped by limit)."},"results":{"type":"array","items":{"type":"object","required":["hex","name","source"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"name":{"type":"string"},"source":{"type":"string","enum":["name","hex","nearest"],"description":"How this result matched the query."}}}}}},"OptimizeContrastResult":{"type":"object","required":["adjust","target","foreground","background","original","adjusted","passes","changed"],"properties":{"adjust":{"type":"string","enum":["foreground","background"],"description":"Which color was nudged."},"target":{"type":"object","required":["context","level","threshold"],"properties":{"context":{"type":"string","enum":["normalText","largeText","uiElement"]},"level":{"type":"string","enum":["AA","AAA"]},"threshold":{"type":"number"}}},"foreground":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"background":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"original":{"type":"object","required":["hex","ratio"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"ratio":{"type":"number"}}},"adjusted":{"type":"object","required":["hex","ratio"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"ratio":{"type":"number"}}},"passes":{"type":"boolean"},"changed":{"type":"boolean","description":"False when the pair already passed and nothing changed."}}},"PaletteSuggestResult":{"type":"object","required":["base","scheme","background","colors"],"properties":{"base":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"scheme":{"type":"string","enum":["complementary","analogous","triadic","tetradic","monochromatic"]},"background":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"colors":{"type":"array","items":{"type":"object","required":["hex","role","hueDeg","contrastOnBackground"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"role":{"type":"string","description":"`base` for the requested color; otherwise a scheme label (e.g. `complement`, `+30°`, `triadic-2`)."},"hueDeg":{"type":"number"},"name":{"type":"string","description":"Nearest named color, when one is found."},"contrastOnBackground":{"type":"object","required":["ratio","status"],"properties":{"ratio":{"type":"number"},"status":{"type":"string","enum":["aa","ui","fail"],"description":"Absolute tier: `aa` (ratio >= 4.5), `ui` (>= 3), else `fail`. It does NOT vary by the requested level — read `passes` for the requested verdict."}}}}}}}},"ScaleStep":{"type":"object","required":["step","hex","alphaHex"],"properties":{"step":{"type":"integer","minimum":1,"maximum":12},"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"alphaHex":{"type":"string","description":"8-digit hex that reproduces `hex` when layered on the mode backdrop (white in light mode, black in dark)."}}},"ScaleSet":{"type":"object","required":["appearance","backdrop","accent","gray","contrastText","checks"],"properties":{"appearance":{"type":"string","enum":["light","dark"]},"backdrop":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"What the alpha variants composite against (#ffffff or #000000)."},"accent":{"type":"array","minItems":12,"maxItems":12,"items":{"type":"object","required":["step","hex","alphaHex"],"properties":{"step":{"type":"integer","minimum":1,"maximum":12},"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"alphaHex":{"type":"string","description":"8-digit hex that reproduces `hex` when layered on the mode backdrop (white in light mode, black in dark)."}}}},"gray":{"type":"array","minItems":12,"maxItems":12,"items":{"type":"object","required":["step","hex","alphaHex"],"properties":{"step":{"type":"integer","minimum":1,"maximum":12},"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"alphaHex":{"type":"string","description":"8-digit hex that reproduces `hex` when layered on the mode backdrop (white in light mode, black in dark)."}}},"description":"Accent-tinted neutral scale generated alongside the accent."},"contrastText":{"type":"object","required":["hex","ratio"],"description":"Readable text color for solid step-9 fills (buttons, badges).","properties":{"hex":{"type":"string","enum":["#ffffff","#000000"]},"ratio":{"type":"number"}}},"checks":{"type":"array","description":"WCAG verdicts for the scale's text steps against its backgrounds.","items":{"type":"object","required":["label","foreground","background","ratio","required","passes"],"properties":{"label":{"type":"string"},"foreground":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"background":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"ratio":{"type":"number"},"required":{"type":"number","description":"WCAG threshold this pair is held to (7, 4.5, or 3)."},"passes":{"type":"boolean"}}}}}},"ScalesResult":{"type":"object","required":["accent","light","dark","stepUsage","css","shareUrl"],"properties":{"accent":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"light":{"$ref":"#/components/schemas/ScaleSet"},"dark":{"$ref":"#/components/schemas/ScaleSet"},"stepUsage":{"type":"object","description":"What each step (1-12) is for, e.g. \"1\": \"App background\", \"9\": \"Solid fill (your color)\".","additionalProperties":{"type":"string"}},"css":{"type":"string","description":"Ready-to-paste CSS custom properties for both appearances (--accent-1..12, --accent-a1..a12, --gray-*, --accent-contrast)."},"shareUrl":{"type":"string","format":"uri"}}},"TextColorResult":{"type":"object","required":["background","target","text","alternative"],"properties":{"background":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"target":{"type":"object","required":["context","level","threshold"],"properties":{"context":{"type":"string","enum":["normalText","largeText","uiElement"]},"level":{"type":"string","enum":["AA","AAA"]},"threshold":{"type":"number"}}},"text":{"type":"object","required":["hex","ratio","status","passes"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"ratio":{"type":"number"},"status":{"type":"string","enum":["aa","ui","fail"],"description":"Absolute tier: `aa` (ratio >= 4.5), `ui` (>= 3), else `fail`. It does NOT vary by the requested level — read `passes` for the requested verdict."},"passes":{"type":"boolean"}}},"alternative":{"type":"object","required":["hex","ratio"],"properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"ratio":{"type":"number"}}}}},"GradientContrastResult":{"type":"object","required":["gradient","css","textHex","backdropHex","min","status","worstAt","worstColor","failingRanges","scrim","recommendedText","sampleCount"],"properties":{"gradient":{"$ref":"#/components/schemas/GradientState"},"css":{"type":"string"},"textHex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"backdropHex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"min":{"type":"number","description":"Minimum text contrast ratio sampled across the gradient."},"status":{"type":"string","enum":["aa","ui","fail"],"description":"Absolute tier: `aa` (ratio >= 4.5), `ui` (>= 3), else `fail`. It does NOT vary by the requested level — read `passes` for the requested verdict."},"worstAt":{"type":"number","description":"Position (0-100) of the lowest-contrast point."},"worstColor":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"failingRanges":{"type":"array","items":{"type":"object","required":["start","end"],"properties":{"start":{"type":"number"},"end":{"type":"number"}}}},"scrim":{"type":["object","null"],"description":"Suggested solid overlay to reach AA, or null when none is needed.","properties":{"color":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"opacity":{"type":"number"},"ratio":{"type":"number"}}},"recommendedText":{"type":"object","required":["hex","minRatio","status"],"description":"Best black/white caption color across the whole gradient (highest worst-case contrast), with its minimum ratio.","properties":{"hex":{"type":"string","pattern":"^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$","examples":["#1a1a1a","#fff"],"description":"A 3- or 6-digit hex color. The leading # is optional."},"minRatio":{"type":"number"},"status":{"type":"string","enum":["aa","ui","fail"],"description":"Absolute tier: `aa` (ratio >= 4.5), `ui` (>= 3), else `fail`. It does NOT vary by the requested level — read `passes` for the requested verdict."}}},"sampleCount":{"type":"integer"}}},"GradientCssResult":{"type":"object","required":["gradient","css","customProperty","tailwindTheme","encoded","shareUrl"],"properties":{"gradient":{"$ref":"#/components/schemas/GradientState"},"css":{"type":"string","description":"background-image value."},"customProperty":{"type":"string","description":"CSS custom property declaration."},"tailwindTheme":{"type":"string","description":"Tailwind v4 @theme block."},"encoded":{"type":"string","description":"Compact share token (the `g` value)."},"shareUrl":{"type":"string","format":"uri"}}},"ShareDecodeResult":{"type":"object","description":"Any subset of the decoded state; a key is present only if its param was supplied and decoded.","properties":{"gradient":{"$ref":"#/components/schemas/GradientState"},"contrastPage":{"type":"object","description":"Decoded `ps` contrast-page state.","additionalProperties":true},"cart":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}},"v":{"type":"integer"}}},"warnings":{"type":"array","items":{"type":"string"},"description":"Present only when a supplied token failed to decode."}}},"ShareEncodeResult":{"type":"object","description":"Deep links for whatever was provided (gradient and/or cart).","properties":{"gradient":{"type":"object","properties":{"g":{"type":"string"},"url":{"type":"string","format":"uri"}}},"cart":{"type":"object","properties":{"cart":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}}}}