{"openapi":"3.1.0","info":{"title":"Armage API","version":"1.3.0","summary":"Published News reads, website forms and health, and the client API for an organization's own systems, at api.arm-age.com.","description":"Public routes need no credentials; the client API (tag `Client API`, ADR 0060) needs an organization API key created in the client portal (`Authorization: Bearer armk_live_...`; staging takes `armk_test_...`). Paths are versioned (`/v1`); breaking changes get `/v2`, and a retiring route announces itself with `Deprecation` and `Sunset` headers at least 6 months ahead. Every error is `{ error: { type, code, message, request_id } }`; every response carries `X-Request-Id` and `X-Armage-Version`. Public reads never return drafts or tenant data. Armage does not process patient data: no route accepts it.","contact":{"name":"Armage","url":"https://arm-age.com/contact"}},"servers":[{"url":"https://api.arm-age.com","description":"Production"}],"tags":[{"name":"Health","description":"Liveness and readiness probes."},{"name":"News","description":"Armage News: published, public content (ADR 0035)."},{"name":"Hosted newsrooms","description":"Client organizations' public newsrooms hosted by Armage (ADR 0064): published, public content of one organization per site."},{"name":"Forms","description":"Website forms, reached through arm-age.com."},{"name":"Newsletter","description":"Organizations' weekly digests, subscribed to from their own websites (ADR 0057)."},{"name":"Client API","description":"An organization's own stories, approval queue and published feed, with its API key (ADR 0060). Read-only in v1; approvals stay with people."},{"name":"Webhooks","description":"Events Armage POSTs to an organization's endpoints when its stories move (ADR 0060 phase 2), signed per Standard Webhooks."},{"name":"Meta","description":"About the API."}],"paths":{"/health":{"get":{"operationId":"getHealth","tags":["Health"],"summary":"Liveness","description":"The Worker answers; reports its version and whether a database is configured.","responses":{"200":{"description":"Alive.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/health/ready":{"get":{"operationId":"getReadiness","tags":["Health"],"summary":"Readiness","description":"The database answers and the public read path works. Cached 15 s per isolate so probes cannot load the database. `Cache-Control: no-store`.","responses":{"200":{"description":"Ready.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Readiness"}}}},"503":{"description":"Not ready.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Readiness"}}}}}}},"/v1/public/newsroom":{"get":{"operationId":"listNewsroom","tags":["News"],"summary":"Published News, newest first","description":"Index of published, public items with the featured set and the available types and topics; `cursor` pages through `items` (24 per page). Edge-cacheable (10.9).","parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]}},{"name":"topic","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,200}$"}}],"responses":{"200":{"description":"The list.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentList"}}}},"400":{"description":"Unknown type, malformed topic or cursor (`invalid_parameters`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/newsroom/{slug}":{"get":{"operationId":"getNewsroomItem","tags":["News"],"summary":"One published item","description":"The item with its structured body, cleared assets, people and related items. In-the-news items have an empty body and an `external_url`.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"The item.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentItem"}}}},"404":{"description":"No published item with this slug (`not_found`; cached 60 s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/topics/{slug}":{"get":{"operationId":"getTopic","tags":["News"],"summary":"A topic and its published items","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,200}$"}}],"responses":{"200":{"description":"The topic page.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopicPage"}}}},"400":{"description":"Malformed cursor (`invalid_parameters`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such topic (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/search/newsroom":{"get":{"operationId":"listSearchDocuments","tags":["News"],"summary":"Site-search documents for published items","description":"Up to 500 flattened documents the website merges into its search index.","responses":{"200":{"description":"The documents.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchDocuments"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/sitemap/newsroom":{"get":{"operationId":"listSitemapEntries","tags":["News"],"summary":"Sitemap entries for published items","responses":{"200":{"description":"The entries.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SitemapEntries"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/sites/{site}":{"get":{"operationId":"getNewsroomSite","tags":["Hosted newsrooms"],"summary":"A hosted client newsroom","description":"Name, publisher, accent, cleared logo, whether it is live (indexable) and its canonical address (the client hostname once active, else news.arm-age.com/{site}). Enabled sites only.","parameters":[{"name":"site","in":"path","required":true,"schema":{"type":"string","maxLength":48,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$"}}],"responses":{"200":{"description":"The site.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewsroomSite"}}}},"404":{"description":"No enabled newsroom with this slug (`not_found`; cached 60 s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/sites/{site}/newsroom":{"get":{"operationId":"listSiteNewsroom","tags":["Hosted newsrooms"],"summary":"One organization's published stories, newest first","description":"The same shape and paging as Armage News, for the organization behind {site}: published, public items only.","parameters":[{"name":"site","in":"path","required":true,"schema":{"type":"string","maxLength":48,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$"}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]}},{"name":"topic","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,200}$"}}],"responses":{"200":{"description":"The list.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentList"}}}},"400":{"description":"Unknown type, malformed topic or cursor (`invalid_parameters`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No enabled newsroom with this slug (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/sites/{site}/newsroom/{slug}":{"get":{"operationId":"getSiteNewsroomItem","tags":["Hosted newsrooms"],"summary":"One published story of the site","parameters":[{"name":"site","in":"path","required":true,"schema":{"type":"string","maxLength":48,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"The item.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentItem"}}}},"404":{"description":"No such site, or no published item with this slug in it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/sites/{site}/topics/{slug}":{"get":{"operationId":"getSiteTopic","tags":["Hosted newsrooms"],"summary":"A topic of the site and its published items","parameters":[{"name":"site","in":"path","required":true,"schema":{"type":"string","maxLength":48,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,200}$"}}],"responses":{"200":{"description":"The topic page.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopicPage"}}}},"400":{"description":"Malformed cursor (`invalid_parameters`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such site or topic (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/sites/{site}/sitemap":{"get":{"operationId":"listSiteSitemapEntries","tags":["Hosted newsrooms"],"summary":"Sitemap entries for the site's published items, on its canonical address","parameters":[{"name":"site","in":"path","required":true,"schema":{"type":"string","maxLength":48,"pattern":"^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$"}}],"responses":{"200":{"description":"The entries.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SitemapEntries"}}}},"404":{"description":"No enabled newsroom with this slug (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/hosts/{hostname}":{"get":{"operationId":"getNewsroomSiteByHostname","tags":["Hosted newsrooms"],"summary":"The hosted newsroom served on a client hostname","description":"Resolves only a hostname whose Cloudflare for SaaS certificate Armage has confirmed active; a pending hostname is 404.","parameters":[{"name":"hostname","in":"path","required":true,"schema":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"}}],"responses":{"200":{"description":"The site.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"Cache-Control":{"description":"public, s-maxage=300, stale-while-revalidate=86400, stale-if-error=604800","schema":{"type":"string"}},"Cache-Tag":{"description":"Purge keys: newsroom-index, content:<id>, topic:<slug>, search, sitemap.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewsroomSite"}}}},"404":{"description":"No active hostname of an enabled newsroom (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Content is not available (no database in this environment, or the read failed). `Retry-After: 60`, `Cache-Control: no-store`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/contact":{"post":{"operationId":"submitContactEnquiry","tags":["Forms"],"summary":"Send a project inquiry","description":"The website \"Get started\" form. Validated (Zod), rate limited per client IP, bot-screened, then the team email and an acknowledgement go out through Resend in one idempotent batch keyed by `submission_id`. Nothing is stored. Never send patient or medical information.","requestBody":{"description":"The inquiry, as JSON or form fields.","required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ContactEnquiry"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"application/x-www-form-urlencoded":{"schema":{"allOf":[{"$ref":"#/components/schemas/ContactEnquiry"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"multipart/form-data":{"schema":{"allOf":[{"$ref":"#/components/schemas/ContactEnquiry"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}}}},"responses":{"202":{"description":"Accepted. The team email and the acknowledgement were queued (or, for a submission that looks automated, silently dropped - the response is identical). `Cache-Control: no-store`.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Accepted"}}}},"415":{"description":"Body is neither JSON nor form data (`unsupported_body`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; `error.fields` names each field (`validation_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 5 submissions a minute from this client (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The email provider refused the send; retry with the same `submission_id` (`delivery_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Forms are not configured in this environment (`forms_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/careers":{"post":{"operationId":"submitCareerApplication","tags":["Forms"],"summary":"Send a career application","description":"The website careers form; a profile link replaces file uploads. Same pipeline as the contact form.","requestBody":{"description":"The application, as JSON or form fields.","required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/CareerApplication"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"application/x-www-form-urlencoded":{"schema":{"allOf":[{"$ref":"#/components/schemas/CareerApplication"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"multipart/form-data":{"schema":{"allOf":[{"$ref":"#/components/schemas/CareerApplication"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}}}},"responses":{"202":{"description":"Accepted. The team email and the acknowledgement were queued (or, for a submission that looks automated, silently dropped - the response is identical). `Cache-Control: no-store`.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Accepted"}}}},"415":{"description":"Body is neither JSON nor form data (`unsupported_body`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; `error.fields` names each field (`validation_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 5 submissions a minute from this client (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The email provider refused the send; retry with the same `submission_id` (`delivery_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Forms are not configured in this environment (`forms_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/newsletter/subscribe":{"post":{"operationId":"subscribeNewsletter","tags":["Forms"],"summary":"Subscribe to the weekly News digest","description":"Double opt-in (ADR 0047, ADR 0070): sends a confirmation email whose single-use link (48 hours) opens arm-age.com/news/subscribe/confirm; nothing is subscribed until it is used. The same 202 whether the address is new, pending, already subscribed or throttled (at most one confirmation an hour and two a day per address). Every digest carries an unsubscribe link. 503 when the digest is not configured in this environment.","requestBody":{"description":"The address and consent.","required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/NewsletterSubscription"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"application/x-www-form-urlencoded":{"schema":{"allOf":[{"$ref":"#/components/schemas/NewsletterSubscription"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"multipart/form-data":{"schema":{"allOf":[{"$ref":"#/components/schemas/NewsletterSubscription"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}}}},"responses":{"202":{"description":"Accepted. The team email and the acknowledgement were queued (or, for a submission that looks automated, silently dropped - the response is identical). `Cache-Control: no-store`.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Accepted"}}}},"415":{"description":"Body is neither JSON nor form data (`unsupported_body`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; `error.fields` names each field (`validation_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 5 submissions a minute from this client (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The email provider refused the send; retry with the same `submission_id` (`delivery_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Forms are not configured in this environment (`forms_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/newsletter/subscribe/confirm":{"post":{"operationId":"confirmDigestSubscription","tags":["Forms"],"summary":"Confirm a weekly News digest subscription","description":"Uses the token from the confirmation email (ADR 0070), posted by the button on arm-age.com/news/subscribe/confirm: joins the digest list, then sends the welcome email. Single use; a used link answers `confirmed` again without doing anything. Unknown, malformed and expired tokens all answer 410.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["t"],"properties":{"t":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["t"],"properties":{"t":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$"}}}}}},"responses":{"200":{"description":"`{ \"status\": \"confirmed\" }`.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["confirmed"]}}}}}},"410":{"description":"`{ \"status\": \"expired\" }`: unknown, malformed, expired or another list’s token.","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["expired"]}}}}}},"429":{"description":"Too many attempts from this client (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The email provider refused the change (`delivery_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The digest is not configured in this environment (`forms_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/newsletter/{org}/subscribe":{"post":{"operationId":"subscribeOrganizationNewsletter","tags":["Newsletter"],"summary":"Subscribe to an organization's weekly digest (from its own website)","description":"For an organization's own website on any CMS (ADR 0057): a plain HTML form, the hosted embed or a headless call. The `Origin` (or `Referer`) must be one of the organization's allowed website origins, or its own hosted newsroom (ADR 0069); CORS answers only those. Rate limited per client IP, bot-screened (honeypot, timing), then double opt-in: a confirmation email with a single-use link valid 48 hours; nothing is subscribed until the link is used, and at most two confirmations an hour go to one address. A form post without `Accept: application/json` is answered `303` back to `redirect` (on an allowed origin) or the posting page, with `?newsletter=pending|invalid|subscribed`, or `closed` while the organization's newsletter is turned off. Never send patient or medical information.","parameters":[{"name":"org","in":"path","required":true,"description":"The organization's slug.","schema":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$"}}],"requestBody":{"description":"The address, consent and an optional return URL, as JSON or form fields.","required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/TenantNewsletterSubscription"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"application/x-www-form-urlencoded":{"schema":{"allOf":[{"$ref":"#/components/schemas/TenantNewsletterSubscription"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"multipart/form-data":{"schema":{"allOf":[{"$ref":"#/components/schemas/TenantNewsletterSubscription"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}}}},"responses":{"202":{"description":"`{ \"status\": \"pending\" }`: a confirmation email was sent (or, for an automated-looking or throttled request, silently not - the response is identical). `\"subscribed\"` only for single opt-in behind a verified Turnstile token.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["pending","subscribed"]}}}}}},"303":{"description":"Form post: back to the website with `?newsletter=<outcome>`."},"403":{"description":"The request does not come from an allowed origin (`origin_not_allowed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No enabled newsletter for this organization (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is neither JSON nor form data (`unsupported_body`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; `error.fields` names each field (`validation_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 5 requests a minute from this client (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The email provider refused the send (`delivery_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The newsletter is not configured in this environment (`forms_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/newsletter/embed.js":{"get":{"operationId":"getNewsletterEmbedScript","tags":["Newsletter"],"summary":"The optional embed script for client websites","description":"Enhances a `<form data-armage-newsletter>` (docs/runbooks/newsletter.md) to post with fetch and show the outcome in place. No dependencies, no cookies. `Cache-Control: public, max-age=3600`.","responses":{"200":{"description":"The script.","content":{"text/javascript":{"schema":{"type":"string"}}}}}}},"/v1/public/newsletter/confirm":{"get":{"operationId":"showNewsletterConfirmation","tags":["Newsletter"],"summary":"The confirmation page behind the link in the email","description":"An HTML page with one button that POSTs the token. Opening the link never subscribes anyone (mail scanners prefetch links).","parameters":[{"name":"t","in":"query","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$"}}],"responses":{"200":{"description":"The confirmation page (or \"already confirmed\")."},"410":{"description":"Unknown, used or expired link."}}},"post":{"operationId":"confirmNewsletterSubscription","tags":["Newsletter"],"summary":"Confirm a subscription (double opt-in)","description":"Opts the address in to the organization's digest (its Resend segment and topic), removes the address from Armage's database and sends a welcome email. Idempotent: a second confirmation changes nothing.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["t"],"properties":{"t":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$"}}}}}},"responses":{"200":{"description":"Confirmed; an HTML page (when the request had no return URL)."},"303":{"description":"Confirmed; back to the website with `?newsletter=confirmed`."},"410":{"description":"Unknown, used or expired link."},"429":{"description":"Rate limited."},"502":{"description":"The email provider failed; the link stays usable."},"503":{"description":"The newsletter is not configured in this environment."}}}},"/v1/public/start":{"post":{"operationId":"requestMappingCall","tags":["Forms"],"summary":"Ask for the 20-minute mapping call (the Start flow)","description":"What the organization runs (site, approvals, CRM, sign-in), what it needs, and who to reply to (ADR 0054). Same pipeline as the other forms: rate limit, validation, bot signals, Turnstile, email to the team and an acknowledgement.","requestBody":{"description":"The stack, the need and a reply address.","required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/StartEnquiry"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"application/x-www-form-urlencoded":{"schema":{"allOf":[{"$ref":"#/components/schemas/StartEnquiry"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}},"multipart/form-data":{"schema":{"allOf":[{"$ref":"#/components/schemas/StartEnquiry"},{"$ref":"#/components/schemas/SubmissionEnvelope"}]}}}},"responses":{"202":{"description":"Accepted. The team email and the acknowledgement were queued (or, for a submission that looks automated, silently dropped - the response is identical). `Cache-Control: no-store`.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Accepted"}}}},"415":{"description":"Body is neither JSON nor form data (`unsupported_body`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed; `error.fields` names each field (`validation_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 5 submissions a minute from this client (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The email provider refused the send; retry with the same `submission_id` (`delivery_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Forms are not configured in this environment (`forms_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/checkout/{id}":{"get":{"operationId":"getCheckout","tags":["Commerce"],"summary":"What a checkout bought (the welcome page)","description":"Status, plan, interval and the amount paid, for the page Stripe returns the buyer to. Session ids are unguessable; nothing here lets a stranger act on the account.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^cs_(test|live)_[A-Za-z0-9]{10,200}$"}}],"responses":{"200":{"description":"The checkout summary.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","complete","expired"]},"payment_status":{"type":"string"},"plan":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"interval":{"type":["string","null"]},"amount_total":{"type":["integer","null"]},"currency":{"type":["string","null"]}}}}}},"404":{"description":"Unknown session."},"503":{"description":"Checkout not configured."}}}},"/v1/openapi.json":{"get":{"operationId":"getOpenApiDocument","tags":["Meta"],"summary":"This document","description":"OpenAPI 3.1 description of the public API. `Cache-Control: public, max-age=3600`.","responses":{"200":{"description":"The document.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/stories":{"get":{"operationId":"listStories","tags":["Client API"],"summary":"The organization's stories, newest change first","description":"Every story of the key's organization whatever its status (draft to published), with the current version's title and summary. Filter with `status` (comma-separated). Keyset pagination: pass `next_cursor` back as `cursor`. Scope `stories:read`. `Cache-Control: private, no-store`.","security":[{"apiKey":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"Comma-separated statuses to include: draft, ai_generated, review_required, approved, scheduled, publishing, published, failed, rejected, unpublished, archived."}},{"name":"limit","in":"query","required":false,"schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"The `next_cursor` of the previous page."}}],"responses":{"200":{"description":"A page of stories.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryList"}}}},"400":{"description":"Unknown status, limit outside 1-100 or a malformed cursor (`invalid_parameters`; `error.fields`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/stories/{id}":{"get":{"operationId":"getStory","tags":["Client API"],"summary":"One story with its body and decision history","description":"The current version's structured body and every recorded approval decision, newest first. Only decisions recorded by the approval credential are listed (ADR 0038). Scope `stories:read`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The story.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryDetail"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No story with this id in the key's organization (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/approvals":{"get":{"operationId":"listApprovals","tags":["Client API"],"summary":"The approval queue: stories waiting for a decision","description":"Stories in `review_required`, newest change first. Decisions are made by people in the Armage workspace (email and Slack approvals are planned), never by an API key: nothing publishes unapproved. Scope `approvals:read`.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"The `next_cursor` of the previous page."}}],"responses":{"200":{"description":"A page of waiting stories.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryList"}}}},"400":{"description":"Limit outside 1-100 or a malformed cursor (`invalid_parameters`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/feed.json":{"get":{"operationId":"getFeedJson","tags":["Client API"],"summary":"Published stories as JSON Feed 1.1 (for a CMS to pull)","description":"The 50 most recent published stories with plain-text and minimal escaped HTML bodies. Scope `feed:read`. For importers that cannot send headers, the key may be given as `?key=` - only a key whose sole scope is `feed:read`.","security":[{"apiKey":[]},{"apiKeyQuery":[]}],"responses":{"200":{"description":"The feed (`application/feed+json`).","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/feed+json":{"schema":{"$ref":"#/components/schemas/JsonFeed"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/feed.xml":{"get":{"operationId":"getFeedRss","tags":["Client API"],"summary":"Published stories as RSS 2.0 (for a CMS to pull)","description":"The same 50 stories as `/v1/feed.json`, with `content:encoded` bodies. Scope `feed:read`; the same `?key=` rule.","security":[{"apiKey":[]},{"apiKeyQuery":[]}],"responses":{"200":{"description":"The feed (`application/rss+xml`).","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/rss+xml":{"schema":{"type":"string"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhook_endpoints":{"get":{"operationId":"listWebhookEndpoints","tags":["Client API"],"summary":"The organization's webhook endpoints","description":"Every endpoint, enabled or not, newest first; never its signing secret (only `secret_hint`, its last four characters). Endpoints are added, tested, disabled and re-enabled by an owner or admin in the Armage workspace. Scope `webhooks:read`.","security":[{"apiKey":[]}],"responses":{"200":{"description":"The endpoints (one page).","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointList"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhook_deliveries":{"get":{"operationId":"listWebhookDeliveries","tags":["Client API"],"summary":"The webhook delivery log, newest first","description":"One row per event per endpoint: status (`pending` while retries remain, `delivered`, `failed`), attempts, the last HTTP status and a short error. Payloads are not kept. Filter with `endpoint_id`; keyset pagination as `/v1/stories`. Scope `webhooks:read`.","security":[{"apiKey":[]}],"parameters":[{"name":"endpoint_id","in":"query","required":false,"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Only this endpoint's deliveries."}},{"name":"limit","in":"query","required":false,"schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"The `next_cursor` of the previous page."}}],"responses":{"200":{"description":"A page of deliveries.","headers":{"X-Request-Id":{"description":"Correlates this response with the structured access log.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Requests allowed per key per window: `300;w=60`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"}}}},"400":{"description":"A malformed `endpoint_id`, limit outside 1-100 or a malformed cursor (`invalid_parameters`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No key, a malformed, expired or revoked key, or a key for the other environment (`missing_api_key`, `invalid_api_key`, `wrong_environment`). `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope this route needs (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"More than 300 requests a minute for this key, or a flood from one client IP (`too_many_requests`). `Retry-After: 60`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The client API is not configured in this environment (`api_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"story.submitted":{"post":{"operationId":"webhook_story_submitted","tags":["Webhooks"],"summary":"story.submitted","description":"A story was sent for review: it is now in the approval queue. Sent to every enabled endpoint subscribed to `story.submitted`, signed per Standard Webhooks.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id. Delivery is at least once: dedupe on it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds when this attempt was signed. Reject outside +-5 minutes.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"{webhook-id}.{webhook-timestamp}.{raw body}\">` keyed with the base64-decoded part of the endpoint secret after `whsec_`. Space-separated when several are sent.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received. Answer within 10 seconds; anything else (or a timeout, or a redirect) is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h, then the delivery is `failed`. Three failed deliveries in a row disable the endpoint."}}}},"story.approved":{"post":{"operationId":"webhook_story_approved","tags":["Webhooks"],"summary":"story.approved","description":"A person approved the current version of a story. Sent to every enabled endpoint subscribed to `story.approved`, signed per Standard Webhooks.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id. Delivery is at least once: dedupe on it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds when this attempt was signed. Reject outside +-5 minutes.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"{webhook-id}.{webhook-timestamp}.{raw body}\">` keyed with the base64-decoded part of the endpoint secret after `whsec_`. Space-separated when several are sent.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received. Answer within 10 seconds; anything else (or a timeout, or a redirect) is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h, then the delivery is `failed`. Three failed deliveries in a row disable the endpoint."}}}},"story.rejected":{"post":{"operationId":"webhook_story_rejected","tags":["Webhooks"],"summary":"story.rejected","description":"A person sent a story back with a note. Sent to every enabled endpoint subscribed to `story.rejected`, signed per Standard Webhooks.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id. Delivery is at least once: dedupe on it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds when this attempt was signed. Reject outside +-5 minutes.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"{webhook-id}.{webhook-timestamp}.{raw body}\">` keyed with the base64-decoded part of the endpoint secret after `whsec_`. Space-separated when several are sent.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received. Answer within 10 seconds; anything else (or a timeout, or a redirect) is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h, then the delivery is `failed`. Three failed deliveries in a row disable the endpoint."}}}},"story.published":{"post":{"operationId":"webhook_story_published","tags":["Webhooks"],"summary":"story.published","description":"A story was published. Sent to every enabled endpoint subscribed to `story.published`, signed per Standard Webhooks.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id. Delivery is at least once: dedupe on it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds when this attempt was signed. Reject outside +-5 minutes.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"{webhook-id}.{webhook-timestamp}.{raw body}\">` keyed with the base64-decoded part of the endpoint secret after `whsec_`. Space-separated when several are sent.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received. Answer within 10 seconds; anything else (or a timeout, or a redirect) is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h, then the delivery is `failed`. Three failed deliveries in a row disable the endpoint."}}}},"story.unpublished":{"post":{"operationId":"webhook_story_unpublished","tags":["Webhooks"],"summary":"story.unpublished","description":"A published story was taken down. Sent to every enabled endpoint subscribed to `story.unpublished`, signed per Standard Webhooks.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id. Delivery is at least once: dedupe on it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds when this attempt was signed. Reject outside +-5 minutes.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"{webhook-id}.{webhook-timestamp}.{raw body}\">` keyed with the base64-decoded part of the endpoint secret after `whsec_`. Space-separated when several are sent.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"2XX":{"description":"Received. Answer within 10 seconds; anything else (or a timeout, or a redirect) is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h, then the delivery is `failed`. Three failed deliveries in a row disable the endpoint."}}}},"webhook.test":{"post":{"operationId":"webhook_test","tags":["Webhooks"],"summary":"webhook.test","description":"Sent once when someone presses \"Send test event\" in the Armage workspace. Signed like every event; never recorded as a delivery and never retried.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id. Delivery is at least once: dedupe on it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds when this attempt was signed. Reject outside +-5 minutes.","schema":{"type":"string"}},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"{webhook-id}.{webhook-timestamp}.{raw body}\">` keyed with the base64-decoded part of the endpoint secret after `whsec_`. Space-separated when several are sent.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestEvent"}}}},"responses":{"2XX":{"description":"Received. Answer within 10 seconds; anything else (or a timeout, or a redirect) is retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h, then the delivery is `failed`. Three failed deliveries in a row disable the endpoint."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An organization API key (`armk_live_...` in production, `armk_test_...` on staging), created by an owner or admin in the client portal. Scoped, expiring, revocable; shown once."},"apiKeyQuery":{"type":"apiKey","in":"query","name":"key","description":"Feeds only, and only for a key whose sole scope is `feed:read`."}},"schemas":{"ContactEnquiry":{"type":"object","properties":{"intent":{"type":"string","enum":["authority-programs","content-engine","digital-twins","platforms","websites","partnership","press","other"]},"audience":{"type":"string","enum":["organization","professional","individual"]},"name":{"type":"string","minLength":1,"maxLength":120},"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"organization":{"type":"string","maxLength":160},"role":{"type":"string","maxLength":120},"website":{"type":"string","maxLength":300},"timeline":{"type":"string","enum":["now","quarter","later","exploring"]},"topic":{"type":"string","enum":["project","programme","distribution","custody","newsroom","security","terms"]},"message":{"type":"string","minLength":10,"maxLength":4000},"consent":{"type":"boolean","const":true}},"required":["intent","audience","name","email","message","consent"]},"CareerApplication":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"location":{"type":"string","minLength":2,"maxLength":120},"profile":{"type":"string","maxLength":300},"areas":{"minItems":1,"maxItems":7,"type":"array","items":{"type":"string","enum":["engineering","design","content-video","research-editorial","partnerships-sales","operations","other"]}},"years":{"type":"integer","minimum":0,"maximum":60},"note":{"type":"string","minLength":20,"maxLength":3000},"consent":{"type":"boolean","const":true}},"required":["name","email","location","profile","areas","note","consent"]},"NewsletterSubscription":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"consent":{"type":"boolean","const":true}},"required":["email","consent"]},"TenantNewsletterSubscription":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"consent":{"type":"boolean","const":true},"redirect":{"type":"string","maxLength":2048,"format":"uri"}},"required":["email","consent"]},"StartEnquiry":{"type":"object","properties":{"organizationType":{"type":"string","enum":["programme","system","group","distributor","manufacturer","other"]},"needs":{"minItems":1,"maxItems":6,"type":"array","items":{"type":"string","enum":["newsroom","presence","programme","custody","connect","unsure"]}},"cms":{"type":"string","enum":["wordpress","webflow","drupal","sitecore","custom","none"]},"approvals":{"type":"string","enum":["email","slack","teams"]},"crm":{"type":"string","enum":["salesforce","veeva","hubspot","dynamics","other","none"]},"identity":{"type":"string","enum":["google","microsoft","other","none"]},"website":{"type":"string","maxLength":300},"organization":{"type":"string","maxLength":160},"name":{"type":"string","minLength":1,"maxLength":120},"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"role":{"type":"string","maxLength":120},"notes":{"type":"string","maxLength":1500},"consent":{"type":"boolean","const":true}},"required":["organizationType","needs","name","email","consent"]},"SubmissionEnvelope":{"type":"object","properties":{"company_website":{"type":"string"},"cf-turnstile-response":{"type":"string","maxLength":2048},"started_at":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"submission_id":{"type":"string","pattern":"^[A-Za-z0-9-]{16,64}$"},"source":{"type":"string","maxLength":200}}},"Health":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"service":{"type":"string","const":"armage-api"},"version":{"type":"string"},"env":{"type":"string"},"db":{"type":"string","enum":["enabled","disabled"]}},"required":["ok","service","version","env","db"]},"Readiness":{"type":"object","properties":{"ready":{"type":"boolean"},"db":{"type":"string","enum":["disabled","ok","error"]},"db_latency_ms":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"read_path":{"type":"string","enum":["ok","error"]},"secrets":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string","enum":["absent","ok","invalid"]}},"version":{"type":"string"}},"required":["ready","db","version"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request","authentication_error","permission_error","unavailable","rate_limited","api_error"]},"code":{"type":"string"},"message":{"type":"string"},"request_id":{"type":"string"},"fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}}},"required":["type","code","message","request_id"]}},"required":["error"]},"Accepted":{"type":"object","properties":{"reference":{"type":"string"},"delivery":{"type":"string","const":"dry-run"}},"required":["reference"]},"TopicRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"]},"ContentPerson":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"role":{"type":["string","null"]}},"required":["name","slug","role"]},"PublicMediaAsset":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["image","video"]},"url":{"type":"string"},"content_type":{"type":"string"},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"duration_seconds":{"type":["number","null"]},"alt":{"type":"string"},"caption":{"type":["string","null"]},"credit":{"type":["string","null"]}},"required":["id","kind","url","content_type","width","height","duration_seconds","alt","caption","credit"]},"ContentBlock":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"paragraph"},"text":{"type":"string"}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","const":"heading"},"level":{"anyOf":[{"type":"number","const":2},{"type":"number","const":3}]},"text":{"type":"string"}},"required":["type","level","text"]},{"type":"object","properties":{"type":{"type":"string","const":"list"},"style":{"type":"string","enum":["bullet","number"]},"items":{"type":"array","items":{"type":"string"}}},"required":["type","style","items"]},{"type":"object","properties":{"type":{"type":"string","const":"quote"},"text":{"type":"string"},"attribution":{"type":"string"}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","const":"callout"},"title":{"type":"string"},"text":{"type":"string"}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","const":"image"},"asset_id":{"type":"string"}},"required":["type","asset_id"]},{"type":"object","properties":{"type":{"type":"string","const":"links"},"links":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"href":{"type":"string"},"note":{"type":"string"}},"required":["text","href"]}}},"required":["type","links"]},{"type":"object","properties":{"type":{"type":"string","const":"video_link"},"title":{"type":"string"},"href":{"type":"string"}},"required":["type","title","href"]}]},"ContentCard":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":["string","null"]},"published_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"featured":{"type":"boolean"},"featured_order":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"topics":{"type":"array","items":{"$ref":"#/components/schemas/TopicRef"}},"external_url":{"type":["string","null"]},"outlet_name":{"type":["string","null"]},"event":{"anyOf":[{"type":"object","properties":{"starts_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"ends_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"location":{"type":["string","null"]}},"required":["starts_at","ends_at","location"]},{"type":"null"}]},"video":{"anyOf":[{"type":"object","properties":{"url":{"type":"string"},"duration_seconds":{"type":["number","null"]}},"required":["url","duration_seconds"]},{"type":"null"}]},"cover":{"anyOf":[{"$ref":"#/components/schemas/PublicMediaAsset"},{"type":"null"}]},"author":{"anyOf":[{"$ref":"#/components/schemas/ContentPerson"},{"type":"null"}]}},"required":["id","type","slug","title","summary","published_at","featured","featured_order","topics","external_url","outlet_name","event","video","cover","author"]},"ContentList":{"type":"object","properties":{"featured":{"anyOf":[{"$ref":"#/components/schemas/ContentCard"},{"type":"null"}]},"highlights":{"type":"array","items":{"$ref":"#/components/schemas/ContentCard"}},"items":{"type":"array","items":{"$ref":"#/components/schemas/ContentCard"}},"next_cursor":{"type":["string","null"]},"types_available":{"type":"array","items":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]}},"topics_available":{"type":"array","items":{"$ref":"#/components/schemas/TopicRef"}}},"required":["featured","highlights","items","next_cursor","types_available","topics_available"]},"ContentItem":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":["string","null"]},"published_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"featured":{"type":"boolean"},"featured_order":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"topics":{"type":"array","items":{"$ref":"#/components/schemas/TopicRef"}},"external_url":{"type":["string","null"]},"outlet_name":{"type":["string","null"]},"event":{"anyOf":[{"type":"object","properties":{"starts_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"ends_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"location":{"type":["string","null"]}},"required":["starts_at","ends_at","location"]},{"type":"null"}]},"video":{"anyOf":[{"type":"object","properties":{"url":{"type":"string"},"duration_seconds":{"type":["number","null"]}},"required":["url","duration_seconds"]},{"type":"null"}]},"cover":{"anyOf":[{"$ref":"#/components/schemas/PublicMediaAsset"},{"type":"null"}]},"author":{"anyOf":[{"$ref":"#/components/schemas/ContentPerson"},{"type":"null"}]},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"body":{"type":"array","items":{"$ref":"#/components/schemas/ContentBlock"}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/PublicMediaAsset"}},"reviewer":{"anyOf":[{"$ref":"#/components/schemas/ContentPerson"},{"type":"null"}]},"related":{"type":"array","items":{"$ref":"#/components/schemas/ContentCard"}}},"required":["id","type","slug","title","summary","published_at","featured","featured_order","topics","external_url","outlet_name","event","video","cover","author","updated_at","body","assets","reviewer","related"]},"TopicPage":{"type":"object","properties":{"topic":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]}},"required":["slug","name","description"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/ContentCard"}},"next_cursor":{"type":["string","null"]}},"required":["topic","items","next_cursor"]},"SearchDocument":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"headings":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"body":{"type":"string"}},"required":["id","url","title","description","category","headings","keywords","body"]},"SearchDocuments":{"type":"object","properties":{"documents":{"type":"array","items":{"$ref":"#/components/schemas/SearchDocument"}}},"required":["documents"]},"SitemapEntry":{"type":"object","properties":{"url":{"type":"string"},"lastmod":{"type":"string"}},"required":["url","lastmod"]},"SitemapEntries":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/SitemapEntry"}}},"required":["entries"]},"NewsroomSite":{"type":"object","properties":{"cache_key":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"publisher":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":["string","null"]}},"required":["name","url"]},"desk":{"type":["string","null"]},"accent":{"type":["string","null"]},"logo":{"anyOf":[{"$ref":"#/components/schemas/PublicMediaAsset"},{"type":"null"}]},"live":{"type":"boolean"},"live_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"custom_hostname":{"type":["string","null"]},"canonical":{"type":"object","properties":{"origin":{"type":"string"},"base_path":{"type":"string"}},"required":["origin","base_path"]},"newsletter":{"description":"The organization's public subscribe endpoint while its newsletter is on (ADR 0069); null when it is off. Only on the site and host reads.","anyOf":[{"type":"object","properties":{"subscribe_url":{"type":"string"}},"required":["subscribe_url"]},{"type":"null"}]}},"required":["cache_key","slug","name","publisher","desk","accent","logo","live","live_at","custom_hostname","canonical"]},"Story":{"type":"object","properties":{"object":{"type":"string","const":"story"},"id":{"type":"string"},"type":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","ai_generated","review_required","approved","scheduled","publishing","published","failed","rejected","unpublished","archived"]},"title":{"type":["string","null"]},"summary":{"type":["string","null"]},"version":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"topics":{"type":"array","items":{"type":"string"}},"published_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["object","id","type","slug","status","title","summary","version","topics","published_at","created_at","updated_at"]},"StoryDetail":{"type":"object","properties":{"object":{"type":"string","const":"story"},"id":{"type":"string"},"type":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","ai_generated","review_required","approved","scheduled","publishing","published","failed","rejected","unpublished","archived"]},"title":{"type":["string","null"]},"summary":{"type":["string","null"]},"version":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"topics":{"type":"array","items":{"type":"string"}},"published_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"body":{"type":"array","items":{}},"approvals":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalDecision"}}},"required":["object","id","type","slug","status","title","summary","version","topics","published_at","created_at","updated_at","body","approvals"]},"StoryList":{"type":"object","properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Story"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}},"required":["object","data","has_more","next_cursor"]},"ApprovalDecision":{"type":"object","properties":{"object":{"type":"string","const":"approval_decision"},"id":{"type":"string"},"version":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"decision":{"type":"string","enum":["approved","rejected"]},"actor_type":{"type":"string"},"actor_role":{"type":"string"},"note":{"type":["string","null"]},"decided_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["object","id","version","decision","actor_type","actor_role","note","decided_at"]},"JsonFeed":{"type":"object","properties":{"version":{"type":"string","const":"https://jsonfeed.org/version/1.1"},"title":{"type":"string"},"feed_url":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"content_text":{"type":"string"},"content_html":{"type":"string"},"date_published":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"date_modified":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"tags":{"type":"array","items":{"type":"string"}},"_armage":{"type":"object","properties":{"slug":{"type":"string"},"type":{"type":"string","enum":["insight","press_release","announcement","in_the_news","event","video"]},"version":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["slug","type","version"]}},"required":["id","title","content_text","content_html","date_published","date_modified","tags","_armage"]}}},"required":["version","title","feed_url","items"]},"WebhookEndpoint":{"type":"object","properties":{"object":{"type":"string","const":"webhook_endpoint"},"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["story.submitted","story.approved","story.rejected","story.published","story.unpublished"]}},"status":{"type":"string","enum":["enabled","disabled"]},"disabled_reason":{"anyOf":[{"type":"string","enum":["failing","by_client","by_armage"]},{"type":"null"}]},"secret_hint":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"updated_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["object","id","url","events","status","disabled_reason","secret_hint","created_at","updated_at"]},"WebhookEndpointList":{"type":"object","properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}},"required":["object","data","has_more","next_cursor"]},"WebhookDelivery":{"type":"object","properties":{"object":{"type":"string","const":"webhook_delivery"},"id":{"type":"string"},"endpoint_id":{"type":"string"},"event_id":{"type":"string"},"event_type":{"type":"string","enum":["story.submitted","story.approved","story.rejected","story.published","story.unpublished"]},"story_id":{"type":["string","null"]},"status":{"type":"string","enum":["pending","delivered","failed"]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"last_status_code":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"last_error":{"type":["string","null"]},"next_attempt_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"delivered_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]}},"required":["object","id","endpoint_id","event_id","event_type","story_id","status","attempts","last_status_code","last_error","next_attempt_at","created_at","delivered_at"]},"WebhookDeliveryList":{"type":"object","properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}},"required":["object","data","has_more","next_cursor"]},"WebhookEvent":{"type":"object","properties":{"id":{"type":"string","description":"The event id; also the `webhook-id` header. Dedupe on it."},"object":{"type":"string","const":"event"},"type":{"type":"string","enum":["story.submitted","story.approved","story.rejected","story.published","story.unpublished"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"When the story changed."},"data":{"type":"object","properties":{"story_id":{"type":"string"},"story":{"anyOf":[{"$ref":"#/components/schemas/Story"},{"type":"null"}],"description":"The story as it reads when the delivery is made; null if it is gone."}},"required":["story_id","story"]}},"required":["id","object","type","created_at","data"]},"WebhookTestEvent":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","const":"event"},"type":{"type":"string","const":"webhook.test"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"data":{"type":"object","properties":{"endpoint_id":{"type":"string"}},"required":["endpoint_id"]}},"required":["id","object","type","created_at","data"]}}}}