docs/api/NewsService.openapi.json
Rama main · réplica de lectura del repositorio original.
{"components":{"schemas":{"BillingVerificationError":{"description":"Returned with HTTP 503 when paid access cannot be confirmed right now: the billing provider is re-verifying a recently expired subscription, or the entitlement backend is unreachable. Retryable — honor Retry-After.","properties":{"code":{"description":"Machine-readable billing-verification state, mirrored in the X-Billing-Verification response header.","enum":["renewal_verification_pending","renewal_verification_failed","entitlement_verification_unavailable"],"type":"string"},"error":{"description":"Human-readable billing-verification failure reason.","type":"string"},"requiredTier":{"description":"Minimum entitlement tier required for this endpoint, when the denial came from a tier gate.","format":"int32","type":"integer"}},"required":["error","code"],"type":"object"},"CategoriesEntry":{"properties":{"key":{"type":"string"},"value":{"$ref":"#/components/schemas/CategoryBucket"}},"type":"object"},"CategoryBucket":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NewsItem"},"type":"array"}},"type":"object"},"CategoryStatesEntry":{"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"},"ClaimHeadline":{"description":"ClaimHeadline is one fresh headline from a configured feed.","properties":{"link":{"description":"Article URL; empty when the ingest gate removed it.","type":"string"},"publishedAt":{"description":"Publication time in Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"source":{"description":"Registered feed name.","type":"string"},"title":{"type":"string"}},"type":"object"},"CountriesEntry":{"properties":{"key":{"type":"string"},"value":{"$ref":"#/components/schemas/CountryHeadlineBucket"}},"type":"object"},"CountryHeadline":{"description":"CountryHeadline is a dated article from a configured RSS feed, before dashboard category caps.","properties":{"link":{"description":"HTTPS article URL.","type":"string"},"publishedAt":{"description":"Publisher publication time in Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"source":{"description":"Publisher name: trusted origin publisher or registered feed name.","type":"string"},"title":{"description":"Headline that names the requested country.","type":"string"}},"type":"object"},"CountryHeadlineBucket":{"description":"CountryHeadlineBucket contains at most five distinct article URLs.","properties":{"items":{"items":{"$ref":"#/components/schemas/CountryHeadline"},"maxItems":5,"type":"array"}},"type":"object"},"DigestCoverage":{"description":"Content vs attempt state for one digest response (#7085). generated_at\n keeps the CONTENT identity (when the served items were produced);\n attempted_at keeps the latest BUILD identity — the two diverge when\n stale content is served after a failed or gate-held attempt (#7084).","properties":{"attemptedAt":{"description":"ISO 8601 time of the latest build attempt (attempt identity).","type":"string"},"categoryCompleted":{"format":"int32","type":"integer"},"categoryStates":{"additionalProperties":{"type":"string"},"description":"Per-category state: ok (a feed completed) | missing (none did).","type":"object"},"categoryTotal":{"description":"Configured categories, and how many had at least one completed feed.","format":"int32","type":"integer"},"droppedCategoryCap":{"format":"int32","type":"integer"},"droppedFeedCap":{"description":"Per-gate drop counts, named by gate position in the pipeline:\n feed cap, undated-item, freshness floor, category cap.","format":"int32","type":"integer"},"droppedFreshness":{"format":"int32","type":"integer"},"droppedUndated":{"format":"int32","type":"integer"},"feedCompleted":{"format":"int32","type":"integer"},"feedTotal":{"description":"Eligible feeds configured for this variant+language, and how many\n completed (with or without items).","format":"int32","type":"integer"},"itemsConsidered":{"description":"Items ranked before the category cap (served count on old replays).","format":"int32","type":"integer"},"itemsServed":{"description":"Items served in this response, after every gate.","format":"int32","type":"integer"},"publisherCount":{"description":"Distinct publishers among the served items (normalized, this cycle only).","format":"int32","type":"integer"},"publishersConsidered":{"description":"Distinct publishers among those ranked items.","format":"int32","type":"integer"},"servedStale":{"description":"True when accepted older content is replayed after a failed or gate-held\n attempt (#7084); counts still describe the served body.","type":"boolean"},"staleAgeSeconds":{"description":"Content age since acceptance, seconds (0 when fresh, at most 21600).","format":"int32","type":"integer"},"staleReason":{"description":"Closed stale reason: empty-rebuild | build-error | gate-held (empty when fresh).","type":"string"},"state":{"description":"complete | partial | stale | unavailable.\n complete: every configured category had at least one completed eligible\n feed and the build did not hit the global deadline.\n partial: content exists, but a category had no completed feed or the\n build hit the global deadline.\n stale: accepted older content is served after a failed or gate-held attempt\n (durable last-good serving, #7084).\n unavailable: no eligible content could be served.","type":"string"}},"type":"object"},"Error":{"description":"Error is returned when a handler encounters an error. It contains a simple error message that the developer can customize.","properties":{"message":{"description":"Error message (e.g., 'user not found', 'database connection failed')","type":"string"}},"type":"object"},"ErrorReport":{"description":"ErrorReport is one stored report as an operator sees it.","properties":{"contact":{"description":"Reply email, if given.","type":"string"},"createdAt":{"description":"Submission time, Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"id":{"description":"Report id.","type":"string"},"message":{"description":"Reader's message.","type":"string"},"publicNote":{"description":"Operator's public note, published with an accepted report.","type":"string"},"reviewedAt":{"description":"Review time, Unix epoch milliseconds; 0 while pending.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"status":{"description":"pending | accepted | rejected.","type":"string"},"storyId":{"description":"Story identity hash, if given.","type":"string"},"url":{"description":"URL, if given.","type":"string"}},"type":"object"},"FactCheck":{"description":"FactCheck is one rating an IFCN-verified fact-checker published about a similar claim, cited with attribution.","properties":{"claimReviewed":{"description":"The claim the fact-checker rated: similar to the claim searched, not necessarily the same.","type":"string"},"publisher":{"description":"The fact-checker's name as it publishes it.","type":"string"},"rating":{"description":"The fact-checker's own rating, verbatim. WorldMonitor does not rate claims.","type":"string"},"reviewedAt":{"description":"When the fact-check was published, ISO 8601.","type":"string"},"site":{"description":"The IFCN-allowlisted domain the link belongs to.","type":"string"},"title":{"description":"The fact-check's headline; empty when the feed gives none.","type":"string"},"url":{"description":"Link to the fact-check.","type":"string"}},"type":"object"},"FeedStatusesEntry":{"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"},"FieldViolation":{"description":"FieldViolation describes a single validation error for a specific field.","properties":{"description":{"description":"Human-readable description of the validation violation (e.g., 'must be a valid email address', 'required field missing')","type":"string"},"field":{"description":"The field path that failed validation (e.g., 'user.email' for nested fields). For header validation, this will be the header name (e.g., 'X-API-Key')","type":"string"}},"required":["field","description"],"type":"object"},"ForbiddenError":{"description":"Returned when a PRO-gated endpoint denies access because the caller has no resolved authenticated user, entitlements cannot be verified, or the caller lacks the required entitlement tier.","properties":{"code":{"description":"Machine-readable denial code, present when the 403 is a billing-provider-confirmed subscription lapse (mirrored in the X-Billing-Verification response header).","enum":["subscription_lapsed"],"type":"string"},"currentTier":{"description":"Caller entitlement tier when known.","format":"int32","type":"integer"},"error":{"description":"Human-readable entitlement failure reason.","type":"string"},"planKey":{"description":"Caller plan key when known.","type":"string"},"requiredTier":{"description":"Minimum entitlement tier required for this endpoint.","format":"int32","type":"integer"}},"required":["error"],"type":"object"},"GatewayError":{"description":"Returned by gateway infrastructure errors before an RPC handler runs, such as origin, routing, method, authentication, or quota checks.","properties":{"error":{"description":"Gateway error reason or structured gateway failure details.","oneOf":[{"type":"string"},{"additionalProperties":true,"type":"object"}]}},"required":["error"],"type":"object"},"GeoCoordinates":{"description":"GeoCoordinates represents a geographic location using WGS84 coordinates.","properties":{"latitude":{"description":"Latitude in decimal degrees (-90 to 90).","format":"double","maximum":90,"minimum":-90,"type":"number"},"longitude":{"description":"Longitude in decimal degrees (-180 to 180).","format":"double","maximum":180,"minimum":-180,"type":"number"}},"type":"object"},"GetSummarizeArticleCacheRequest":{"description":"GetSummarizeArticleCacheRequest looks up a pre-computed summary by cache key.","properties":{"cacheKey":{"description":"Deterministic cache key computed by buildSummaryCacheKey().","type":"string"}},"type":"object"},"InvalidRequestBodyError":{"description":"Returned when a JSON POST request body is empty or malformed.","properties":{"message":{"description":"Invalid request body","type":"string"}},"required":["message"],"type":"object"},"JmespathProjectionError":{"description":"Returned when a REST jmespath projection is invalid or exceeds the expression/output byte limits.","properties":{"_jmespath_error":{"description":"Projection error discriminator and details.","type":"string"},"original_keys":{"description":"Top-level keys or shape of the unprojected response.","items":{"type":"string"},"type":"array"}},"required":["_jmespath_error","original_keys"],"type":"object"},"LabelChange":{"description":"LabelChange records one story moving between single-publisher and corroborated\n coverage, or one correction a reviewer accepted from a reader report.","properties":{"at":{"description":"Time the change was recorded, Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"credibilityScore":{"description":"Story CRED score 0-100 when the change was recorded; -1 when unknown.","format":"int32","type":"integer"},"field":{"description":"corroboration (a label moved) | correction (an accepted reader report).","type":"string"},"link":{"description":"Representative article URL. Empty when it failed the publisher link gate,\n was revoked after the change was recorded, or the revocation set could not\n be read.","type":"string"},"next":{"description":"New state: single-publisher | corroborated; empty for a correction.","type":"string"},"nextPublishers":{"description":"Publisher families behind the new label; 0 for a correction.","format":"int32","type":"integer"},"previous":{"description":"Previous state: single-publisher | corroborated; empty for a correction.","type":"string"},"previousPublishers":{"description":"Publisher families behind the previous label; 0 for a correction.","format":"int32","type":"integer"},"reason":{"description":"Plain-language reason, in English.","type":"string"},"sources":{"items":{"$ref":"#/components/schemas/LabelChangePublisher"},"type":"array"},"storyId":{"description":"Story identity hash; empty for a correction that names no story.","type":"string"},"title":{"description":"Story headline when the change was recorded.","type":"string"},"trigger":{"description":"What recorded it: digest (automatic) | report (an accepted error report).","type":"string"}},"type":"object"},"LabelChangePublisher":{"description":"LabelChangePublisher is one publisher family behind the new label.","properties":{"name":{"description":"Curated publisher name, or the feed label when no family is curated.","type":"string"},"tier":{"description":"Declared source tier 1-4; 0 when no feed label declares one.","format":"int32","type":"integer"}},"type":"object"},"LabelCounts":{"description":"LabelCounts is how many stories in the latest full English digest carry each label.","properties":{"at":{"description":"When the digest counted, Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"corroborated":{"description":"Two or more publisher families, not all tier 4.","format":"int32","type":"integer"},"singlePublisher":{"description":"One publisher family.","format":"int32","type":"integer"},"stories":{"description":"Stories counted.","format":"int32","type":"integer"},"tier4Only":{"description":"Two or more families, every one a declared tier-4 outlet.","format":"int32","type":"integer"},"unknown":{"description":"No evidence to judge.","format":"int32","type":"integer"}},"type":"object"},"ListCountryHeadlinesRequest":{"description":"ListCountryHeadlinesRequest selects countries from the full English RSS pool.","properties":{"countryCodes":{"items":{"description":"ISO 3166-1 alpha-2 codes. Repeated query parameter, at most 250 countries.","type":"string"},"maxItems":250,"minItems":1,"type":"array"}},"required":["countryCodes"],"type":"object"},"ListCountryHeadlinesResponse":{"description":"ListCountryHeadlinesResponse describes cache availability independently of country matches.","properties":{"countries":{"additionalProperties":{"$ref":"#/components/schemas/CountryHeadlineBucket"},"description":"Requested country codes with matching headlines; absent countries had no match.","type":"object"},"feedCached":{"description":"Feeds whose parsed cache was available, including cached empty feeds.","format":"int32","type":"integer"},"feedTotal":{"description":"Configured full-variant feeds reachable from the English digest.","format":"int32","type":"integer"},"readAt":{"description":"Time the caches were read; article freshness is determined by published_at.","type":"string"},"state":{"description":"complete | partial | unavailable. A failed revocation read is unavailable.","type":"string"}},"type":"object"},"ListErrorReportsRequest":{"description":"ListErrorReportsRequest selects reports for review. Requires the X-Trust-Review-Key header.","properties":{"status":{"description":"pending (default) | reviewed.","maxLength":16,"type":"string"}},"type":"object"},"ListErrorReportsResponse":{"description":"ListErrorReportsResponse lists reports, oldest pending first or newest reviewed first.","properties":{"reports":{"items":{"$ref":"#/components/schemas/ErrorReport"},"type":"array"},"state":{"description":"ok | unavailable.","type":"string"}},"type":"object"},"ListFeedDigestRequest":{"properties":{"lang":{"description":"ISO 639-1 language code of a supported digest language (en, fr, ar, etc.). Unsupported codes are rejected with 400.","type":"string"},"variant":{"description":"Digest variant: full, tech, finance, happy, commodity, energy. Unsupported variants fall back to full.","type":"string"}},"type":"object"},"ListFeedDigestResponse":{"properties":{"categories":{"additionalProperties":{"$ref":"#/components/schemas/CategoryBucket"},"description":"Per-category buckets — keys match category names from feed config","type":"object"},"coverage":{"$ref":"#/components/schemas/DigestCoverage"},"feedStatuses":{"additionalProperties":{"type":"string"},"description":"Per-feed status — only non-ok states emitted; absent key implies ok.\n Values: empty (feed returned 0 items), timeout (timed out during fetch),\n all-undated (every parsed item lacked a usable date), partial-undated\n (some parsed items lacked a usable date).","type":"object"},"generatedAt":{"description":"ISO 8601 timestamp of when this digest was generated","type":"string"}},"type":"object"},"ListLabelChangesRequest":{"description":"ListLabelChangesRequest selects recent corroboration label changes, optionally for one story.","properties":{"limit":{"description":"Maximum changes returned, newest first. 0 uses the default of 50; at most 200.","format":"int32","maximum":200,"minimum":0,"type":"integer"},"storyId":{"description":"Story identity hash (64 hex characters). Empty lists changes across all stories.","maxLength":64,"type":"string"}},"type":"object"},"ListLabelChangesResponse":{"description":"ListLabelChangesResponse lists changes from the last seven days, newest first.","properties":{"changes":{"items":{"$ref":"#/components/schemas/LabelChange"},"type":"array"},"counts":{"$ref":"#/components/schemas/LabelCounts"},"retentionDays":{"description":"Retention window in days.","format":"int32","type":"integer"},"state":{"description":"ok | unavailable. Unavailable means the log could not be read, not that it is empty.","type":"string"}},"type":"object"},"ListMediaPerspectiveRequest":{"properties":{"audience":{"description":"domestic, international or diaspora.","type":"string"},"outletCountries":{"items":{"description":"ISO2, at most 30.","maxItems":30,"type":"string"},"maxItems":30,"type":"array"},"outletRegions":{"items":{"description":"UN M49 region slugs, e.g. northern-europe.","maxItems":5,"type":"string"},"maxItems":5,"type":"array"},"subjectCountry":{"description":"ISO2.","maxLength":2,"minLength":2,"type":"string"},"windowHours":{"description":"24, 48, 72 or 96.","enum":[0,24,48,72,96],"format":"int32","type":"integer"}},"required":["subjectCountry"],"type":"object"},"ListMediaPerspectiveResponse":{"properties":{"feedCached":{"format":"int32","type":"integer"},"feedTotal":{"format":"int32","type":"integer"},"groups":{"items":{"$ref":"#/components/schemas/MediaPerspectiveGroup"},"type":"array"},"noCoverageCountries":{"items":{"type":"string"},"type":"array"},"readAt":{"type":"string"},"state":{"description":"complete, partial or unavailable.","type":"string"},"subjectCountry":{"type":"string"},"unattributedCount":{"description":"No outlet profile or multi-publisher feed.","format":"int32","type":"integer"},"windowHours":{"format":"int32","type":"integer"}},"type":"object"},"ListNewsPoolRequest":{"properties":{"category":{"description":"One category key within the selected editions.","maxLength":32,"type":"string"},"editions":{"items":{"description":"Digest editions, e.g. full, tech, finance. Empty means full.","maxItems":8,"type":"string"},"maxItems":8,"type":"array"},"languages":{"items":{"description":"Content languages (ISO 639-1), at most 8. Empty means en.","maxItems":8,"type":"string"},"maxItems":8,"type":"array"}},"type":"object"},"ListNewsPoolResponse":{"properties":{"cells":{"items":{"$ref":"#/components/schemas/NewsPoolCell"},"type":"array"},"generatedAt":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/NewsPoolItem"},"type":"array"},"state":{"description":"complete, partial, stale or unavailable.","type":"string"}},"type":"object"},"ListSourceItemsRequest":{"properties":{"category":{"description":"One category key within the selected editions.","maxLength":32,"minLength":1,"type":"string"},"editions":{"items":{"description":"Digest editions to look for each source in, in order, e.g. full, tech.\n Empty means full.","maxItems":8,"type":"string"},"maxItems":8,"type":"array"},"lang":{"description":"ISO 639-1 language code of a supported digest language (en, fr, ar, etc.);\n empty means en. Unsupported codes are rejected with 400. Only sources this\n language's digest fetches are read.","maxLength":8,"type":"string"},"sources":{"items":{"description":"Catalog source names, at most 24, each at most 120 characters. Order and\n duplicates do not matter.","maxItems":24,"minItems":1,"type":"string"},"maxItems":24,"minItems":1,"type":"array"}},"required":["category","sources"],"type":"object"},"ListSourceItemsResponse":{"properties":{"generatedAt":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/NewsItem"},"maxItems":72,"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/SourceItemsState"},"maxItems":24,"type":"array"}},"type":"object"},"MediaPerspectiveFramingCounts":{"properties":{"critical":{"format":"int32","type":"integer"},"favorable":{"format":"int32","type":"integer"},"hostile":{"format":"int32","type":"integer"},"method":{"description":"e.g. llm-headline-stance.","type":"string"},"model":{"type":"string"},"neutral":{"format":"int32","type":"integer"},"promptVersion":{"type":"string"},"unlabeled":{"format":"int32","type":"integer"}},"type":"object"},"MediaPerspectiveGroup":{"properties":{"audience":{"type":"string"},"framing":{"$ref":"#/components/schemas/MediaPerspectiveFramingCounts"},"headlineCount":{"description":"Before the 20-headline cap.","format":"int32","type":"integer"},"headlines":{"items":{"$ref":"#/components/schemas/MediaPerspectiveHeadline"},"maxItems":20,"type":"array"},"homeCountry":{"type":"string"},"outletCount":{"description":"Publisher families.","format":"int32","type":"integer"}},"type":"object"},"MediaPerspectiveHeadline":{"properties":{"englishTitle":{"description":"Machine translation unless English; untrusted.","type":"string"},"framing":{"description":"favorable, neutral, critical or hostile.","type":"string"},"link":{"type":"string"},"outlet":{"type":"string"},"ownershipClass":{"type":"string"},"propagandaRisk":{"type":"string"},"publishedAt":{"description":"Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"publisherFamily":{"type":"string"},"stateAffiliated":{"type":"string"},"tier":{"format":"int32","type":"integer"},"title":{"type":"string"},"titleLanguage":{"type":"string"}},"type":"object"},"NewsItem":{"description":"NewsItem represents a single news article from RSS feed aggregation.","properties":{"corroborationCount":{"description":"Number of distinct sources that reported the same story in this digest\n cycle. \"Same story\" is determined by edit-tolerant story-identity\n clustering (headlines describing one event with different wording,\n ordering, source suffixes, truncation, or morphology corroborate each\n other) — not exact-title matching. See shared/story-identity.js.","format":"int32","type":"integer"},"credibilityScore":{"description":"Credibility score (0-100), distinct from importance_score. Measures\n source reliability / truthfulness, not newsworthiness. The base score\n uses source tier × 30% + propaganda risk × 50% + independent\n corroboration × 20%. High propaganda-risk sources (state-controlled\n media) are capped at 40 so a highly newsworthy RT/TASS story still\n scores low. Third-party bias/factuality ratings are reserved and not\n currently applied. See docs/methodology/news-credibility.mdx.","format":"int32","type":"integer"},"importanceScore":{"description":"Composite importance score. The base 0-100 score uses severity × 55% +\n source tier × 20% + corroboration × 15% + recency × 10%, then may add an\n 18-point diplomacy/flashpoint boost and 4 points per entity-level\n corroborating source, capped at five sources. The final score can exceed\n 100; with current boosts it is approximately capped at 138.","format":"int32","type":"integer"},"isAlert":{"description":"Whether this article triggered an alert condition.","type":"boolean"},"link":{"description":"Article URL.","type":"string"},"location":{"$ref":"#/components/schemas/GeoCoordinates"},"locationName":{"description":"Human-readable location name.","type":"string"},"publishedAt":{"description":"Publication time, as Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"snippet":{"description":"Cleaned article description from the RSS/Atom \u003cdescription\u003e /\n \u003ccontent:encoded\u003e / \u003csummary\u003e / \u003ccontent\u003e tag: HTML-stripped,\n entity-decoded, whitespace-normalised, clipped to 400 chars. Empty string\n when unavailable or indistinguishable from the headline — consumers must\n fall back to the headline for display/LLM grounding in that case.","type":"string"},"source":{"description":"Source feed name.","minLength":1,"type":"string"},"storyMeta":{"$ref":"#/components/schemas/StoryMeta"},"threat":{"$ref":"#/components/schemas/ThreatClassification"},"tickers":{"items":{"description":"Equity ticker symbols mentioned by the story, extracted at ingest from\n cashtags ($AAPL) and the curated company dictionary\n (shared/stocks.json). Uppercase symbols, deduplicated, capped at 8.\n Empty when no tracked ticker is mentioned. See shared/ticker-extract.js.","maxItems":8,"type":"string"},"maxItems":8,"type":"array"},"title":{"description":"Article headline.","minLength":1,"type":"string"}},"required":["source","title"],"type":"object"},"NewsPoolCell":{"properties":{"edition":{"type":"string"},"generatedAt":{"type":"string"},"items":{"description":"Items this cell contributed after the language and category filters.","format":"int32","type":"integer"},"language":{"type":"string"},"staleAgeSeconds":{"format":"int32","type":"integer"},"state":{"description":"English digest cell: complete or partial (a fresh build), stale (a\n last-good replay), or missing. Other languages: complete (every feed\n cached), partial (some), or missing (none).","type":"string"}},"type":"object"},"NewsPoolItem":{"properties":{"category":{"type":"string"},"editions":{"items":{"description":"Editions that carry this article, in catalog order.","type":"string"},"type":"array"},"item":{"$ref":"#/components/schemas/NewsItem"},"language":{"description":"ISO 639-1 content language of the outlet. Titles are untranslated.","type":"string"}},"type":"object"},"RateLimitError":{"description":"Returned when a gateway or handler rate limit rejects the request.","properties":{"error":{"description":"Human-readable rate-limit failure reason.","type":"string"}},"required":["error"],"type":"object"},"ReviewErrorReportRequest":{"description":"ReviewErrorReportRequest accepts or rejects one report. Requires the X-Trust-Review-Key header.","properties":{"decision":{"description":"accept | reject.","maxLength":16,"type":"string"},"publicNote":{"description":"Public note. Required to accept: it is what the label log publishes.","maxLength":500,"type":"string"},"reportId":{"description":"Report id.","maxLength":64,"minLength":1,"type":"string"}},"type":"object"},"ReviewErrorReportResponse":{"description":"ReviewErrorReportResponse returns the reviewed report.","properties":{"report":{"$ref":"#/components/schemas/ErrorReport"}},"type":"object"},"SearchClaimHeadlinesRequest":{"description":"SearchClaimHeadlinesRequest names the claim to search fresh headlines for.","properties":{"claim":{"description":"The claim or headline, up to 300 characters. Empty returns only the counts.","maxLength":300,"type":"string"}},"type":"object"},"SearchClaimHeadlinesResponse":{"description":"SearchClaimHeadlinesResponse holds the headlines that share the claim's words, for building its coverage card.","properties":{"feedCached":{"description":"Feeds whose parsed cache was available.","format":"int32","type":"integer"},"feedTotal":{"description":"Configured full-variant English feeds.","format":"int32","type":"integer"},"headlines":{"items":{"$ref":"#/components/schemas/ClaimHeadline"},"type":"array"},"readAt":{"description":"When the feed caches were read, ISO 8601.","type":"string"},"searchedFeeds":{"description":"Feeds those headlines came from.","format":"int32","type":"integer"},"searchedHeadlines":{"description":"Fresh headlines searched.","format":"int32","type":"integer"},"state":{"description":"complete | partial | unavailable. Unavailable searched nothing.","type":"string"}},"type":"object"},"SearchFactChecksRequest":{"description":"SearchFactChecksRequest names the claim to look up published fact-checks for.","properties":{"claim":{"description":"The claim or headline, up to 300 characters.","maxLength":300,"type":"string"}},"type":"object"},"SearchFactChecksResponse":{"description":"SearchFactChecksResponse lists published fact-checks of claims similar to the one searched.","properties":{"caveat":{"description":"Fixed reminder that ratings are the fact-checkers' own and never change the coverage result.","type":"string"},"factChecks":{"items":{"$ref":"#/components/schemas/FactCheck"},"type":"array"},"state":{"description":"ok | unavailable | not-configured. Only ok means the search ran; an empty list is then no published fact-check found.","type":"string"}},"type":"object"},"SourceItemsState":{"properties":{"edition":{"description":"Edition whose cached parse was read; empty unless found.","type":"string"},"items":{"format":"int32","type":"integer"},"source":{"type":"string"},"state":{"description":"found (a cached parse), missing (no healthy cached parse), or unplaced\n (no requested edition's digest in this language fetches the source in\n the category).","type":"string"}},"type":"object"},"StoryMeta":{"description":"StoryMeta carries cross-cycle persistence data attached to each news item.","properties":{"firstSeen":{"description":"Epoch ms when the story first appeared in any digest cycle.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"mentionCount":{"description":"Total number of digest cycles in which this story appeared.","format":"int32","type":"integer"},"phase":{"description":"StoryPhase represents the lifecycle stage of a tracked news story.","enum":["STORY_PHASE_UNSPECIFIED","STORY_PHASE_BREAKING","STORY_PHASE_DEVELOPING","STORY_PHASE_SUSTAINED","STORY_PHASE_FADING"],"type":"string"},"sourceCount":{"description":"Number of unique sources reporting this story in the CURRENT digest\n cycle; 1 for unclustered items.","format":"int32","type":"integer"}},"type":"object"},"SubmitErrorReportRequest":{"description":"SubmitErrorReportRequest is a reader's report that a WorldMonitor label or story is wrong. Stored for review; never applied automatically.","properties":{"contact":{"description":"Optional email for a reply. Never published.","maxLength":320,"type":"string"},"message":{"description":"What is wrong, in the reader's words.","maxLength":2000,"minLength":10,"type":"string"},"storyId":{"description":"Story identity hash the report concerns, when known (64 hex characters).","maxLength":64,"type":"string"},"url":{"description":"Article or WorldMonitor URL the report concerns, when known.","maxLength":2048,"type":"string"},"website":{"description":"Honeypot. Leave empty: a filled value is accepted and discarded.","type":"string"}},"type":"object"},"SubmitErrorReportResponse":{"description":"SubmitErrorReportResponse acknowledges a stored report.","properties":{"reportId":{"description":"Report id, empty unless received.","type":"string"},"status":{"description":"received | queue-full.","type":"string"}},"type":"object"},"SummarizeArticleRequest":{"description":"SummarizeArticleRequest specifies parameters for LLM article summarization.","properties":{"bodies":{"items":{"description":"Optional article bodies paired 1:1 with `headlines`. When bodies[i] is\n non-empty, the prompt interleaves it as grounding context under\n headlines[i]; when empty, behavior is identical to headline-only today.\n Callers may supply a shorter array; missing entries are treated as empty.\n Each body is subject to the same sanitisation as headlines before reaching\n the LLM prompt.","type":"string"},"type":"array"},"geoContext":{"description":"Geographic signal context to include in the prompt.","type":"string"},"headlines":{"items":{"description":"Headlines to summarize. Up to 10 raw headlines are used for request\n identity/cache matching; the LLM prompt uses up to 5 unique, non-empty\n sanitized headline/body pairs.","minItems":1,"type":"string"},"minItems":1,"type":"array"},"lang":{"description":"Output language code, default \"en\".","type":"string"},"mode":{"description":"Summarization mode: \"brief\", \"analysis\", \"translate\", \"\" (default).","type":"string"},"provider":{"description":"LLM provider: \"ollama\", \"openrouter\"","minLength":1,"type":"string"},"systemAppend":{"description":"Optional system prompt append for analytical framework instructions.","type":"string"},"variant":{"description":"Variant: \"full\", \"tech\", or target language for translate mode.","type":"string"}},"required":["provider"],"type":"object"},"SummarizeArticleResponse":{"description":"SummarizeArticleResponse contains the LLM summarization result.","properties":{"error":{"description":"Error message if the request failed.","type":"string"},"errorType":{"description":"Error type/name (e.g. \"TypeError\").","type":"string"},"fallback":{"description":"Whether the client should try the next provider in the fallback chain.","type":"boolean"},"model":{"description":"Model identifier used for generation.","type":"string"},"provider":{"description":"Provider that produced the result (or \"cache\").","type":"string"},"status":{"description":"SummarizeStatus indicates the outcome of a summarization request.","enum":["SUMMARIZE_STATUS_UNSPECIFIED","SUMMARIZE_STATUS_SUCCESS","SUMMARIZE_STATUS_CACHED","SUMMARIZE_STATUS_SKIPPED","SUMMARIZE_STATUS_ERROR"],"type":"string"},"statusDetail":{"description":"Human-readable detail for non-success statuses (skip reason, etc.).","type":"string"},"summary":{"description":"The generated summary text.","type":"string"},"tokens":{"description":"Token count from the LLM response.","format":"int32","type":"integer"}},"type":"object"},"ThreatClassification":{"description":"ThreatClassification represents an AI-assessed threat level for a news item.","properties":{"category":{"description":"Event category.","type":"string"},"confidence":{"description":"Confidence score (0.0 to 1.0).","format":"double","maximum":1,"minimum":0,"type":"number"},"level":{"description":"ThreatLevel represents the assessed threat level of a news event.","enum":["THREAT_LEVEL_UNSPECIFIED","THREAT_LEVEL_LOW","THREAT_LEVEL_MEDIUM","THREAT_LEVEL_HIGH","THREAT_LEVEL_CRITICAL"],"type":"string"},"source":{"description":"Classification source — \"keyword\", \"keyword-historical-downgrade\", or\n \"llm\".","type":"string"}},"type":"object"},"UnauthorizedError":{"description":"Returned when the API key is missing, malformed, or lacks current API access.","properties":{"error":{"description":"Human-readable error message.","type":"string"}},"required":["error"],"type":"object"},"ValidationError":{"description":"ValidationError is returned when request validation fails. It contains a list of field violations describing what went wrong.","properties":{"violations":{"description":"List of validation violations","items":{"$ref":"#/components/schemas/FieldViolation"},"type":"array"}},"required":["violations"],"type":"object"}},"securitySchemes":{"ApiKeyHeader":{"description":"Alias header for the WorldMonitor API key (X-WorldMonitor-Key).","in":"header","name":"X-Api-Key","type":"apiKey"},"BearerAuth":{"description":"Bearer token: a Clerk-issued JWT for browser session flows, passed as Authorization: Bearer \u003ctoken\u003e.","scheme":"bearer","type":"http"},"TrustReviewKey":{"description":"Operator review key (the server TRUST_REVIEW_SECRET). Required by the error-report review operations; a WorldMonitor API key does not authorize them.","in":"header","name":"X-Trust-Review-Key","type":"apiKey"},"WorldMonitorKey":{"description":"User-issued WorldMonitor API key.","in":"header","name":"X-WorldMonitor-Key","type":"apiKey"}}},"info":{"title":"NewsService API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/api/news/v1/list-country-headlines":{"get":{"description":"ListCountryHeadlines reads fresh country reporting from existing RSS caches without fetching publishers or applying dashboard category caps.","operationId":"ListCountryHeadlines","parameters":[{"description":"ISO 3166-1 alpha-2 codes. Repeated query parameter, at most 250 countries.","example":["US"],"explode":true,"in":"query","name":"country_codes","required":true,"schema":{"items":{"type":"string"},"maxItems":250,"minItems":1,"type":"array"},"style":"form"},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"countries":{"exampleKey":{"items":[{"link":"https://example.com/worldmonitor","publishedAt":1717200000000,"source":"example","title":"example"}]}},"feedCached":1,"feedTotal":1,"readAt":"2026-01-15T12:00:00Z","state":"example"},"schema":{"$ref":"#/components/schemas/ListCountryHeadlinesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"API access requires an active subscription (the API key's subscription is inactive or expired).","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"summary":"ListCountryHeadlines","tags":["NewsService"]}},"/api/news/v1/list-error-reports":{"get":{"description":"ListErrorReports lists stored reports for review. Operators only.\n Requires the X-Trust-Review-Key header (the server's TRUST_REVIEW_SECRET), not an API key; without it the response is 401.","operationId":"ListErrorReports","parameters":[{"description":"pending (default) | reviewed.","example":"example","in":"query","name":"status","required":false,"schema":{"type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"reports":[{"contact":"example","createdAt":1717200000000,"id":"example-id","message":"Example WorldMonitor observation.","publicNote":"example"}],"state":"example"},"schema":{"$ref":"#/components/schemas/ListErrorReportsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid X-Trust-Review-Key, or review is not configured on the server."},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[{"TrustReviewKey":[]}],"summary":"ListErrorReports","tags":["NewsService"]}},"/api/news/v1/list-feed-digest":{"get":{"description":"ListFeedDigest returns RSS headlines grouped by category for a site variant (full, tech, finance, happy, commodity, energy) and language, with coverage state.","operationId":"ListFeedDigest","parameters":[{"description":"Digest variant: full, tech, finance, happy, commodity, energy. Unsupported variants fall back to full.","example":"example","in":"query","name":"variant","required":false,"schema":{"type":"string"}},{"description":"ISO 639-1 language code of a supported digest language (en, fr, ar, etc.). Unsupported codes are rejected with 400.","example":"en","in":"query","name":"lang","required":false,"schema":{"type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"categories":{"exampleKey":{"items":[{"corroborationCount":42,"credibilityScore":42,"importanceScore":42,"isAlert":true,"link":"https://example.com/worldmonitor","source":"example","title":"example"}]}},"coverage":{"attemptedAt":"2026-01-15T12:00:00Z","categoryCompleted":1,"categoryStates":{"exampleKey":"example"},"categoryTotal":1,"droppedCategoryCap":1},"feedStatuses":{"exampleKey":"example"},"generatedAt":"2026-01-15T12:00:00Z"},"schema":{"$ref":"#/components/schemas/ListFeedDigestResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"API access requires an active subscription (the API key's subscription is inactive or expired).","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"summary":"ListFeedDigest","tags":["NewsService"]}},"/api/news/v1/list-label-changes":{"get":{"description":"ListLabelChanges returns stories whose corroboration label moved between single-publisher and corroborated in the last seven days, with the publishers behind each change.","operationId":"ListLabelChanges","parameters":[{"description":"Story identity hash (64 hex characters). Empty lists changes across all stories.","example":"example-id","in":"query","name":"story_id","required":false,"schema":{"type":"string"}},{"description":"Maximum changes returned, newest first. 0 uses the default of 50; at most 200.","example":25,"in":"query","name":"limit","required":false,"schema":{"format":"int32","type":"integer"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"changes":[{"at":1767182400000,"credibilityScore":72,"field":"corroboration","link":"https://example.com/news/synthetic-example","next":"corroborated","nextPublishers":2,"previous":"single-publisher","previousPublishers":1,"reason":"2 publisher families now carry this story (was 1).","sources":[{"name":"Example Wire (synthetic)","tier":1},{"name":"Example Daily (synthetic)","tier":2}],"storyId":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","title":"Synthetic example story","trigger":"digest"}],"counts":{"at":1767182400000,"corroborated":64,"singlePublisher":51,"stories":120,"tier4Only":3,"unknown":2},"retentionDays":7,"state":"ok"},"schema":{"$ref":"#/components/schemas/ListLabelChangesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[],"summary":"ListLabelChanges","tags":["NewsService"]}},"/api/news/v1/list-media-perspective":{"get":{"description":"ListMediaPerspective groups headlines about one country by outlet home country and audience. PRO-gated. Requires entitlement tier \u003e= 1.","operationId":"ListMediaPerspective","parameters":[{"description":"ISO2.","example":"SA","in":"query","name":"subject_country","required":true,"schema":{"type":"string"}},{"description":"ISO2, at most 30.","example":["US"],"explode":true,"in":"query","name":"outlet_countries","required":false,"schema":{"items":{"type":"string"},"type":"array"},"style":"form"},{"description":"domestic, international or diaspora.","example":"domestic","in":"query","name":"audience","required":false,"schema":{"type":"string"}},{"description":"24, 48, 72 or 96.","example":0,"in":"query","name":"window_hours","required":false,"schema":{"enum":[0,24,48,72,96],"format":"int32","type":"integer"}},{"description":"UN M49 region slugs, e.g. northern-europe.","example":["northern-europe"],"explode":true,"in":"query","name":"outlet_regions","required":false,"schema":{"items":{"type":"string"},"type":"array"},"style":"form"},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"feedCached":812,"feedTotal":812,"groups":[{"audience":"international","framing":{"critical":0,"favorable":0,"hostile":0,"method":"","model":"","neutral":0,"promptVersion":"","unlabeled":1},"headlineCount":1,"headlines":[{"englishTitle":"Saudi Arabia hosts ceasefire talks","framing":"","link":"https://www.bbc.co.uk/news/articles/example","outlet":"BBC World","ownershipClass":"public-service","propagandaRisk":"low","publishedAt":1717200000000,"publisherFamily":"bbc","stateAffiliated":"","tier":1,"title":"Saudi Arabia hosts ceasefire talks","titleLanguage":"en"}],"homeCountry":"GB","outletCount":1}],"noCoverageCountries":[],"readAt":"2026-10-27T12:00:00.000Z","state":"complete","subjectCountry":"SA","unattributedCount":0,"windowHours":72},"schema":{"$ref":"#/components/schemas/ListMediaPerspectiveResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"PRO entitlement access denied.","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[{"WorldMonitorKey":[]},{"ApiKeyHeader":[]},{"BearerAuth":[]}],"summary":"ListMediaPerspective","tags":["NewsService"]}},"/api/news/v1/list-news-pool":{"get":{"description":"ListNewsPool merges cached news for several editions and content languages, one item per article URL: English from the warmed digest cells, other languages from their feeds' cached parses. Reads cache only; never builds a digest or fetches a feed. PRO-gated. Requires entitlement tier \u003e= 1.","operationId":"ListNewsPool","parameters":[{"description":"Content languages (ISO 639-1), at most 8. Empty means en.","example":["fr"],"explode":true,"in":"query","name":"languages","required":false,"schema":{"items":{"type":"string"},"maxItems":8,"type":"array"},"style":"form"},{"description":"Digest editions, e.g. full, tech, finance. Empty means full.","example":["full"],"explode":true,"in":"query","name":"editions","required":false,"schema":{"items":{"type":"string"},"maxItems":8,"type":"array"},"style":"form"},{"description":"One category key within the selected editions.","example":"us","in":"query","name":"category","required":false,"schema":{"maxLength":32,"type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"cells":[{"edition":"full","items":1,"language":"fr","state":"complete"}],"generatedAt":"2026-01-15T12:00:00Z","items":[{"category":"us","editions":["full"],"item":{"corroborationCount":42,"credibilityScore":42,"importanceScore":42,"isAlert":true,"link":"https://example.com/worldmonitor","source":"example","title":"example"},"language":"fr"}],"state":"complete"},"schema":{"$ref":"#/components/schemas/ListNewsPoolResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"PRO entitlement access denied.","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[{"WorldMonitorKey":[]},{"ApiKeyHeader":[]},{"BearerAuth":[]}],"summary":"ListNewsPool","tags":["NewsService"]}},"/api/news/v1/list-source-items":{"get":{"description":"ListSourceItems returns the newest cached items of named sources in one category, for a dashboard panel whose enabled sources fell below the digest's per-category cap. Reads cached feed parses only; never fetches a feed.","operationId":"ListSourceItems","parameters":[{"description":"Digest editions to look for each source in, in order, e.g. full, tech.\n Empty means full.","example":["full"],"explode":true,"in":"query","name":"editions","required":false,"schema":{"items":{"type":"string"},"maxItems":8,"type":"array"},"style":"form"},{"description":"One category key within the selected editions.","example":"europe","in":"query","name":"category","required":true,"schema":{"maxLength":32,"minLength":1,"type":"string"}},{"description":"Catalog source names, at most 24, each at most 120 characters. Order and\n duplicates do not matter.","example":["France 24"],"explode":true,"in":"query","name":"sources","required":true,"schema":{"items":{"maxLength":120,"type":"string"},"maxItems":24,"minItems":1,"type":"array"},"style":"form"},{"description":"ISO 639-1 language code of a supported digest language (en, fr, ar, etc.);\n empty means en. Unsupported codes are rejected with 400. Only sources this\n language's digest fetches are read.","example":"en","in":"query","name":"lang","required":false,"schema":{"maxLength":8,"type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"generatedAt":"2026-01-15T12:00:00Z","items":[{"corroborationCount":42,"credibilityScore":42,"importanceScore":42,"isAlert":true,"link":"https://example.com/worldmonitor","source":"France 24","title":"example"}],"sources":[{"edition":"full","items":1,"source":"France 24","state":"found"}]},"schema":{"$ref":"#/components/schemas/ListSourceItemsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"API access requires an active subscription (the API key's subscription is inactive or expired).","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"summary":"ListSourceItems","tags":["NewsService"]}},"/api/news/v1/review-error-report":{"post":{"description":"ReviewErrorReport accepts or rejects a report. An accepted report's public note is added to the label log. Operators only.\n Requires the X-Trust-Review-Key header (the server's TRUST_REVIEW_SECRET), not an API key; without it the response is 401.","operationId":"ReviewErrorReport","requestBody":{"content":{"application/json":{"example":{"decision":"example","publicNote":"example","reportId":"example-id"},"schema":{"$ref":"#/components/schemas/ReviewErrorReportRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"report":{"contact":"example","createdAt":1717200000000,"id":"example-id","message":"Example WorldMonitor observation.","publicNote":"example"}},"schema":{"$ref":"#/components/schemas/ReviewErrorReportResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/InvalidRequestBodyError"}]}}},"description":"Validation error or malformed JSON request body"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid X-Trust-Review-Key, or review is not configured on the server."},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[{"TrustReviewKey":[]}],"summary":"ReviewErrorReport","tags":["NewsService"]}},"/api/news/v1/search-claim-headlines":{"get":{"description":"SearchClaimHeadlines returns the fresh headlines from every configured feed that share a claim's words, so the /verify page and verify_claim can build its coverage card.","operationId":"SearchClaimHeadlines","parameters":[{"description":"The claim or headline, up to 300 characters. Empty returns only the counts.","example":"example","in":"query","name":"claim","required":false,"schema":{"type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"feedCached":241,"feedTotal":241,"headlines":[{"link":"https://example.com/news/synthetic-example","publishedAt":1768478400000,"source":"Example Feed","title":"Synthetic example headline sharing the claim's words"}],"readAt":"2026-01-15T12:00:00.000Z","searchedFeeds":216,"searchedHeadlines":4140,"state":"complete"},"schema":{"$ref":"#/components/schemas/SearchClaimHeadlinesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[],"summary":"SearchClaimHeadlines","tags":["NewsService"]}},"/api/news/v1/search-fact-checks":{"get":{"description":"SearchFactChecks returns ratings that IFCN-verified fact-checkers published about claims similar to the one given, for citation with attribution.","operationId":"SearchFactChecks","parameters":[{"description":"The claim or headline, up to 300 characters.","example":"example","in":"query","name":"claim","required":false,"schema":{"type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"caveat":"Ratings are the fact-checkers' own, quoted with attribution from IFCN-verified signatories. WorldMonitor does not rate claims, and a rating never changes the coverage result above.","factChecks":[{"claimReviewed":"A synthetic example claim, similar to the claim checked","publisher":"Example Fact-Checker (synthetic)","rating":"Example rating","reviewedAt":"2026-01-15T12:00:00.000Z","site":"example.com","title":"Synthetic example fact-check","url":"https://example.com/fact-check/synthetic-example"}],"state":"ok"},"schema":{"$ref":"#/components/schemas/SearchFactChecksResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[],"summary":"SearchFactChecks","tags":["NewsService"]}},"/api/news/v1/submit-error-report":{"post":{"description":"SubmitErrorReport stores a reader's report that a label or story is wrong, for an operator to review. No account required; rate-limited; never applied automatically.","operationId":"SubmitErrorReport","parameters":[{"description":"Optional client-generated idempotency key. Retrying a POST with the same key and an identical request body replays the original response (only the status, body, and Content-Type are reproduced) instead of re-executing; reusing the key with a different body is rejected with 422. For mutations this avoids duplicating the side effect, while for batch-read POSTs it replays a cached snapshot that can be up to 24 hours stale. Keys are scoped per authenticated caller (falling back to the source IP for unauthenticated endpoints) and retained for 24 hours.","example":"4f8b9c2e-1a3d-4b6f-8e0a-2c5d7f9b1e34","in":"header","name":"Idempotency-Key","required":false,"schema":{"maxLength":255,"minLength":1,"pattern":"^[\\x21-\\x7E]{1,255}$","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"contact":"example","message":"Example WorldMonitor observation.","storyId":"example-id","url":"https://example.com/worldmonitor"},"schema":{"$ref":"#/components/schemas/SubmitErrorReportRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"reportId":"example-id","status":"example"},"schema":{"$ref":"#/components/schemas/SubmitErrorReportResponse"}}},"description":"Successful response","headers":{"Idempotency-Key":{"description":"The idempotency key echoed from the request. Present only when the client opted into idempotency.","schema":{"type":"string"}},"Idempotent-Replayed":{"description":"true when this response was replayed from an earlier request with the same key, false on the first (original) request. Present only when the client opted into idempotency.","schema":{"type":"boolean"}}}},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"type":"object"},{"$ref":"#/components/schemas/InvalidRequestBodyError"}]}}},"description":"Validation error, invalid Idempotency-Key header, or malformed JSON request body"},"409":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"type":"object"}}},"description":"A request with this Idempotency-Key is still being processed","headers":{"Idempotency-Key":{"description":"The idempotency key supplied by the client.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before retrying the in-flight request.","schema":{"type":"string"}}}},"422":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"type":"object"}}},"description":"The Idempotency-Key was already used with a different request body","headers":{"Idempotency-Key":{"description":"The idempotency key supplied by the client.","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"security":[],"summary":"SubmitErrorReport","tags":["NewsService"]}},"/api/news/v1/summarize-article":{"post":{"description":"Generate an article summary with language-model provider selection and fallback support, returning the chosen provider, model and reported processing errors.","operationId":"SummarizeArticle","parameters":[{"description":"Optional client-generated idempotency key. Retrying a POST with the same key and an identical request body replays the original response (only the status, body, and Content-Type are reproduced) instead of re-executing; reusing the key with a different body is rejected with 422. For mutations this avoids duplicating the side effect, while for batch-read POSTs it replays a cached snapshot that can be up to 24 hours stale. Keys are scoped per authenticated caller (falling back to the source IP for unauthenticated endpoints) and retained for 24 hours.","example":"4f8b9c2e-1a3d-4b6f-8e0a-2c5d7f9b1e34","in":"header","name":"Idempotency-Key","required":false,"schema":{"maxLength":255,"minLength":1,"pattern":"^[\\x21-\\x7E]{1,255}$","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"bodies":["example"],"geoContext":"example","headlines":["example"],"lang":"en","mode":"brief","provider":"openrouter"},"schema":{"$ref":"#/components/schemas/SummarizeArticleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"error":"example","errorType":"all","fallback":true,"model":"example","provider":"openrouter"},"schema":{"$ref":"#/components/schemas/SummarizeArticleResponse"}}},"description":"Successful response","headers":{"Idempotency-Key":{"description":"The idempotency key echoed from the request. Present only when the client opted into idempotency.","schema":{"type":"string"}},"Idempotent-Replayed":{"description":"true when this response was replayed from an earlier request with the same key, false on the first (original) request. Present only when the client opted into idempotency.","schema":{"type":"boolean"}}}},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"type":"object"},{"$ref":"#/components/schemas/InvalidRequestBodyError"}]}}},"description":"Validation error, invalid Idempotency-Key header, or malformed JSON request body"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"API access requires an active subscription (the API key's subscription is inactive or expired).","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"409":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"type":"object"}}},"description":"A request with this Idempotency-Key is still being processed","headers":{"Idempotency-Key":{"description":"The idempotency key supplied by the client.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before retrying the in-flight request.","schema":{"type":"string"}}}},"422":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error","message"],"type":"object"}}},"description":"The Idempotency-Key was already used with a different request body","headers":{"Idempotency-Key":{"description":"The idempotency key supplied by the client.","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"summary":"SummarizeArticle","tags":["NewsService"]}},"/api/news/v1/summarize-article-cache":{"get":{"description":"GetSummarizeArticleCache returns an AI summary already stored by SummarizeArticle for a deterministic cache key. Hits are CDN-cacheable; misses are not.","operationId":"GetSummarizeArticleCache","parameters":[{"description":"Deterministic cache key computed by buildSummaryCacheKey().","example":"summary:v1:example-cache","in":"query","name":"cache_key","required":false,"schema":{"pattern":"^summary:v\\d+:[a-z0-9:_-]{3,120}$","type":"string"}},{"description":"Optional JMESPath expression applied server-side to project or reduce the JSON response before it is returned (mirrors the MCP jmespath argument). Invalid expressions, expressions larger than 1024 UTF-8 bytes, or projections that exceed the 256 KB output cap return HTTP 400 with a {_jmespath_error, original_keys} envelope. Grammar and worked examples: https://www.worldmonitor.app/docs/mcp-jmespath.","example":"keys(@)","in":"query","name":"jmespath","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"error":"example","errorType":"all","fallback":true,"model":"example","provider":"openrouter"},"schema":{"$ref":"#/components/schemas/SummarizeArticleResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationError"},{"$ref":"#/components/schemas/JmespathProjectionError"}]}}},"description":"Validation error"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedError"}}},"description":"Missing or invalid API key."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}},"description":"API access requires an active subscription (the API key's subscription is inactive or expired).","headers":{"X-Billing-Verification":{"description":"Present when the 403 is a billing-provider-confirmed subscription lapse (value subscription_lapsed, matching the body `code`).","schema":{"type":"string"}}}},"429":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/RateLimitError"}]}}},"description":"Rate limit exceeded.","headers":{"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the active rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix epoch milliseconds when the active rate-limit window resets.","schema":{"type":"string"}}}},"503":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingVerificationError"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Service unavailable. Billing-verification responses include code and X-Billing-Verification; other gateway infrastructure failures use the generic GatewayError shape.","headers":{"Retry-After":{"description":"Seconds to wait before retrying (1-60).","schema":{"type":"string"}},"X-Billing-Verification":{"description":"Billing-verification state that produced this response (matches the body `code`).","schema":{"type":"string"}},"X-RateLimit-Mode":{"description":"Present with value degraded when a fail-closed rate-limit dependency is unavailable.","schema":{"type":"string"}},"X-Validation-Mode":{"description":"Present with value degraded when user API-key validation is temporarily unavailable.","schema":{"type":"string"}}}},"default":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/GatewayError"}]}}},"description":"Gateway or handler error response."}},"summary":"GetSummarizeArticleCache","tags":["NewsService"]}}},"security":[{"WorldMonitorKey":[]},{"ApiKeyHeader":[]}],"servers":[{"url":"https://api.worldmonitor.app"}]}