{"openapi":"3.1.0","info":{"title":"Shift Nudge Public API","version":"1.0.0","summary":"Public read endpoints for shiftnudge.com.","description":"The public, unauthenticated JSON API for Shift Nudge (professional interface design training). It exposes read endpoints (service health, newsletter reach) and a WCAG 2.1 / APCA contrast calculator. For membership-gated design reviews, connect the MCP server described at /.well-known/mcp/server-card.json. A prose overview of every machine-readable surface is at /developers, and the canonical facts are at /facts.json.\n\n## Versioning\nThis API is versioned. The current version is 1.0.0 (major.minor.patch); the major version is the compatibility contract. Every response carries an `X-API-Version` header, and a request MAY send `X-API-Version` to pin a major. Breaking changes ship a new major and the prior major is announced with a `Deprecation` header and a `Sunset` header (RFC 8594) giving the retirement date, no sooner than 90 days out.\n\n## Rate limits\nResponses carry RFC 9331 `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers (plus `RateLimit-Policy`). The public read endpoints are advisory today; a `429` response would include `Retry-After`. Self-throttle from these headers.\n\n## Authentication\nEvery endpoint in this document is public and needs no authentication, key, or token. The separate membership-gated design-review surface is the MCP server, which uses OAuth 2.0 with a `review` scope; its scopes and metadata are published at /.well-known/oauth-protected-resource (not here, because this REST API is unauthenticated).\n\n## Deprecation\nNothing is deprecated today. When an endpoint or version is retired, its responses carry a `Deprecation: true` header and a `Sunset` header (RFC 8594) with the retirement date at least 90 days out; the machine-readable policy is the `x-deprecation-policy` field below.","contact":{"name":"Shift Nudge support","email":"help@shiftnudge.com","url":"https://www.shiftnudge.com/contact"},"license":{"name":"Proprietary"}},"x-deprecation-policy":{"versioning":"semver-major","version_header":"X-API-Version","signals":["Deprecation","Sunset"],"sunset_rfc":"RFC 8594","minimum_notice_days":90,"policy_url":"https://www.shiftnudge.com/developers"},"servers":[{"url":"https://www.shiftnudge.com","description":"This origin"}],"externalDocs":{"description":"Developer & agent resources","url":"https://www.shiftnudge.com/developers"},"security":[],"paths":{"/api/health":{"get":{"operationId":"getHealth","summary":"Service health and resource descriptor","description":"Returns service status and absolute URLs to every machine-readable surface (OpenAPI, developer docs, facts, llms.txt, sitemap, MCP server card, contrast). Unauthenticated. Safe to poll.","tags":["status"],"security":[],"parameters":[{"$ref":"#/components/parameters/ApiVersionHeader"}],"responses":{"200":{"description":"Service is healthy.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-API-Version":{"$ref":"#/components/headers/ApiVersion"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"405":{"description":"A non-GET method was used.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-API-Version":{"$ref":"#/components/headers/ApiVersion"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/contrast":{"get":{"operationId":"computeContrast","summary":"WCAG 2.1 and APCA contrast for a color pair","description":"Computes the WCAG 2.1 contrast ratio (with AA/AAA pass flags) and the APCA Lc for a foreground and background color. Colors are any CSS color (hex, rgb(), hsl(), named). Unauthenticated and deterministic.","tags":["contrast"],"security":[],"parameters":[{"name":"fg","in":"query","required":true,"description":"Foreground (text) color as any CSS color.","schema":{"type":"string","examples":["#767676"]}},{"name":"bg","in":"query","required":true,"description":"Background color as any CSS color.","schema":{"type":"string","examples":["white"]}},{"$ref":"#/components/parameters/ApiVersionHeader"}],"responses":{"200":{"description":"The computed contrast.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-API-Version":{"$ref":"#/components/headers/ApiVersion"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contrast"}}}},"400":{"description":"A required parameter is missing, or a color could not be parsed.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-API-Version":{"$ref":"#/components/headers/ApiVersion"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"A non-GET method was used.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-API-Version":{"$ref":"#/components/headers/ApiVersion"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/subscriber-count":{"get":{"operationId":"getSubscriberCount","summary":"Newsletter subscriber count","description":"Returns the approximate number of Shift Nudge newsletter subscribers. Cached daily. Unauthenticated.","tags":["stats"],"security":[],"responses":{"200":{"description":"The current subscriber count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriberCount"}}}}}}}},"components":{"parameters":{"ApiVersionHeader":{"name":"X-API-Version","in":"header","required":false,"description":"Optional. Pin the API major version (e.g. \"1\"). The response always echoes X-API-Version.","schema":{"type":"string","examples":["1.0.0"]}}},"headers":{"RateLimitLimit":{"description":"Requests allowed in the current window (RFC 9331).","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests remaining in the current window (RFC 9331).","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until the current window resets (RFC 9331).","schema":{"type":"integer"}},"ApiVersion":{"description":"The API version that served the response.","schema":{"type":"string"}}},"schemas":{"Health":{"type":"object","required":["status","service","version","resources"],"properties":{"status":{"type":"string","enum":["ok"],"description":"Overall service status."},"service":{"type":"string","description":"Service identifier.","examples":["shiftnudge-web"]},"version":{"type":"string","description":"Service version.","examples":["1.0.0"]},"resources":{"type":"object","description":"Absolute URLs to the machine-readable surfaces of this origin.","additionalProperties":{"type":"string","format":"uri"}}}},"Contrast":{"type":"object","required":["foreground","background","wcag","apca"],"properties":{"foreground":{"type":"string","description":"Echoed foreground color."},"background":{"type":"string","description":"Echoed background color."},"wcag":{"type":"object","required":["ratio","aa","aaLarge","aaa","aaaLarge"],"properties":{"ratio":{"type":"number","description":"WCAG 2.1 contrast ratio, 1..21."},"aa":{"type":"boolean","description":"Passes AA for normal text (>= 4.5)."},"aaLarge":{"type":"boolean","description":"Passes AA for large text (>= 3)."},"aaa":{"type":"boolean","description":"Passes AAA for normal text (>= 7)."},"aaaLarge":{"type":"boolean","description":"Passes AAA for large text (>= 4.5)."}}},"apca":{"type":"object","required":["lc"],"properties":{"lc":{"type":"number","description":"APCA Lightness contrast (Lc), signed."}}}}},"SubscriberCount":{"type":"object","required":["count"],"properties":{"count":{"type":"integer","minimum":0,"description":"Approximate newsletter subscriber count."}}},"Error":{"type":"object","required":["error"],"description":"Structured error envelope returned by every endpoint on failure.","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code.","examples":["method_not_allowed"]},"message":{"type":"string","description":"Human-readable description of the error."},"hint":{"type":"string","description":"How to resolve the error."}}}}}}},"x-mcp":{"description":"Membership-gated design intelligence (design reviews) is exposed over MCP, not this REST API.","serverCard":"https://www.shiftnudge.com/.well-known/mcp/server-card.json"}}