{"openapi":"3.1.0","info":{"title":"The Chicago Signal API","version":"1.0.0","description":"The documented, machine-readable surface of thechicagosignal.com: subscribing to the newsletter, reading published posts, and checking the current deploy.","contact":{"email":"hey@thechicagosignal.com","url":"https://www.thechicagosignal.com/contact"}},"servers":[{"url":"https://www.thechicagosignal.com"}],"paths":{"/api/posts":{"get":{"operationId":"listPosts","summary":"List all published posts","description":"Returns every published guide and newsletter issue as JSON, in the shape { categories, guides, issues }. Cached; safe to poll.","responses":{"200":{"description":"Posts payload.","content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array"},"guides":{"type":"array"},"issues":{"type":"array"}}}}}},"405":{"description":"Wrong method.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream fetch failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/subscribe":{"post":{"operationId":"subscribe","summary":"Subscribe an email address to the newsletter","description":"Submit only an address whose owner asked to subscribe — this sends that address a welcome email. Refuses cross-origin browser writes from any origin other than thechicagosignal.com; a request with no Origin header (server-to-server, curl, an agent) is accepted. Rate limited per IP; see the RateLimit response headers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"utm_source":{"type":"string"},"utm_medium":{"type":"string"},"utm_campaign":{"type":"string"},"utm_term":{"type":"string"},"ref":{"type":"string","description":"A subscriber's own referral code, for referral credit."}}}}}},"responses":{"200":{"description":"Subscribed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"400":{"description":"Invalid body or email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Disallowed cross-origin caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the rate-limit window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream subscription failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/unsubscribe":{"post":{"operationId":"unsubscribe","summary":"Unsubscribe an email address","description":"Rate limited per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Unsubscribed or already inactive.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"Invalid body or email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/__version":{"get":{"operationId":"getVersion","summary":"Current deploy marker","description":"One-curl check that production matches the committed bundle.","responses":{"200":{"description":"Version info.","content":{"application/json":{"schema":{"type":"object","properties":{"builtAt":{"type":"string"},"guideCount":{"type":"integer"},"worker":{"type":"string"}}}}}}}}}},"components":{"schemas":{"Error":{"type":"object","description":"Every error on this site returns this shape. `error` is a plain string kept for backward compatibility with the site's own forms; `code` is the stable machine-readable field.","properties":{"error":{"type":"string","description":"Human-readable message. Always equal to `message`."},"code":{"type":"string","description":"Stable machine-readable error code, e.g. invalid_email."},"message":{"type":"string"},"hint":{"type":"string"},"status":{"type":"integer"},"docs":{"type":"string","format":"uri"},"retryAfter":{"type":"integer","description":"Present on 429 responses only."}},"required":["error","code","message","status"]}}}}