{"openapi":"3.1.0","info":{"title":"News Factory API","version":"1.0.0","summary":"Find niche news, write stories with AI, check quality and SEO, publish.","x-summary":"REST API for News Factory: stories, content sources, RSS feeds, AI writing, quality and SEO checks, and workflows, scoped by API key.","description":"The News Factory public REST API: find news in your niche, turn it into stories with AI, check them for quality and SEO, and publish them to your site. Everything the app does with stories, content sources, RSS feeds and workflows, scoped to one organization and one Topic Workspace.\n\n**Authentication.** Send `Authorization: Bearer <API key>` with every request. An organization admin creates keys in the app under Organization settings, API keys (Pro, Business and Enterprise plans), choosing the scopes each integration needs and, optionally, one Topic Workspace and an expiry. Keys start with `nfk_` and are shown once.\n\n**Start with `GET /me`.** It returns the organization, the key's scopes, the Topic Workspaces the key can act on and anything that would block a request.\n\n**Errors** are always `{ \"error\": { \"code\", \"message\", \"details\", \"requestId\", \"docs\" } }`; `docs` links to what the code means and what to do.\n\n**Limits.** 100 requests per minute per organization (429 `RATE_LIMIT_EXCEEDED` with `Retry-After`). AI endpoints spend the organization's AI credits (429 `LLM_QUOTA_EXCEEDED` when they run out).\n\n**Locale codes** are always full codes such as `en-US`, `pt-BR` or `es-CO`, never bare `en`.","contact":{"name":"News Factory support","email":"support@news-factory.app","url":"https://news-factory.app"},"termsOfService":"https://news-factory.app/terms"},"externalDocs":{"description":"Guides and the full reference","url":"https://client-api.news-factory.app/docs"},"servers":[{"url":"https://client-api.news-factory.app/v1","description":"News Factory public API"}],"security":[{"apiKey":[]}],"tags":[{"name":"Account","description":"Who the credential is and what it may do. Start here."},{"name":"Stories","description":"Read, create and publish the stories News Factory writes, with their images, translations, quality checks and SEO audits"},{"name":"Content Sources","description":"Articles, web pages, documents and images that stories are written from"},{"name":"RSS Feeds","description":"Subscribe to catalogue feeds and read their articles with the topic's evaluation"},{"name":"Workflows","description":"Run the automation workflows designed in the app and follow their runs step by step"}],"paths":{"/me":{"get":{"operationId":"getMe","summary":"Who Am I","description":"Describe the credential: the organization, the plan, the key's scopes, the Topic Workspaces it can act on, the rate limit and where the documentation lives. It answers even when the plan or the Topic Workspace would block every other endpoint, and lists the problem in `problems`, so call it first.","tags":["Account"],"parameters":[{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The credential and what it may do","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"The description","properties":{"credential":{"type":"object","properties":{"type":{"type":"string","description":"`api-key`, `legacy-api-key` (a key from before scoped keys, full access, replace it) or `session` (the in-app playground)"},"keyId":{"type":["string","null"],"description":"Id of the API key"},"name":{"type":["string","null"],"description":"Name given to the key in the app"},"scopes":{"type":"array","items":{"type":"object"},"description":"Granted scopes: `{ scope, description }`"},"expiresAt":{"type":["string","null"],"description":"When the key stops working (ISO 8601), or null"},"deprecated":{"type":"boolean","description":"True for a legacy key"}}},"organization":{"type":"object","properties":{"id":{"type":"string","description":"Organization id"},"plan":{"type":"string","description":"Plan slug: starter, pro, business, enterprise or free_org"},"apiIncluded":{"type":"boolean","description":"Whether the plan includes the API (Pro, Business, Enterprise)"}}},"topic":{"type":"object","properties":{"current":{"type":["string","null"],"description":"Topic Workspace this request resolved to (honours X-Topic-Key)"},"boundTo":{"type":["string","null"],"description":"Topic Workspace the key is bound to, or null when it may use any"},"available":{"type":"array","items":{"type":"object"},"description":"Topic Workspaces the key can act on: `{ key, name }`"},"howToChoose":{"type":"string","description":"How to pick a Topic Workspace with this key"}}},"limits":{"type":"object","properties":{"requestsPerMinute":{"type":"number","description":"Per-organization rate limit"},"dailyRequests":{"type":["number","null"],"description":"Daily request quota, or null when the plan sets none"}}},"problems":{"type":"array","items":{"type":"object"},"description":"Anything that blocks or will block requests: `{ code, message }` (PLAN_REQUIRED, INVALID_TOPIC, NO_TOPIC_WORKSPACE, LEGACY_API_KEY)"},"docs":{"type":"object","properties":{"reference":{"type":"string","description":"HTML reference"},"openapi":{"type":"string","description":"OpenAPI 3.1 specification"},"llms":{"type":"string","description":"llms.txt index for AI agents"},"llmsFull":{"type":"string","description":"The whole reference as one Markdown file"}}}}}}},"example":{"data":{"credential":{"type":"api-key","keyId":"68f0c1a2b3c4d5e6f7a8b9c0","name":"Website sync","scopes":[{"scope":"stories:read","description":"Read stories, their media, quality-check and SEO audit history"},{"scope":"feeds:read","description":"Read followed feeds, the feed catalogue, feed articles and the scheduler status"}],"expiresAt":null,"deprecated":false},"organization":{"id":"org_2abc123","plan":"pro","apiIncluded":true},"topic":{"current":"tech","boundTo":"tech","available":[{"key":"tech","name":"Technology"}],"howToChoose":"This key always acts on its Topic Workspace; X-Topic-Key is not needed."},"limits":{"requestsPerMinute":100,"dailyRequests":null},"problems":[],"docs":{"reference":"https://client-api.news-factory.app/docs","openapi":"https://client-api.news-factory.app/openapi.json","llms":"https://client-api.news-factory.app/llms.txt","llmsFull":"https://client-api.news-factory.app/llms-full.txt"}}}}},"x-fields":[{"name":"data","type":"object","description":"The description"},{"name":"data.credential.type","type":"string","description":"`api-key`, `legacy-api-key` (a key from before scoped keys, full access, replace it) or `session` (the in-app playground)"},{"name":"data.credential.keyId","type":"string | null","description":"Id of the API key"},{"name":"data.credential.name","type":"string | null","description":"Name given to the key in the app"},{"name":"data.credential.scopes","type":"object[]","description":"Granted scopes: `{ scope, description }`"},{"name":"data.credential.expiresAt","type":"string | null","description":"When the key stops working (ISO 8601), or null"},{"name":"data.credential.deprecated","type":"boolean","description":"True for a legacy key"},{"name":"data.organization.id","type":"string","description":"Organization id"},{"name":"data.organization.plan","type":"string","description":"Plan slug: starter, pro, business, enterprise or free_org"},{"name":"data.organization.apiIncluded","type":"boolean","description":"Whether the plan includes the API (Pro, Business, Enterprise)"},{"name":"data.topic.current","type":"string | null","description":"Topic Workspace this request resolved to (honours X-Topic-Key)"},{"name":"data.topic.boundTo","type":"string | null","description":"Topic Workspace the key is bound to, or null when it may use any"},{"name":"data.topic.available","type":"object[]","description":"Topic Workspaces the key can act on: `{ key, name }`"},{"name":"data.topic.howToChoose","type":"string","description":"How to pick a Topic Workspace with this key"},{"name":"data.limits.requestsPerMinute","type":"number","description":"Per-organization rate limit"},{"name":"data.limits.dailyRequests","type":"number | null","description":"Daily request quota, or null when the plan sets none"},{"name":"data.problems","type":"object[]","description":"Anything that blocks or will block requests: `{ code, message }` (PLAN_REQUIRED, INVALID_TOPIC, NO_TOPIC_WORKSPACE, LEGACY_API_KEY)"},{"name":"data.docs.reference","type":"string","description":"HTML reference"},{"name":"data.docs.openapi","type":"string","description":"OpenAPI 3.1 specification"},{"name":"data.docs.llms","type":"string","description":"llms.txt index for AI agents"},{"name":"data.docs.llmsFull","type":"string","description":"The whole reference as one Markdown file"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"429":{"description":"Rate limited: more than 100 requests in one minute per organization. Wait for the Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"RATE_LIMIT_EXCEEDED","message":"Too many requests: at most 100 per minute per organization. Wait for the Retry-After seconds.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-rate-limit-exceeded"}}}}}},"security":[{"apiKey":[]}],"x-required-scopes":[],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/me`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/me\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/me\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories":{"get":{"operationId":"listStories","summary":"List Stories","description":"Page through the Topic Workspace's original stories (translations are reached through `include=translations` or `GET /stories/by-language/{language}`). Filter with the flat params or the advanced `filter` JSON. The list is lightweight: `content`, `bulletPoints`, `sentiment` and `metadata` are only returned by the detail endpoints.","tags":["Stories"],"parameters":[{"name":"language","in":"query","required":false,"description":"Only stories in this language (full locale code such as en-US)","schema":{"type":"string","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"],"examples":["en-US"]}},{"name":"tag","in":"query","required":false,"description":"Only stories carrying this tag (accent and case insensitive)","schema":{"type":"string","examples":["healthcare"]}},{"name":"search","in":"query","required":false,"description":"Full text search on title, summary and tags. Combines with the paging, order and include params.","schema":{"type":"string","examples":["climate"]}},{"name":"status","in":"query","required":false,"description":"Only stories in this status","schema":{"type":"string","enum":["draft","pending","approved","rejected","scheduled","published","archived","deleted"],"examples":["published"]}},{"name":"dateFrom","in":"query","required":false,"description":"Only stories created on or after this instant (for publication dates use `filter` on `publishedAt`)","schema":{"type":"string","format":"date-time","examples":["2026-08-01T00:00:00Z"]}},{"name":"dateTo","in":"query","required":false,"description":"Only stories created on or before this instant","schema":{"type":"string","format":"date-time","examples":["2026-08-31T23:59:59Z"]}},{"name":"include","in":"query","required":false,"description":"Comma separated relations to embed: images, authors, categories, qualityChecks, seoAudits, translations, contentSources. Relations you do not ask for are never returned. `images` returns public Image objects with `origin`, `aiGenerated` and `generation`.","schema":{"type":"string","description":"Comma separated, any of: images, authors, categories, qualityChecks, seoAudits, translations, contentSources","examples":["images,translations"]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default createdAt DESC). Allowed fields: createdAt, publishedAt, updatedAt, firstReported, title.","schema":{"type":"string","enum":["createdAt DESC","createdAt ASC","publishedAt DESC","updatedAt DESC","firstReported DESC","title ASC","title DESC"],"examples":["createdAt DESC"]}},{"name":"filter","in":"query","required":false,"description":"Advanced LoopBack-style filter as a JSON string. Supports `where` (with `and` / `or` / `nor`, nested at most 3 levels with 20 conditions each, and the operators eq, neq, gt, gte, lt, lte, between, inq, nin, like, nlike, ilike, nilike, exists), `include`, `order`, `limit`, `offset` or `skip` (at most 10000), and `fields`. Allowed where fields: id, title, slug, summary, content, language, status, tags, thumbnail, thumbnailImageId, mainImageId, mainImageUrl, translationStatus, publishedAt, firstReported, createdAt, updatedAt. `like` patterns are regular expressions of at most 100 characters without groups or repetition counts; `regexp` and `near` are not supported; strings are at most 500 characters and `inq` / `nin` take at most 100 values. A filter that breaks a rule answers 400 VALIDATION_ERROR naming the rule. When present it replaces status, language, dateFrom, dateTo, include, limit, page and order (tag and search still apply).","schema":{"type":"string","contentMediaType":"application/json","examples":["{\"where\":{\"status\":\"published\",\"publishedAt\":{\"gte\":\"2026-08-01\"}},\"include\":[\"images\"],\"order\":[\"publishedAt DESC\"],\"limit\":50}"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"A page of stories","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"Story","properties":{"id":{"type":"string","description":"Unique story identifier"},"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"slug":{"type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},"language":{"type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"topicKey":{"type":"string","description":"Topic Workspace the story belongs to"},"parentId":{"type":["string","null"],"description":"Id of the original story when this one is a translation, else null"},"translationStatus":{"type":["string","null"],"description":"pending, in-progress, completed or needs-update (translations only)"},"thumbnail":{"type":"string","description":"URL of the current thumbnail. Match it against `images[].url` to learn its provenance"},"thumbnailImageId":{"type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},"mainImageId":{"type":"string","description":"Id of the current main image, when one was generated, picked from stock or reused from the source"},"mainImageUrl":{"type":"string","description":"URL of the current main image"},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},"firstReported":{"type":"string","description":"ISO 8601 timestamp of the earliest source publication"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},"authors":{"type":"array","items":{"type":"object","x-type":"Author"},"description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},"categories":{"type":"array","items":{"type":"object","x-type":"Category"},"description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},"qualityChecks":{"type":"array","items":{"type":"object","x-type":"QualityCheck"},"description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},"seoAudits":{"type":"array","items":{"type":"object","x-type":"SeoAudit"},"description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},"translations":{"type":"array","items":{"type":"object","x-type":"Translation"},"description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},"contentSources":{"type":"array","items":{"type":"object","x-type":"ContentSourceRef"},"description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."}}},"description":"Stories, newest first by default"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","summary":"How artificial intelligence is transforming medical diagnostics.","slug":"ai-revolution-in-healthcare","status":"published","language":"en-US","tags":["ai","healthcare","technology"],"topicKey":"tech","parentId":null,"thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_400x225.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","mainImageId":"68b9b7c2f1a2b3c4d5e6f702","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","publishedAt":"2026-08-15T11:45:00.000Z","firstReported":"2026-08-15T08:00:00.000Z","createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T12:00:00.000Z","images":[{"id":"68b9b7c2f1a2b3c4d5e6f701","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","originalUrl":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"main","origin":"source","aiGenerated":false,"generation":null,"sourceType":"story","parentId":null,"title":"AI Revolution in Healthcare","description":null,"altText":null,"caption":null,"author":null,"source":"https://techcrunch.com/2026/07/30/ai-healthcare/","license":null,"creditLine":null,"tags":[],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},{"id":"68b9b7c2f1a2b3c4d5e6f702","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","originalUrl":null,"width":1024,"height":576,"format":"PNG","contentType":"image/png","fileSize":1583022,"imageType":"main","origin":"generated","aiGenerated":true,"generation":{"provider":"openai","model":"gpt-image-1","prompt":"Editorial illustration of AI-assisted medical diagnostics in a modern hospital"},"sourceType":"generated","parentId":null,"title":"AI Generated Image - openai","description":null,"altText":null,"caption":null,"author":"openai AI","source":"openai","license":null,"creditLine":null,"tags":["ai-generated","openai","healthcare"],"createdAt":"2026-08-15T11:02:00.000Z","updatedAt":"2026-08-15T11:02:00.000Z"}],"translations":[{"code":"en-US","storyId":"68b9b7c2f1a2b3c4d5e6f700","default":true,"slug":"ai-revolution-in-healthcare","title":"AI Revolution in Healthcare"},{"code":"de-DE","storyId":"68b9b7c2f1a2b3c4d5e6f710","default":false,"slug":"ki-revolution-im-gesundheitswesen","title":"KI-Revolution im Gesundheitswesen"},{"code":"pt-BR","storyId":"68b9b7c2f1a2b3c4d5e6f711","default":false,"slug":"revolucao-da-ia-na-saude","title":"Revolução da IA na saúde"}]}],"meta":{"page":0,"limit":20,"hasMore":true}}}},"x-fields":[{"name":"data","type":"Story[]","description":"Stories, newest first by default"},{"name":"data[].id","type":"string","description":"Unique story identifier"},{"name":"data[].title","type":"string","description":"Story title"},{"name":"data[].summary","type":"string","description":"Short summary"},{"name":"data[].slug","type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},{"name":"data[].status","type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},{"name":"data[].language","type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},{"name":"data[].tags","type":"string[]","description":"Tags"},{"name":"data[].topicKey","type":"string","description":"Topic Workspace the story belongs to"},{"name":"data[].parentId","type":"string | null","description":"Id of the original story when this one is a translation, else null"},{"name":"data[].translationStatus","type":"string | null","description":"pending, in-progress, completed or needs-update (translations only)"},{"name":"data[].thumbnail","type":"string","description":"URL of the current thumbnail. Match it against `images[].url` to learn its provenance"},{"name":"data[].thumbnailImageId","type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},{"name":"data[].mainImageId","type":"string","description":"Id of the current main image, when one was generated, picked from stock or reused from the source"},{"name":"data[].mainImageUrl","type":"string","description":"URL of the current main image"},{"name":"data[].publishedAt","type":"string | null","description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},{"name":"data[].firstReported","type":"string","description":"ISO 8601 timestamp of the earliest source publication"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data[].images","type":"Image[]","description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},{"name":"data[].images[].id","type":"string","description":"Unique image identifier"},{"name":"data[].images[].url","type":"string","description":"Public URL of the image file"},{"name":"data[].images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data[].images[].width","type":"number","description":"Width in pixels"},{"name":"data[].images[].height","type":"number","description":"Height in pixels"},{"name":"data[].images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data[].images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data[].images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data[].images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data[].images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data[].images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data[].images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data[].images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data[].images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data[].images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data[].images[].title","type":"string | null","description":"Image title"},{"name":"data[].images[].description","type":"string | null","description":"Image description"},{"name":"data[].images[].altText","type":"string | null","description":"Alt text"},{"name":"data[].images[].caption","type":"string | null","description":"Caption"},{"name":"data[].images[].author","type":"string | null","description":"Author or photographer"},{"name":"data[].images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data[].images[].license","type":"string | null","description":"License (stock images)"},{"name":"data[].images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data[].images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data[].images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"data[].authors","type":"Author[]","description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},{"name":"data[].categories","type":"Category[]","description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},{"name":"data[].qualityChecks","type":"QualityCheck[]","description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},{"name":"data[].seoAudits","type":"SeoAudit[]","description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},{"name":"data[].translations","type":"Translation[]","description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},{"name":"data[].contentSources","type":"ContentSourceRef[]","description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"429":{"description":"Rate limited: more than 100 requests in one minute per organization. Wait for the Retry-After seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"RATE_LIMIT_EXCEEDED","message":"Too many requests: at most 100 per minute per organization. Wait for the Retry-After seconds.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-rate-limit-exceeded"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/stories?status=published&language=en-US&include=images%2Ctranslations&limit=20`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories?status=published&language=en-US&include=images%2Ctranslations&limit=20\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/stories\"\nparams = {\n    \"status\": \"published\",\n    \"language\": \"en-US\",\n    \"include\": \"images,translations\",\n    \"limit\": 20\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"post":{"operationId":"createStory","summary":"Create Story","description":"Create a story in the Topic Workspace. Only `title` is required; everything else defaults (draft status, the Topic Workspace's default language). Fields outside the documented list are ignored.","tags":["Stories"],"parameters":[{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Story data","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"content":{"type":"string","description":"Full HTML body. Link another story of the Topic Workspace with `<a href=\"/{slug}\">`"},"bulletPoints":{"type":"array","items":{"type":"string"},"description":"Key points"},"language":{"type":"string","description":"Full locale code (default: the Topic Workspace's default language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted (default draft)","enum":["draft","pending","approved","rejected","scheduled","published","archived","deleted"]},"publishedAt":{"type":["string","null"],"format":"date-time","description":"When the story was published. Set automatically the first time `status` becomes `published`; send a date-time to backdate it, or null to clear it"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"slug":{"type":"string","description":"URL slug (generated from the title when omitted)"},"thumbnail":{"type":"string","description":"Thumbnail URL"},"metadata":{"type":"object","description":"SEO metadata: title, description, keywords, canonical, ogImage and custom keys"},"topicKey":{"type":"string","description":"Topic Workspace to create the story in (default: the key's Topic Workspace or the organization's first Topic Workspace)"}},"required":["title"]},"example":{"title":"AI Revolution in Healthcare","summary":"How AI is transforming medical diagnostics","content":"<p>Full story content here...</p>","language":"en-US","status":"draft","tags":["ai","healthcare"]}}},"x-fields":[{"name":"title","type":"string","required":true,"description":"Story title"},{"name":"summary","type":"string","required":false,"description":"Short summary"},{"name":"content","type":"string","required":false,"description":"Full HTML body. Link another story of the Topic Workspace with `<a href=\"/{slug}\">`"},{"name":"bulletPoints","type":"string[]","required":false,"description":"Key points"},{"name":"language","type":"string","required":false,"description":"Full locale code (default: the Topic Workspace's default language)"},{"name":"status","type":"string","required":false,"description":"draft, pending, approved, rejected, scheduled, published, archived or deleted (default draft)"},{"name":"publishedAt","type":"string (ISO 8601) | null","required":false,"description":"When the story was published. Set automatically the first time `status` becomes `published`; send a date-time to backdate it, or null to clear it"},{"name":"tags","type":"string[]","required":false,"description":"Tags"},{"name":"slug","type":"string","required":false,"description":"URL slug (generated from the title when omitted)"},{"name":"thumbnail","type":"string","required":false,"description":"Thumbnail URL"},{"name":"metadata","type":"object","required":false,"description":"SEO metadata: title, description, keywords, canonical, ogImage and custom keys"},{"name":"topicKey","type":"string","required":false,"description":"Topic Workspace to create the story in (default: the key's Topic Workspace or the organization's first Topic Workspace)"}]},"responses":{"201":{"description":"Story created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","x-type":"Story","description":"The created story","properties":{"id":{"type":"string","description":"Unique story identifier"},"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"content":{"type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},"bulletPoints":{"type":"array","items":{"type":"string"},"description":"Key points"},"slug":{"type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},"language":{"type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"topicKey":{"type":"string","description":"Topic Workspace the story belongs to"},"parentId":{"type":["string","null"],"description":"Id of the original story when this one is a translation, else null"},"translationStatus":{"type":["string","null"],"description":"pending, in-progress, completed or needs-update (translations only)"},"sentiment":{"type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},"thumbnail":{"type":"string","description":"URL of the current thumbnail"},"thumbnailImageId":{"type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},"mainImageId":{"type":"string","description":"Id of the current main image"},"mainImageUrl":{"type":"string","description":"URL of the current main image"},"metadata":{"type":"object","description":"SEO metadata and custom fields","properties":{"title":{"type":"string","description":"Meta title"},"description":{"type":"string","description":"Meta description"},"keywords":{"type":"string","description":"Meta keywords, comma separated"},"canonical":{"type":"string","description":"Canonical URL"},"ogImage":{"type":"string","description":"Open Graph image URL"}}},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},"firstReported":{"type":"string","description":"ISO 8601 timestamp of the earliest source publication"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},"authors":{"type":"array","items":{"type":"object","x-type":"Author"},"description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},"categories":{"type":"array","items":{"type":"object","x-type":"Category"},"description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},"qualityChecks":{"type":"array","items":{"type":"object","x-type":"QualityCheck"},"description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},"seoAudits":{"type":"array","items":{"type":"object","x-type":"SeoAudit"},"description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},"translations":{"type":"array","items":{"type":"object","x-type":"Translation"},"description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},"contentSources":{"type":"array","items":{"type":"object","x-type":"ContentSourceRef"},"description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","summary":"How artificial intelligence is transforming medical diagnostics.","slug":"ai-revolution-in-healthcare","status":"draft","language":"en-US","tags":["ai","healthcare","technology"],"topicKey":"tech","parentId":null,"thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_400x225.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","mainImageId":"68b9b7c2f1a2b3c4d5e6f702","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","publishedAt":null,"firstReported":"2026-08-15T08:00:00.000Z","createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T12:00:00.000Z","content":"<h3>Overview</h3><p>A comprehensive look at how AI is being deployed across diagnostics, triage and treatment planning.</p>","bulletPoints":["AI diagnostics accuracy reaches 95%","Cost reduction of 40% in pilot hospitals"],"sentiment":0.8,"metadata":{"title":"AI Revolution in Healthcare: What Changes in 2026","description":"AI is transforming diagnostics, triage and treatment planning. Here is what hospitals report.","keywords":"ai, healthcare, diagnostics"}},"message":"Story created successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"Story","description":"The created story"},{"name":"data.id","type":"string","description":"Unique story identifier"},{"name":"data.title","type":"string","description":"Story title"},{"name":"data.summary","type":"string","description":"Short summary"},{"name":"data.content","type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},{"name":"data.bulletPoints","type":"string[]","description":"Key points"},{"name":"data.slug","type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},{"name":"data.status","type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},{"name":"data.language","type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},{"name":"data.tags","type":"string[]","description":"Tags"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the story belongs to"},{"name":"data.parentId","type":"string | null","description":"Id of the original story when this one is a translation, else null"},{"name":"data.translationStatus","type":"string | null","description":"pending, in-progress, completed or needs-update (translations only)"},{"name":"data.sentiment","type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},{"name":"data.thumbnail","type":"string","description":"URL of the current thumbnail"},{"name":"data.thumbnailImageId","type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},{"name":"data.mainImageId","type":"string","description":"Id of the current main image"},{"name":"data.mainImageUrl","type":"string","description":"URL of the current main image"},{"name":"data.metadata","type":"object","description":"SEO metadata and custom fields"},{"name":"data.metadata.title","type":"string","description":"Meta title"},{"name":"data.metadata.description","type":"string","description":"Meta description"},{"name":"data.metadata.keywords","type":"string","description":"Meta keywords, comma separated"},{"name":"data.metadata.canonical","type":"string","description":"Canonical URL"},{"name":"data.metadata.ogImage","type":"string","description":"Open Graph image URL"},{"name":"data.publishedAt","type":"string | null","description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},{"name":"data.firstReported","type":"string","description":"ISO 8601 timestamp of the earliest source publication"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.images","type":"Image[]","description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},{"name":"data.images[].id","type":"string","description":"Unique image identifier"},{"name":"data.images[].url","type":"string","description":"Public URL of the image file"},{"name":"data.images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.images[].width","type":"number","description":"Width in pixels"},{"name":"data.images[].height","type":"number","description":"Height in pixels"},{"name":"data.images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data.images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.images[].title","type":"string | null","description":"Image title"},{"name":"data.images[].description","type":"string | null","description":"Image description"},{"name":"data.images[].altText","type":"string | null","description":"Alt text"},{"name":"data.images[].caption","type":"string | null","description":"Caption"},{"name":"data.images[].author","type":"string | null","description":"Author or photographer"},{"name":"data.images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.images[].license","type":"string | null","description":"License (stock images)"},{"name":"data.images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"data.authors","type":"Author[]","description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},{"name":"data.categories","type":"Category[]","description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},{"name":"data.qualityChecks","type":"QualityCheck[]","description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},{"name":"data.seoAudits","type":"SeoAudit[]","description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},{"name":"data.translations","type":"Translation[]","description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},{"name":"data.contentSources","type":"ContentSourceRef[]","description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"title is required","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/stories`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"title\": \"AI Revolution in Healthcare\",\n    \"summary\": \"How AI is transforming medical diagnostics\",\n    \"content\": \"<p>Full story content here...</p>\",\n    \"language\": \"en-US\",\n    \"status\": \"draft\",\n    \"tags\": [\n      \"ai\",\n      \"healthcare\"\n    ]\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\":\"AI Revolution in Healthcare\",\"summary\":\"How AI is transforming medical diagnostics\",\"content\":\"<p>Full story content here...</p>\",\"language\":\"en-US\",\"status\":\"draft\",\"tags\":[\"ai\",\"healthcare\"]}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/stories\"\npayload = {\n    \"title\": \"AI Revolution in Healthcare\",\n    \"summary\": \"How AI is transforming medical diagnostics\",\n    \"content\": \"<p>Full story content here...</p>\",\n    \"language\": \"en-US\",\n    \"status\": \"draft\",\n    \"tags\": [\"ai\", \"healthcare\"]\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/by-language/{language}":{"get":{"operationId":"listStoriesByLanguage","summary":"List Stories by Language","description":"Page through every story written in one language, originals and translations alike, with a stable order. Ideal for building a per-language site: pair it with `include=translations` to render language switchers. Every story carries its `relatedStories` in that language, so a static site can render Read next blocks without a call per story.","tags":["Stories"],"parameters":[{"name":"language","in":"path","required":true,"description":"Full locale code, e.g. en-US, de-DE, pt-BR","schema":{"type":"string","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"],"examples":["en-US"]}},{"name":"status","in":"query","required":false,"description":"Only stories in this status","schema":{"type":"string","enum":["draft","pending","approved","rejected","scheduled","published","archived","deleted"],"examples":["published"]}},{"name":"dateFrom","in":"query","required":false,"description":"Only stories created on or after this instant (for publication dates use `filter` on `publishedAt`)","schema":{"type":"string","format":"date-time","examples":["2026-08-01T00:00:00Z"]}},{"name":"dateTo","in":"query","required":false,"description":"Only stories created on or before this instant","schema":{"type":"string","format":"date-time","examples":["2026-08-31T23:59:59Z"]}},{"name":"include","in":"query","required":false,"description":"Comma separated relations to embed: images, authors, categories, qualityChecks, seoAudits, translations, contentSources. Relations you do not ask for are never returned. `images` returns public Image objects with `origin`, `aiGenerated` and `generation`.","schema":{"type":"string","description":"Comma separated, any of: images, authors, categories, qualityChecks, seoAudits, translations, contentSources","examples":["images,translations"]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default createdAt DESC). Allowed fields: createdAt, publishedAt, updatedAt, firstReported, title.","schema":{"type":"string","enum":["createdAt DESC","createdAt ASC","publishedAt DESC","updatedAt DESC","firstReported DESC","title ASC","title DESC"],"examples":["createdAt DESC"]}},{"name":"filter","in":"query","required":false,"description":"Advanced LoopBack-style filter as a JSON string. Supports `where` (with `and` / `or` / `nor`, nested at most 3 levels with 20 conditions each, and the operators eq, neq, gt, gte, lt, lte, between, inq, nin, like, nlike, ilike, nilike, exists), `include`, `order`, `limit`, `offset` or `skip` (at most 10000), and `fields`. Allowed where fields: id, title, slug, summary, content, language, status, tags, thumbnail, thumbnailImageId, mainImageId, mainImageUrl, translationStatus, publishedAt, firstReported, createdAt, updatedAt. `like` patterns are regular expressions of at most 100 characters without groups or repetition counts; `regexp` and `near` are not supported; strings are at most 500 characters and `inq` / `nin` take at most 100 values. A filter that breaks a rule answers 400 VALIDATION_ERROR naming the rule. When present it replaces status, language, dateFrom, dateTo, include, limit, page and order (tag and search still apply).","schema":{"type":"string","contentMediaType":"application/json","examples":["{\"where\":{\"status\":\"published\",\"publishedAt\":{\"gte\":\"2026-08-01\"}},\"include\":[\"images\"],\"order\":[\"publishedAt DESC\"],\"limit\":50}"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"A page of stories in that language","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"Story","properties":{"id":{"type":"string","description":"Unique story identifier"},"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"slug":{"type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},"language":{"type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"topicKey":{"type":"string","description":"Topic Workspace the story belongs to"},"parentId":{"type":["string","null"],"description":"Id of the original story when this one is a translation, else null"},"translationStatus":{"type":["string","null"],"description":"pending, in-progress, completed or needs-update (translations only)"},"thumbnail":{"type":"string","description":"URL of the current thumbnail. Match it against `images[].url` to learn its provenance"},"thumbnailImageId":{"type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},"mainImageId":{"type":"string","description":"Id of the current main image, when one was generated, picked from stock or reused from the source"},"mainImageUrl":{"type":"string","description":"URL of the current main image"},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},"firstReported":{"type":"string","description":"ISO 8601 timestamp of the earliest source publication"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},"authors":{"type":"array","items":{"type":"object","x-type":"Author"},"description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},"categories":{"type":"array","items":{"type":"object","x-type":"Category"},"description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},"qualityChecks":{"type":"array","items":{"type":"object","x-type":"QualityCheck"},"description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},"seoAudits":{"type":"array","items":{"type":"object","x-type":"SeoAudit"},"description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},"translations":{"type":"array","items":{"type":"object","x-type":"Translation"},"description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},"contentSources":{"type":"array","items":{"type":"object","x-type":"ContentSourceRef"},"description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},"relatedStories":{"type":"array","items":{"type":"object","x-type":"RelatedStory","properties":{"id":{"type":"string","description":"Id of the related story"},"title":{"type":"string","description":"Its current title"},"slug":{"type":"string","description":"Its slug. Point it at your own story route, like the links inside `content`"},"summary":{"type":"string","description":"Its short summary"},"language":{"type":"string","description":"Full locale code, always the language of this story"},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of its first publication"},"mainImageUrl":{"type":["string","null"],"description":"URL of its main image, when it has one"},"thumbnail":{"type":["string","null"],"description":"URL of its thumbnail, often the source publisher's photo"},"score":{"type":"number","description":"Similarity to this story from 0 to 1, higher is closer. Listed stories score 0.7 or more"}}},"description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."}}},"description":"Stories in the requested language"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"},"language":{"type":"string","description":"The language requested"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","summary":"How artificial intelligence is transforming medical diagnostics.","slug":"ai-revolution-in-healthcare","status":"published","language":"en-US","tags":["ai","healthcare","technology"],"topicKey":"tech","parentId":null,"thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_400x225.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","mainImageId":"68b9b7c2f1a2b3c4d5e6f702","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","publishedAt":"2026-08-15T11:45:00.000Z","firstReported":"2026-08-15T08:00:00.000Z","createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T12:00:00.000Z","translations":[{"code":"en-US","storyId":"68b9b7c2f1a2b3c4d5e6f700","default":true,"slug":"ai-revolution-in-healthcare","title":"AI Revolution in Healthcare"},{"code":"de-DE","storyId":"68b9b7c2f1a2b3c4d5e6f710","default":false,"slug":"ki-revolution-im-gesundheitswesen","title":"KI-Revolution im Gesundheitswesen"},{"code":"pt-BR","storyId":"68b9b7c2f1a2b3c4d5e6f711","default":false,"slug":"revolucao-da-ia-na-saude","title":"Revolução da IA na saúde"}],"relatedStories":[{"id":"68b9b7c2f1a2b3c4d5e6f740","title":"Hospitals Cut Waiting Times with AI Triage","slug":"hospitals-cut-waiting-times-with-ai-triage","summary":"Emergency departments using AI triage report shorter waits and fewer missed cases.","language":"en-US","publishedAt":"2026-08-12T09:10:00.000Z","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/generated_openai_1788125993651_0.png","thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/thumb_400x225.jpg","score":0.8412},{"id":"68b9b7c2f1a2b3c4d5e6f741","title":"Regulators Draft Rules for Diagnostic AI","slug":"regulators-draft-rules-for-diagnostic-ai","summary":"A draft framework would require clinical trials before diagnostic models reach patients.","language":"en-US","publishedAt":"2026-08-09T15:30:00.000Z","mainImageUrl":null,"thumbnail":null,"score":0.7761}]}],"meta":{"page":0,"limit":20,"hasMore":false,"language":"en-US"}}}},"x-fields":[{"name":"data","type":"Story[]","description":"Stories in the requested language"},{"name":"data[].id","type":"string","description":"Unique story identifier"},{"name":"data[].title","type":"string","description":"Story title"},{"name":"data[].summary","type":"string","description":"Short summary"},{"name":"data[].slug","type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},{"name":"data[].status","type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},{"name":"data[].language","type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},{"name":"data[].tags","type":"string[]","description":"Tags"},{"name":"data[].topicKey","type":"string","description":"Topic Workspace the story belongs to"},{"name":"data[].parentId","type":"string | null","description":"Id of the original story when this one is a translation, else null"},{"name":"data[].translationStatus","type":"string | null","description":"pending, in-progress, completed or needs-update (translations only)"},{"name":"data[].thumbnail","type":"string","description":"URL of the current thumbnail. Match it against `images[].url` to learn its provenance"},{"name":"data[].thumbnailImageId","type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},{"name":"data[].mainImageId","type":"string","description":"Id of the current main image, when one was generated, picked from stock or reused from the source"},{"name":"data[].mainImageUrl","type":"string","description":"URL of the current main image"},{"name":"data[].publishedAt","type":"string | null","description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},{"name":"data[].firstReported","type":"string","description":"ISO 8601 timestamp of the earliest source publication"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data[].images","type":"Image[]","description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},{"name":"data[].images[].id","type":"string","description":"Unique image identifier"},{"name":"data[].images[].url","type":"string","description":"Public URL of the image file"},{"name":"data[].images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data[].images[].width","type":"number","description":"Width in pixels"},{"name":"data[].images[].height","type":"number","description":"Height in pixels"},{"name":"data[].images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data[].images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data[].images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data[].images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data[].images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data[].images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data[].images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data[].images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data[].images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data[].images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data[].images[].title","type":"string | null","description":"Image title"},{"name":"data[].images[].description","type":"string | null","description":"Image description"},{"name":"data[].images[].altText","type":"string | null","description":"Alt text"},{"name":"data[].images[].caption","type":"string | null","description":"Caption"},{"name":"data[].images[].author","type":"string | null","description":"Author or photographer"},{"name":"data[].images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data[].images[].license","type":"string | null","description":"License (stock images)"},{"name":"data[].images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data[].images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data[].images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"data[].authors","type":"Author[]","description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},{"name":"data[].categories","type":"Category[]","description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},{"name":"data[].qualityChecks","type":"QualityCheck[]","description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},{"name":"data[].seoAudits","type":"SeoAudit[]","description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},{"name":"data[].translations","type":"Translation[]","description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},{"name":"data[].contentSources","type":"ContentSourceRef[]","description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},{"name":"data[].relatedStories","type":"RelatedStory[]","description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."},{"name":"data[].relatedStories[].id","type":"string","description":"Id of the related story"},{"name":"data[].relatedStories[].title","type":"string","description":"Its current title"},{"name":"data[].relatedStories[].slug","type":"string","description":"Its slug. Point it at your own story route, like the links inside `content`"},{"name":"data[].relatedStories[].summary","type":"string","description":"Its short summary"},{"name":"data[].relatedStories[].language","type":"string","description":"Full locale code, always the language of this story"},{"name":"data[].relatedStories[].publishedAt","type":"string | null","description":"ISO 8601 timestamp of its first publication"},{"name":"data[].relatedStories[].mainImageUrl","type":"string | null","description":"URL of its main image, when it has one"},{"name":"data[].relatedStories[].thumbnail","type":"string | null","description":"URL of its thumbnail, often the source publisher's photo"},{"name":"data[].relatedStories[].score","type":"number","description":"Similarity to this story from 0 to 1, higher is closer. Listed stories score 0.7 or more"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"},{"name":"meta.language","type":"string","description":"The language requested"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const language = \"en-US\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/by-language/${language}?include=translations&limit=20`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/by-language/en-US?include=translations&limit=20\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nlanguage = \"en-US\"\nurl = f\"https://client-api.news-factory.app/v1/stories/by-language/{language}\"\nparams = {\n    \"include\": \"translations\",\n    \"limit\": 20\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/translation-map":{"get":{"operationId":"translationMap","summary":"Translation Map","description":"A compact map from each story to all of its language versions, without loading the stories themselves. Use it to generate hreflang tags or language switchers. With `lang`, entries are anchored on the stories in that language so the map lines up with `GET /stories/by-language/{language}`.","tags":["Stories"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Anchor the map on stories in this language (full locale code)","schema":{"type":"string","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"],"examples":["en-US"]}},{"name":"limit","in":"query","required":false,"description":"Maximum entries (default 500, max 1000)","schema":{"type":"number","examples":[500]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The map","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Story id"},"translations":{"type":"array","items":{"type":"object"},"description":"Every language version of the family, the original first: `{ id, code, slug }`"}}},"description":"One entry per anchor story"},"meta":{"type":"object","properties":{"count":{"type":"number","description":"Entries returned"},"lang":{"type":["string","null"],"description":"Language filter applied"},"limit":{"type":"number","description":"Limit applied"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f700","translations":[{"id":"68b9b7c2f1a2b3c4d5e6f700","code":"en-US","slug":"ai-revolution-in-healthcare"},{"id":"68b9b7c2f1a2b3c4d5e6f710","code":"de-DE","slug":"ki-revolution-im-gesundheitswesen"},{"id":"68b9b7c2f1a2b3c4d5e6f711","code":"pt-BR","slug":"revolucao-da-ia-na-saude"}]}],"meta":{"count":1,"lang":"en-US","limit":500}}}},"x-fields":[{"name":"data","type":"object[]","description":"One entry per anchor story"},{"name":"data[].id","type":"string","description":"Story id"},{"name":"data[].translations","type":"object[]","description":"Every language version of the family, the original first: `{ id, code, slug }`"},{"name":"meta.count","type":"number","description":"Entries returned"},{"name":"meta.lang","type":"string | null","description":"Language filter applied"},{"name":"meta.limit","type":"number","description":"Limit applied"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/stories/translation-map?lang=en-US&limit=200`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/translation-map?lang=en-US&limit=200\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/stories/translation-map\"\nparams = {\n    \"lang\": \"en-US\",\n    \"limit\": 200\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{id}":{"get":{"operationId":"getStoryById","summary":"Get Story","description":"One story with its full HTML content, key points, sentiment, SEO metadata and `relatedStories`: up to 5 published stories in the same language that are closest to it, for a Read next block. Add relations with `include`.","tags":["Stories"],"parameters":[{"name":"id","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"include","in":"query","required":false,"description":"Comma separated relations to embed: images, authors, categories, qualityChecks, seoAudits, translations, contentSources. Relations you do not ask for are never returned. `images` returns public Image objects with `origin`, `aiGenerated` and `generation`.","schema":{"type":"string","description":"Comma separated, any of: images, authors, categories, qualityChecks, seoAudits, translations, contentSources","examples":["images,translations"]}},{"name":"fullContentSources","in":"query","required":false,"description":"With `include=contentSources`, return the complete source records instead of the short references","schema":{"type":"boolean","examples":[false]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The story","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"Story","description":"The story with its full content","properties":{"id":{"type":"string","description":"Unique story identifier"},"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"content":{"type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},"bulletPoints":{"type":"array","items":{"type":"string"},"description":"Key points"},"slug":{"type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},"language":{"type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"topicKey":{"type":"string","description":"Topic Workspace the story belongs to"},"parentId":{"type":["string","null"],"description":"Id of the original story when this one is a translation, else null"},"translationStatus":{"type":["string","null"],"description":"pending, in-progress, completed or needs-update (translations only)"},"sentiment":{"type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},"thumbnail":{"type":"string","description":"URL of the current thumbnail"},"thumbnailImageId":{"type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},"mainImageId":{"type":"string","description":"Id of the current main image"},"mainImageUrl":{"type":"string","description":"URL of the current main image"},"metadata":{"type":"object","description":"SEO metadata and custom fields","properties":{"title":{"type":"string","description":"Meta title"},"description":{"type":"string","description":"Meta description"},"keywords":{"type":"string","description":"Meta keywords, comma separated"},"canonical":{"type":"string","description":"Canonical URL"},"ogImage":{"type":"string","description":"Open Graph image URL"}}},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},"firstReported":{"type":"string","description":"ISO 8601 timestamp of the earliest source publication"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},"authors":{"type":"array","items":{"type":"object","x-type":"Author"},"description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},"categories":{"type":"array","items":{"type":"object","x-type":"Category"},"description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},"qualityChecks":{"type":"array","items":{"type":"object","x-type":"QualityCheck"},"description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},"seoAudits":{"type":"array","items":{"type":"object","x-type":"SeoAudit"},"description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},"translations":{"type":"array","items":{"type":"object","x-type":"Translation"},"description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},"contentSources":{"type":"array","items":{"type":"object","x-type":"ContentSourceRef"},"description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},"relatedStories":{"type":"array","items":{"type":"object","x-type":"RelatedStory","properties":{"id":{"type":"string","description":"Id of the related story"},"title":{"type":"string","description":"Its current title"},"slug":{"type":"string","description":"Its slug. Point it at your own story route, like the links inside `content`"},"summary":{"type":"string","description":"Its short summary"},"language":{"type":"string","description":"Full locale code, always the language of this story"},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of its first publication"},"mainImageUrl":{"type":["string","null"],"description":"URL of its main image, when it has one"},"thumbnail":{"type":["string","null"],"description":"URL of its thumbnail, often the source publisher's photo"},"score":{"type":"number","description":"Similarity to this story from 0 to 1, higher is closer. Listed stories score 0.7 or more"}}},"description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","summary":"How artificial intelligence is transforming medical diagnostics.","slug":"ai-revolution-in-healthcare","status":"published","language":"en-US","tags":["ai","healthcare","technology"],"topicKey":"tech","parentId":null,"thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_400x225.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","mainImageId":"68b9b7c2f1a2b3c4d5e6f702","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","publishedAt":"2026-08-15T11:45:00.000Z","firstReported":"2026-08-15T08:00:00.000Z","createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T12:00:00.000Z","content":"<h3>Overview</h3><p>A comprehensive look at how AI is being deployed across diagnostics, triage and treatment planning.</p>","bulletPoints":["AI diagnostics accuracy reaches 95%","Cost reduction of 40% in pilot hospitals"],"sentiment":0.8,"metadata":{"title":"AI Revolution in Healthcare: What Changes in 2026","description":"AI is transforming diagnostics, triage and treatment planning. Here is what hospitals report.","keywords":"ai, healthcare, diagnostics"},"relatedStories":[{"id":"68b9b7c2f1a2b3c4d5e6f740","title":"Hospitals Cut Waiting Times with AI Triage","slug":"hospitals-cut-waiting-times-with-ai-triage","summary":"Emergency departments using AI triage report shorter waits and fewer missed cases.","language":"en-US","publishedAt":"2026-08-12T09:10:00.000Z","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/generated_openai_1788125993651_0.png","thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/thumb_400x225.jpg","score":0.8412},{"id":"68b9b7c2f1a2b3c4d5e6f741","title":"Regulators Draft Rules for Diagnostic AI","slug":"regulators-draft-rules-for-diagnostic-ai","summary":"A draft framework would require clinical trials before diagnostic models reach patients.","language":"en-US","publishedAt":"2026-08-09T15:30:00.000Z","mainImageUrl":null,"thumbnail":null,"score":0.7761}],"images":[{"id":"68b9b7c2f1a2b3c4d5e6f701","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","originalUrl":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"main","origin":"source","aiGenerated":false,"generation":null,"sourceType":"story","parentId":null,"title":"AI Revolution in Healthcare","description":null,"altText":null,"caption":null,"author":null,"source":"https://techcrunch.com/2026/07/30/ai-healthcare/","license":null,"creditLine":null,"tags":[],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},{"id":"68b9b7c2f1a2b3c4d5e6f702","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","originalUrl":null,"width":1024,"height":576,"format":"PNG","contentType":"image/png","fileSize":1583022,"imageType":"main","origin":"generated","aiGenerated":true,"generation":{"provider":"openai","model":"gpt-image-1","prompt":"Editorial illustration of AI-assisted medical diagnostics in a modern hospital"},"sourceType":"generated","parentId":null,"title":"AI Generated Image - openai","description":null,"altText":null,"caption":null,"author":"openai AI","source":"openai","license":null,"creditLine":null,"tags":["ai-generated","openai","healthcare"],"createdAt":"2026-08-15T11:02:00.000Z","updatedAt":"2026-08-15T11:02:00.000Z"}],"authors":[{"id":"68b9b7c2f1a2b3c4d5e6f720","name":"Jane Doe","status":"active","avatarUrl":"https://cdn.news-factory.app/authors/jane.jpg","topicKey":"tech"}],"categories":[{"id":"68b9b7c2f1a2b3c4d5e6f730","name":"Health Tech","slug":"health-tech","parentId":null}],"translations":[{"code":"en-US","storyId":"68b9b7c2f1a2b3c4d5e6f700","default":true,"slug":"ai-revolution-in-healthcare","title":"AI Revolution in Healthcare"},{"code":"de-DE","storyId":"68b9b7c2f1a2b3c4d5e6f710","default":false,"slug":"ki-revolution-im-gesundheitswesen","title":"KI-Revolution im Gesundheitswesen"},{"code":"pt-BR","storyId":"68b9b7c2f1a2b3c4d5e6f711","default":false,"slug":"revolucao-da-ia-na-saude","title":"Revolução da IA na saúde"}]}}}},"x-fields":[{"name":"data","type":"Story","description":"The story with its full content"},{"name":"data.id","type":"string","description":"Unique story identifier"},{"name":"data.title","type":"string","description":"Story title"},{"name":"data.summary","type":"string","description":"Short summary"},{"name":"data.content","type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},{"name":"data.bulletPoints","type":"string[]","description":"Key points"},{"name":"data.slug","type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},{"name":"data.status","type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},{"name":"data.language","type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},{"name":"data.tags","type":"string[]","description":"Tags"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the story belongs to"},{"name":"data.parentId","type":"string | null","description":"Id of the original story when this one is a translation, else null"},{"name":"data.translationStatus","type":"string | null","description":"pending, in-progress, completed or needs-update (translations only)"},{"name":"data.sentiment","type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},{"name":"data.thumbnail","type":"string","description":"URL of the current thumbnail"},{"name":"data.thumbnailImageId","type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},{"name":"data.mainImageId","type":"string","description":"Id of the current main image"},{"name":"data.mainImageUrl","type":"string","description":"URL of the current main image"},{"name":"data.metadata","type":"object","description":"SEO metadata and custom fields"},{"name":"data.metadata.title","type":"string","description":"Meta title"},{"name":"data.metadata.description","type":"string","description":"Meta description"},{"name":"data.metadata.keywords","type":"string","description":"Meta keywords, comma separated"},{"name":"data.metadata.canonical","type":"string","description":"Canonical URL"},{"name":"data.metadata.ogImage","type":"string","description":"Open Graph image URL"},{"name":"data.publishedAt","type":"string | null","description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},{"name":"data.firstReported","type":"string","description":"ISO 8601 timestamp of the earliest source publication"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.images","type":"Image[]","description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},{"name":"data.images[].id","type":"string","description":"Unique image identifier"},{"name":"data.images[].url","type":"string","description":"Public URL of the image file"},{"name":"data.images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.images[].width","type":"number","description":"Width in pixels"},{"name":"data.images[].height","type":"number","description":"Height in pixels"},{"name":"data.images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data.images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.images[].title","type":"string | null","description":"Image title"},{"name":"data.images[].description","type":"string | null","description":"Image description"},{"name":"data.images[].altText","type":"string | null","description":"Alt text"},{"name":"data.images[].caption","type":"string | null","description":"Caption"},{"name":"data.images[].author","type":"string | null","description":"Author or photographer"},{"name":"data.images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.images[].license","type":"string | null","description":"License (stock images)"},{"name":"data.images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"data.authors","type":"Author[]","description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},{"name":"data.categories","type":"Category[]","description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},{"name":"data.qualityChecks","type":"QualityCheck[]","description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},{"name":"data.seoAudits","type":"SeoAudit[]","description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},{"name":"data.translations","type":"Translation[]","description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},{"name":"data.contentSources","type":"ContentSourceRef[]","description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},{"name":"data.relatedStories","type":"RelatedStory[]","description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."},{"name":"data.relatedStories[].id","type":"string","description":"Id of the related story"},{"name":"data.relatedStories[].title","type":"string","description":"Its current title"},{"name":"data.relatedStories[].slug","type":"string","description":"Its slug. Point it at your own story route, like the links inside `content`"},{"name":"data.relatedStories[].summary","type":"string","description":"Its short summary"},{"name":"data.relatedStories[].language","type":"string","description":"Full locale code, always the language of this story"},{"name":"data.relatedStories[].publishedAt","type":"string | null","description":"ISO 8601 timestamp of its first publication"},{"name":"data.relatedStories[].mainImageUrl","type":"string | null","description":"URL of its main image, when it has one"},{"name":"data.relatedStories[].thumbnail","type":"string | null","description":"URL of its thumbnail, often the source publisher's photo"},{"name":"data.relatedStories[].score","type":"number","description":"Similarity to this story from 0 to 1, higher is closer. Listed stories score 0.7 or more"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${id}?include=images%2Cauthors%2Ccategories%2Ctranslations`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700?include=images%2Cauthors%2Ccategories%2Ctranslations\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{id}\"\nparams = {\n    \"include\": \"images,authors,categories,translations\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"patch":{"operationId":"updateStory","summary":"Update Story","description":"Update the provided fields and return the fresh story. Arrays such as `tags` replace the existing value. Publishing is a status change: set `status` to `published`. The related stories of a story you publish (or retitle) are found within about a minute, so the response still shows the previous `relatedStories`: read the story again later for the new ones.","tags":["Stories"],"parameters":[{"name":"id","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Fields to update (at least one)","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"content":{"type":"string","description":"Full HTML body. Link another story of the Topic Workspace with `<a href=\"/{slug}\">`"},"bulletPoints":{"type":"array","items":{"type":"string"},"description":"Key points"},"language":{"type":"string","description":"Full locale code (default: the Topic Workspace's default language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted (default draft)","enum":["draft","pending","approved","rejected","scheduled","published","archived","deleted"]},"publishedAt":{"type":["string","null"],"format":"date-time","description":"When the story was published. Set automatically the first time `status` becomes `published`; send a date-time to backdate it, or null to clear it"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"slug":{"type":"string","description":"URL slug (generated from the title when omitted)"},"thumbnail":{"type":"string","description":"Thumbnail URL"},"metadata":{"type":"object","description":"SEO metadata: title, description, keywords, canonical, ogImage and custom keys"}}},"example":{"title":"AI Revolution in Healthcare: 2026 Update","status":"published","tags":["ai","healthcare","2026"]}}},"x-fields":[{"name":"title","type":"string","required":false,"description":"Story title"},{"name":"summary","type":"string","required":false,"description":"Short summary"},{"name":"content","type":"string","required":false,"description":"Full HTML body. Link another story of the Topic Workspace with `<a href=\"/{slug}\">`"},{"name":"bulletPoints","type":"string[]","required":false,"description":"Key points"},{"name":"language","type":"string","required":false,"description":"Full locale code (default: the Topic Workspace's default language)"},{"name":"status","type":"string","required":false,"description":"draft, pending, approved, rejected, scheduled, published, archived or deleted (default draft)"},{"name":"publishedAt","type":"string (ISO 8601) | null","required":false,"description":"When the story was published. Set automatically the first time `status` becomes `published`; send a date-time to backdate it, or null to clear it"},{"name":"tags","type":"string[]","required":false,"description":"Tags"},{"name":"slug","type":"string","required":false,"description":"URL slug (generated from the title when omitted)"},{"name":"thumbnail","type":"string","required":false,"description":"Thumbnail URL"},{"name":"metadata","type":"object","required":false,"description":"SEO metadata: title, description, keywords, canonical, ogImage and custom keys"}]},"responses":{"200":{"description":"Story updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","x-type":"Story","description":"The updated story (no relations)","properties":{"id":{"type":"string","description":"Unique story identifier"},"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"content":{"type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},"bulletPoints":{"type":"array","items":{"type":"string"},"description":"Key points"},"slug":{"type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},"language":{"type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"topicKey":{"type":"string","description":"Topic Workspace the story belongs to"},"parentId":{"type":["string","null"],"description":"Id of the original story when this one is a translation, else null"},"translationStatus":{"type":["string","null"],"description":"pending, in-progress, completed or needs-update (translations only)"},"sentiment":{"type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},"thumbnail":{"type":"string","description":"URL of the current thumbnail"},"thumbnailImageId":{"type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},"mainImageId":{"type":"string","description":"Id of the current main image"},"mainImageUrl":{"type":"string","description":"URL of the current main image"},"metadata":{"type":"object","description":"SEO metadata and custom fields","properties":{"title":{"type":"string","description":"Meta title"},"description":{"type":"string","description":"Meta description"},"keywords":{"type":"string","description":"Meta keywords, comma separated"},"canonical":{"type":"string","description":"Canonical URL"},"ogImage":{"type":"string","description":"Open Graph image URL"}}},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},"firstReported":{"type":"string","description":"ISO 8601 timestamp of the earliest source publication"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"relatedStories":{"type":"array","items":{"type":"object","x-type":"RelatedStory"},"description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare: 2026 Update","summary":"How artificial intelligence is transforming medical diagnostics.","slug":"ai-revolution-in-healthcare","status":"published","language":"en-US","tags":["ai","healthcare","technology"],"topicKey":"tech","parentId":null,"thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_400x225.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","mainImageId":"68b9b7c2f1a2b3c4d5e6f702","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","publishedAt":"2026-08-15T11:45:00.000Z","firstReported":"2026-08-15T08:00:00.000Z","createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T12:00:00.000Z","content":"<h3>Overview</h3><p>A comprehensive look at how AI is being deployed across diagnostics, triage and treatment planning.</p>","bulletPoints":["AI diagnostics accuracy reaches 95%","Cost reduction of 40% in pilot hospitals"],"sentiment":0.8,"metadata":{"title":"AI Revolution in Healthcare: What Changes in 2026","description":"AI is transforming diagnostics, triage and treatment planning. Here is what hospitals report.","keywords":"ai, healthcare, diagnostics"},"relatedStories":[{"id":"68b9b7c2f1a2b3c4d5e6f740","title":"Hospitals Cut Waiting Times with AI Triage","slug":"hospitals-cut-waiting-times-with-ai-triage","summary":"Emergency departments using AI triage report shorter waits and fewer missed cases.","language":"en-US","publishedAt":"2026-08-12T09:10:00.000Z","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/generated_openai_1788125993651_0.png","thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/thumb_400x225.jpg","score":0.8412},{"id":"68b9b7c2f1a2b3c4d5e6f741","title":"Regulators Draft Rules for Diagnostic AI","slug":"regulators-draft-rules-for-diagnostic-ai","summary":"A draft framework would require clinical trials before diagnostic models reach patients.","language":"en-US","publishedAt":"2026-08-09T15:30:00.000Z","mainImageUrl":null,"thumbnail":null,"score":0.7761}]},"message":"Story updated successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"Story","description":"The updated story (no relations)"},{"name":"data.id","type":"string","description":"Unique story identifier"},{"name":"data.title","type":"string","description":"Story title"},{"name":"data.summary","type":"string","description":"Short summary"},{"name":"data.content","type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},{"name":"data.bulletPoints","type":"string[]","description":"Key points"},{"name":"data.slug","type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},{"name":"data.status","type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},{"name":"data.language","type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},{"name":"data.tags","type":"string[]","description":"Tags"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the story belongs to"},{"name":"data.parentId","type":"string | null","description":"Id of the original story when this one is a translation, else null"},{"name":"data.translationStatus","type":"string | null","description":"pending, in-progress, completed or needs-update (translations only)"},{"name":"data.sentiment","type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},{"name":"data.thumbnail","type":"string","description":"URL of the current thumbnail"},{"name":"data.thumbnailImageId","type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},{"name":"data.mainImageId","type":"string","description":"Id of the current main image"},{"name":"data.mainImageUrl","type":"string","description":"URL of the current main image"},{"name":"data.metadata","type":"object","description":"SEO metadata and custom fields"},{"name":"data.metadata.title","type":"string","description":"Meta title"},{"name":"data.metadata.description","type":"string","description":"Meta description"},{"name":"data.metadata.keywords","type":"string","description":"Meta keywords, comma separated"},{"name":"data.metadata.canonical","type":"string","description":"Canonical URL"},{"name":"data.metadata.ogImage","type":"string","description":"Open Graph image URL"},{"name":"data.publishedAt","type":"string | null","description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},{"name":"data.firstReported","type":"string","description":"ISO 8601 timestamp of the earliest source publication"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.relatedStories","type":"RelatedStory[]","description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Provide at least one updatable field: title, summary, content, bulletPoints, language, status, tags, slug, thumbnail, metadata, firstReported, publishedAt","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${id}`, {\n  method: 'PATCH',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"title\": \"AI Revolution in Healthcare: 2026 Update\",\n    \"status\": \"published\",\n    \"tags\": [\n      \"ai\",\n      \"healthcare\",\n      \"2026\"\n    ]\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X PATCH \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\":\"AI Revolution in Healthcare: 2026 Update\",\"status\":\"published\",\"tags\":[\"ai\",\"healthcare\",\"2026\"]}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{id}\"\npayload = {\n    \"title\": \"AI Revolution in Healthcare: 2026 Update\",\n    \"status\": \"published\",\n    \"tags\": [\"ai\", \"healthcare\", \"2026\"]\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.patch(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"delete":{"operationId":"deleteStory","summary":"Delete Story","description":"Delete a story permanently. Images that no other story or source uses are removed from storage as well. Translations of the story are not deleted.","tags":["Stories"],"parameters":[{"name":"id","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Story deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"message":"Story deleted successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${id}`, {\n  method: 'DELETE',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.success);"},{"lang":"Shell","label":"cURL","source":"curl -X DELETE \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.delete(url, headers=headers)\nresult = response.json()\nprint(result.success)"}]}},"/stories/slug/{slug}":{"get":{"operationId":"getStoryBySlug","summary":"Get Story by Slug","description":"The same detail as `GET /stories/{id}`, related stories included, looked up by the story's URL slug. Slugs are unique per Topic Workspace and language, so send the `X-Topic-Key` header when your key spans Topic Workspaces.","tags":["Stories"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The story slug","schema":{"type":"string","examples":["ai-revolution-in-healthcare"]}},{"name":"include","in":"query","required":false,"description":"Comma separated relations to embed: images, authors, categories, qualityChecks, seoAudits, translations, contentSources. Relations you do not ask for are never returned. `images` returns public Image objects with `origin`, `aiGenerated` and `generation`.","schema":{"type":"string","description":"Comma separated, any of: images, authors, categories, qualityChecks, seoAudits, translations, contentSources","examples":["images,translations"]}},{"name":"fullContentSources","in":"query","required":false,"description":"With `include=contentSources`, return the complete source records instead of the short references","schema":{"type":"boolean","examples":[false]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The story","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"Story","description":"The story with its full content","properties":{"id":{"type":"string","description":"Unique story identifier"},"title":{"type":"string","description":"Story title"},"summary":{"type":"string","description":"Short summary"},"content":{"type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},"bulletPoints":{"type":"array","items":{"type":"string"},"description":"Key points"},"slug":{"type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},"status":{"type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},"language":{"type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"topicKey":{"type":"string","description":"Topic Workspace the story belongs to"},"parentId":{"type":["string","null"],"description":"Id of the original story when this one is a translation, else null"},"translationStatus":{"type":["string","null"],"description":"pending, in-progress, completed or needs-update (translations only)"},"sentiment":{"type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},"thumbnail":{"type":"string","description":"URL of the current thumbnail"},"thumbnailImageId":{"type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},"mainImageId":{"type":"string","description":"Id of the current main image"},"mainImageUrl":{"type":"string","description":"URL of the current main image"},"metadata":{"type":"object","description":"SEO metadata and custom fields","properties":{"title":{"type":"string","description":"Meta title"},"description":{"type":"string","description":"Meta description"},"keywords":{"type":"string","description":"Meta keywords, comma separated"},"canonical":{"type":"string","description":"Canonical URL"},"ogImage":{"type":"string","description":"Open Graph image URL"}}},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},"firstReported":{"type":"string","description":"ISO 8601 timestamp of the earliest source publication"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},"authors":{"type":"array","items":{"type":"object","x-type":"Author"},"description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},"categories":{"type":"array","items":{"type":"object","x-type":"Category"},"description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},"qualityChecks":{"type":"array","items":{"type":"object","x-type":"QualityCheck"},"description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},"seoAudits":{"type":"array","items":{"type":"object","x-type":"SeoAudit"},"description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},"translations":{"type":"array","items":{"type":"object","x-type":"Translation"},"description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},"contentSources":{"type":"array","items":{"type":"object","x-type":"ContentSourceRef"},"description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},"relatedStories":{"type":"array","items":{"type":"object","x-type":"RelatedStory","properties":{"id":{"type":"string","description":"Id of the related story"},"title":{"type":"string","description":"Its current title"},"slug":{"type":"string","description":"Its slug. Point it at your own story route, like the links inside `content`"},"summary":{"type":"string","description":"Its short summary"},"language":{"type":"string","description":"Full locale code, always the language of this story"},"publishedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of its first publication"},"mainImageUrl":{"type":["string","null"],"description":"URL of its main image, when it has one"},"thumbnail":{"type":["string","null"],"description":"URL of its thumbnail, often the source publisher's photo"},"score":{"type":"number","description":"Similarity to this story from 0 to 1, higher is closer. Listed stories score 0.7 or more"}}},"description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","summary":"How artificial intelligence is transforming medical diagnostics.","slug":"ai-revolution-in-healthcare","status":"published","language":"en-US","tags":["ai","healthcare","technology"],"topicKey":"tech","parentId":null,"thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_400x225.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","mainImageId":"68b9b7c2f1a2b3c4d5e6f702","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","publishedAt":"2026-08-15T11:45:00.000Z","firstReported":"2026-08-15T08:00:00.000Z","createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T12:00:00.000Z","content":"<h3>Overview</h3><p>A comprehensive look at how AI is being deployed across diagnostics, triage and treatment planning.</p>","bulletPoints":["AI diagnostics accuracy reaches 95%","Cost reduction of 40% in pilot hospitals"],"sentiment":0.8,"metadata":{"title":"AI Revolution in Healthcare: What Changes in 2026","description":"AI is transforming diagnostics, triage and treatment planning. Here is what hospitals report.","keywords":"ai, healthcare, diagnostics"},"relatedStories":[{"id":"68b9b7c2f1a2b3c4d5e6f740","title":"Hospitals Cut Waiting Times with AI Triage","slug":"hospitals-cut-waiting-times-with-ai-triage","summary":"Emergency departments using AI triage report shorter waits and fewer missed cases.","language":"en-US","publishedAt":"2026-08-12T09:10:00.000Z","mainImageUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/generated_openai_1788125993651_0.png","thumbnail":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f740/thumb_400x225.jpg","score":0.8412},{"id":"68b9b7c2f1a2b3c4d5e6f741","title":"Regulators Draft Rules for Diagnostic AI","slug":"regulators-draft-rules-for-diagnostic-ai","summary":"A draft framework would require clinical trials before diagnostic models reach patients.","language":"en-US","publishedAt":"2026-08-09T15:30:00.000Z","mainImageUrl":null,"thumbnail":null,"score":0.7761}],"images":[{"id":"68b9b7c2f1a2b3c4d5e6f701","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","originalUrl":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"main","origin":"source","aiGenerated":false,"generation":null,"sourceType":"story","parentId":null,"title":"AI Revolution in Healthcare","description":null,"altText":null,"caption":null,"author":null,"source":"https://techcrunch.com/2026/07/30/ai-healthcare/","license":null,"creditLine":null,"tags":[],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},{"id":"68b9b7c2f1a2b3c4d5e6f702","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","originalUrl":null,"width":1024,"height":576,"format":"PNG","contentType":"image/png","fileSize":1583022,"imageType":"main","origin":"generated","aiGenerated":true,"generation":{"provider":"openai","model":"gpt-image-1","prompt":"Editorial illustration of AI-assisted medical diagnostics in a modern hospital"},"sourceType":"generated","parentId":null,"title":"AI Generated Image - openai","description":null,"altText":null,"caption":null,"author":"openai AI","source":"openai","license":null,"creditLine":null,"tags":["ai-generated","openai","healthcare"],"createdAt":"2026-08-15T11:02:00.000Z","updatedAt":"2026-08-15T11:02:00.000Z"}],"authors":[{"id":"68b9b7c2f1a2b3c4d5e6f720","name":"Jane Doe","status":"active","avatarUrl":"https://cdn.news-factory.app/authors/jane.jpg","topicKey":"tech"}],"categories":[{"id":"68b9b7c2f1a2b3c4d5e6f730","name":"Health Tech","slug":"health-tech","parentId":null}],"translations":[{"code":"en-US","storyId":"68b9b7c2f1a2b3c4d5e6f700","default":true,"slug":"ai-revolution-in-healthcare","title":"AI Revolution in Healthcare"},{"code":"de-DE","storyId":"68b9b7c2f1a2b3c4d5e6f710","default":false,"slug":"ki-revolution-im-gesundheitswesen","title":"KI-Revolution im Gesundheitswesen"},{"code":"pt-BR","storyId":"68b9b7c2f1a2b3c4d5e6f711","default":false,"slug":"revolucao-da-ia-na-saude","title":"Revolução da IA na saúde"}]}}}},"x-fields":[{"name":"data","type":"Story","description":"The story with its full content"},{"name":"data.id","type":"string","description":"Unique story identifier"},{"name":"data.title","type":"string","description":"Story title"},{"name":"data.summary","type":"string","description":"Short summary"},{"name":"data.content","type":"string","description":"Full HTML body of the story. Links to other stories are written as `<a href=\"/{slug}\">` (a slug, never a URL): point them at your own story route before rendering, as the guide Rendering stories on your site explains"},{"name":"data.bulletPoints","type":"string[]","description":"Key points"},{"name":"data.slug","type":"string","description":"URL-friendly identifier, unique per Topic Workspace and language"},{"name":"data.status","type":"string","description":"draft, pending, approved, rejected, scheduled, published, archived or deleted"},{"name":"data.language","type":"string","description":"Full locale code of the story text, e.g. en-US, de-DE, pt-BR"},{"name":"data.tags","type":"string[]","description":"Tags"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the story belongs to"},{"name":"data.parentId","type":"string | null","description":"Id of the original story when this one is a translation, else null"},{"name":"data.translationStatus","type":"string | null","description":"pending, in-progress, completed or needs-update (translations only)"},{"name":"data.sentiment","type":"number","description":"Sentiment score from -1 (negative) to 1 (positive)"},{"name":"data.thumbnail","type":"string","description":"URL of the current thumbnail"},{"name":"data.thumbnailImageId","type":"string","description":"Id of the `images[]` entry backing `thumbnail`"},{"name":"data.mainImageId","type":"string","description":"Id of the current main image"},{"name":"data.mainImageUrl","type":"string","description":"URL of the current main image"},{"name":"data.metadata","type":"object","description":"SEO metadata and custom fields"},{"name":"data.metadata.title","type":"string","description":"Meta title"},{"name":"data.metadata.description","type":"string","description":"Meta description"},{"name":"data.metadata.keywords","type":"string","description":"Meta keywords, comma separated"},{"name":"data.metadata.canonical","type":"string","description":"Canonical URL"},{"name":"data.metadata.ogImage","type":"string","description":"Open Graph image URL"},{"name":"data.publishedAt","type":"string | null","description":"ISO 8601 timestamp of the first publication: set when `status` first becomes `published`, kept if the story is unpublished later, null until then"},{"name":"data.firstReported","type":"string","description":"ISO 8601 timestamp of the earliest source publication"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.images","type":"Image[]","description":"All images attached to the story (only with `include=images`). Each item is a public Image object; prefer the ones with `aiGenerated: true` over the publisher's photo."},{"name":"data.images[].id","type":"string","description":"Unique image identifier"},{"name":"data.images[].url","type":"string","description":"Public URL of the image file"},{"name":"data.images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.images[].width","type":"number","description":"Width in pixels"},{"name":"data.images[].height","type":"number","description":"Height in pixels"},{"name":"data.images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data.images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.images[].title","type":"string | null","description":"Image title"},{"name":"data.images[].description","type":"string | null","description":"Image description"},{"name":"data.images[].altText","type":"string | null","description":"Alt text"},{"name":"data.images[].caption","type":"string | null","description":"Caption"},{"name":"data.images[].author","type":"string | null","description":"Author or photographer"},{"name":"data.images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.images[].license","type":"string | null","description":"License (stock images)"},{"name":"data.images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"data.authors","type":"Author[]","description":"Assigned authors (only with `include=authors`): `{ id, name, status, avatarUrl, bio, topicKey }`"},{"name":"data.categories","type":"Category[]","description":"Assigned categories (only with `include=categories`): `{ id, name, slug, description, parentId }`"},{"name":"data.qualityChecks","type":"QualityCheck[]","description":"Quality check history, newest first (only with `include=qualityChecks`). Same objects as `GET /stories/{id}/content/quality-checks`."},{"name":"data.seoAudits","type":"SeoAudit[]","description":"SEO audit history, newest first (only with `include=seoAudits`). Same objects as `GET /stories/{id}/content/seo-audits`."},{"name":"data.translations","type":"Translation[]","description":"Every language version of this story including itself (only with `include=translations`): `{ code, storyId, default, slug, title }`. `default` marks the original."},{"name":"data.contentSources","type":"ContentSourceRef[]","description":"Sources the story was written from (only with `include=contentSources`): `{ id, title, link, sourceType, sourceMeta }`. Add `fullContentSources=true` on the detail endpoints for the complete records."},{"name":"data.relatedStories","type":"RelatedStory[]","description":"Up to 5 published stories of the same Topic Workspace and language that are closest to this one in meaning, closest first: ready for a Read next block. Always returned, no `include` needed. Found when the story is published (within about a minute) and again when a published story gets a new title or summary; empty until then and when no story is close enough. Only stories published right now are listed, with their current title and slug, and never another language version of this story."},{"name":"data.relatedStories[].id","type":"string","description":"Id of the related story"},{"name":"data.relatedStories[].title","type":"string","description":"Its current title"},{"name":"data.relatedStories[].slug","type":"string","description":"Its slug. Point it at your own story route, like the links inside `content`"},{"name":"data.relatedStories[].summary","type":"string","description":"Its short summary"},{"name":"data.relatedStories[].language","type":"string","description":"Full locale code, always the language of this story"},{"name":"data.relatedStories[].publishedAt","type":"string | null","description":"ISO 8601 timestamp of its first publication"},{"name":"data.relatedStories[].mainImageUrl","type":"string | null","description":"URL of its main image, when it has one"},{"name":"data.relatedStories[].thumbnail","type":"string | null","description":"URL of its thumbnail, often the source publisher's photo"},{"name":"data.relatedStories[].score","type":"number","description":"Similarity to this story from 0 to 1, higher is closer. Listed stories score 0.7 or more"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const slug = \"ai-revolution-in-healthcare\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/slug/${slug}?include=images%2Ctranslations`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/slug/ai-revolution-in-healthcare?include=images%2Ctranslations\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nslug = \"ai-revolution-in-healthcare\"\nurl = f\"https://client-api.news-factory.app/v1/stories/slug/{slug}\"\nparams = {\n    \"include\": \"images,translations\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{storyId}/media":{"get":{"operationId":"listStoryMedia","summary":"List Story Media","description":"All images attached to a story as public Image objects. Each carries `origin`, `aiGenerated` and `generation`, so you can tell the images News Factory generated (safe to publish as your own) from the publisher's photo (`origin: \"source\"`).","tags":["Stories"],"x-group":"Media","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The story's images","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"Public Image objects. Filter on `aiGenerated` or `origin` to pick what to publish."},"meta":{"type":"object","properties":{"count":{"type":"number","description":"Number of images"}}}}},"example":{"success":true,"data":[{"id":"68b9b7c2f1a2b3c4d5e6f701","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","originalUrl":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"main","origin":"source","aiGenerated":false,"generation":null,"sourceType":"story","parentId":null,"title":"AI Revolution in Healthcare","description":null,"altText":null,"caption":null,"author":null,"source":"https://techcrunch.com/2026/07/30/ai-healthcare/","license":null,"creditLine":null,"tags":[],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},{"id":"68b9b7c2f1a2b3c4d5e6f702","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","originalUrl":null,"width":1024,"height":576,"format":"PNG","contentType":"image/png","fileSize":1583022,"imageType":"main","origin":"generated","aiGenerated":true,"generation":{"provider":"openai","model":"gpt-image-1","prompt":"Editorial illustration of AI-assisted medical diagnostics in a modern hospital"},"sourceType":"generated","parentId":null,"title":"AI Generated Image - openai","description":null,"altText":null,"caption":null,"author":"openai AI","source":"openai","license":null,"creditLine":null,"tags":["ai-generated","openai","healthcare"],"createdAt":"2026-08-15T11:02:00.000Z","updatedAt":"2026-08-15T11:02:00.000Z"}],"meta":{"count":2}}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"Image[]","description":"Public Image objects. Filter on `aiGenerated` or `origin` to pick what to publish."},{"name":"data[].id","type":"string","description":"Unique image identifier"},{"name":"data[].url","type":"string","description":"Public URL of the image file"},{"name":"data[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data[].width","type":"number","description":"Width in pixels"},{"name":"data[].height","type":"number","description":"Height in pixels"},{"name":"data[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data[].contentType","type":"string","description":"MIME type of the image"},{"name":"data[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data[].title","type":"string | null","description":"Image title"},{"name":"data[].description","type":"string | null","description":"Image description"},{"name":"data[].altText","type":"string | null","description":"Alt text"},{"name":"data[].caption","type":"string | null","description":"Caption"},{"name":"data[].author","type":"string | null","description":"Author or photographer"},{"name":"data[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data[].license","type":"string | null","description":"License (stock images)"},{"name":"data[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"meta.count","type":"number","description":"Number of images"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"post":{"operationId":"uploadStoryMedia","summary":"Upload Media","description":"Store an image and attach it to the story. Send either a base64 `fileData` with `fileName` and `mimeType`, or a `url` to import from.","tags":["Stories"],"x-group":"Media","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Either fileData (with fileName and mimeType) or url","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Image title"},"description":{"type":"string","description":"Image description"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"imageType":{"type":"string","description":"Role on the story (default content)","enum":["content","main","gallery","thumbnail","other"]},"fileData":{"type":"string","x-type":"file","description":"Base64 encoded image (PNG, JPEG, WEBP, GIF). 10 MB request limit."},"fileName":{"type":"string","description":"Original file name (required with fileData)"},"mimeType":{"type":"string","description":"MIME type (required with fileData)"},"url":{"type":"string","description":"Public image URL to import instead of fileData"}}},"example":{"title":"Hero image","tags":["photo","hero"],"imageType":"content","url":"https://example.com/image.jpg"}}},"x-fields":[{"name":"title","type":"string","required":false,"description":"Image title"},{"name":"description","type":"string","required":false,"description":"Image description"},{"name":"tags","type":"string[]","required":false,"description":"Tags"},{"name":"imageType","type":"string","required":false,"description":"Role on the story (default content)"},{"name":"fileData","type":"file","required":false,"description":"Base64 encoded image (PNG, JPEG, WEBP, GIF). 10 MB request limit."},{"name":"fileName","type":"string","required":false,"description":"Original file name (required with fileData)"},{"name":"mimeType","type":"string","required":false,"description":"MIME type (required with fileData)"},{"name":"url","type":"string","required":false,"description":"Public image URL to import instead of fileData"}]},"responses":{"201":{"description":"Image stored and attached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","x-type":"Image","description":"The public Image object (`origin: \"upload\"`, `aiGenerated: false`)","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f705","url":"https://cdn.news-factory.app/organizations/acme/hero.jpg","s3Url":"https://cdn.news-factory.app/organizations/acme/hero.jpg","originalUrl":null,"width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"content","origin":"upload","aiGenerated":false,"generation":null,"sourceType":"user-upload","parentId":null,"title":"Hero image","description":null,"altText":null,"caption":null,"author":null,"source":null,"license":null,"creditLine":null,"tags":["photo","hero"],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},"message":"Image imported from URL and attached to story"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"Image","description":"The public Image object (`origin: \"upload\"`, `aiGenerated: false`)"},{"name":"data.id","type":"string","description":"Unique image identifier"},{"name":"data.url","type":"string","description":"Public URL of the image file"},{"name":"data.s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.width","type":"number","description":"Width in pixels"},{"name":"data.height","type":"number","description":"Height in pixels"},{"name":"data.format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.contentType","type":"string","description":"MIME type of the image"},{"name":"data.fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.title","type":"string | null","description":"Image title"},{"name":"data.description","type":"string | null","description":"Image description"},{"name":"data.altText","type":"string | null","description":"Alt text"},{"name":"data.caption","type":"string | null","description":"Caption"},{"name":"data.author","type":"string | null","description":"Author or photographer"},{"name":"data.source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.license","type":"string | null","description":"License (stock images)"},{"name":"data.creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Provide either fileData (base64) with fileName and mimeType, or url","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"title\": \"Hero image\",\n    \"tags\": [\n      \"photo\",\n      \"hero\"\n    ],\n    \"imageType\": \"content\",\n    \"url\": \"https://example.com/image.jpg\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\":\"Hero image\",\"tags\":[\"photo\",\"hero\"],\"imageType\":\"content\",\"url\":\"https://example.com/image.jpg\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media\"\npayload = {\n    \"title\": \"Hero image\",\n    \"tags\": [\"photo\", \"hero\"],\n    \"imageType\": \"content\",\n    \"url\": \"https://example.com/image.jpg\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{storyId}/media/from-url":{"post":{"operationId":"importImageUrl","summary":"Import Image from URL","description":"Download an image from a public URL, store it and attach it to the story.","tags":["Stories"],"x-group":"Media","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"The URL and optional metadata","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Public image URL"},"title":{"type":"string","description":"Image title"},"description":{"type":"string","description":"Image description"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags"},"imageType":{"type":"string","description":"Role on the story (default content)","enum":["content","main","gallery","thumbnail","other"]}},"required":["url"]},"example":{"url":"https://example.com/image.jpg","title":"Hero image","imageType":"main"}}},"x-fields":[{"name":"url","type":"string","required":true,"description":"Public image URL"},{"name":"title","type":"string","required":false,"description":"Image title"},{"name":"description","type":"string","required":false,"description":"Image description"},{"name":"tags","type":"string[]","required":false,"description":"Tags"},{"name":"imageType","type":"string","required":false,"description":"Role on the story (default content)"}]},"responses":{"201":{"description":"Image stored and attached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","x-type":"Image","description":"The public Image object (`origin: \"upload\"`, `aiGenerated: false`)","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f705","url":"https://cdn.news-factory.app/organizations/acme/hero.jpg","s3Url":"https://cdn.news-factory.app/organizations/acme/hero.jpg","originalUrl":null,"width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"content","origin":"upload","aiGenerated":false,"generation":null,"sourceType":"user-upload","parentId":null,"title":"Hero image","description":null,"altText":null,"caption":null,"author":null,"source":null,"license":null,"creditLine":null,"tags":["photo","hero"],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},"message":"Image imported from URL and attached to story"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"Image","description":"The public Image object (`origin: \"upload\"`, `aiGenerated: false`)"},{"name":"data.id","type":"string","description":"Unique image identifier"},{"name":"data.url","type":"string","description":"Public URL of the image file"},{"name":"data.s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.width","type":"number","description":"Width in pixels"},{"name":"data.height","type":"number","description":"Height in pixels"},{"name":"data.format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.contentType","type":"string","description":"MIME type of the image"},{"name":"data.fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.title","type":"string | null","description":"Image title"},{"name":"data.description","type":"string | null","description":"Image description"},{"name":"data.altText","type":"string | null","description":"Alt text"},{"name":"data.caption","type":"string | null","description":"Caption"},{"name":"data.author","type":"string | null","description":"Author or photographer"},{"name":"data.source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.license","type":"string | null","description":"License (stock images)"},{"name":"data.creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"url is required","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media/from-url`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"url\": \"https://example.com/image.jpg\",\n    \"title\": \"Hero image\",\n    \"imageType\": \"main\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media/from-url\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"url\":\"https://example.com/image.jpg\",\"title\":\"Hero image\",\"imageType\":\"main\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media/from-url\"\npayload = {\n    \"url\": \"https://example.com/image.jpg\",\n    \"title\": \"Hero image\",\n    \"imageType\": \"main\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{storyId}/media/generate":{"post":{"operationId":"generateImage","summary":"Generate Image (AI)","description":"Generate an image with an AI model and attach it to the story. Providers: OpenAI (GPT Image), Google Gemini and Ideogram. Story context (title, summary) is added to the prompt unless `includeStoryContext` is false. The saved image is flagged `aiGenerated: true` with `origin: \"generated\"`; read it back with `GET /stories/{storyId}/media`. Costs AI credits.","tags":["Stories"],"x-group":"Media","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Generation parameters","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","description":"What to generate"},"provider":{"type":"string","description":"AI provider","enum":["openai","gemini","ideogram"]},"aspectRatio":{"type":"string","description":"Aspect ratio","enum":["16:9","4:3","1:1","3:4","9:16","3:2"]},"quality":{"type":"string","description":"Quality level (default high)","enum":["high","medium","low","auto"]},"style":{"type":"string","description":"Visual style (default photographic)","enum":["photographic","realistic","natural","vivid","artistic","design","auto"]},"negativePrompt":{"type":"string","description":"What to avoid"},"numImages":{"type":"string","description":"Number of images (default 1)","enum":["1","2","4"]},"imageType":{"type":"string","description":"Role of the generated image (default main)","enum":["content","main","gallery","thumbnail","other"]},"includeStoryContext":{"type":"string","description":"Add the story title and summary to the prompt (default true)","enum":["true","false"]}},"required":["prompt","provider"]},"example":{"prompt":"A photorealistic editorial photograph of a hospital diagnostics lab","provider":"openai","aspectRatio":"16:9","quality":"high"}}},"x-fields":[{"name":"prompt","type":"string","required":true,"description":"What to generate"},{"name":"provider","type":"string","required":true,"description":"AI provider"},{"name":"aspectRatio","type":"string","required":false,"description":"Aspect ratio"},{"name":"quality","type":"string","required":false,"description":"Quality level (default high)"},{"name":"style","type":"string","required":false,"description":"Visual style (default photographic)"},{"name":"negativePrompt","type":"string","required":false,"description":"What to avoid"},{"name":"numImages","type":"string","required":false,"description":"Number of images (default 1)"},{"name":"imageType","type":"string","required":false,"description":"Role of the generated image (default main)"},{"name":"includeStoryContext","type":"string","required":false,"description":"Add the story title and summary to the prompt (default true)"}]},"responses":{"200":{"description":"Image generated and attached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"storyId":{"type":"string","description":"The story"},"provider":{"type":"string","description":"Provider used"},"model":{"type":["string","null"],"description":"Model used"},"processingTimeMs":{"type":["number","null"],"description":"Generation time"},"images":{"type":"array","items":{"type":"object"},"description":"Generated images: `{ imageId, url, width, height, format, prompt }`. Fetch the full public Image objects with `GET /stories/{storyId}/media`."}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"storyId":"68b9b7c2f1a2b3c4d5e6f700","provider":"openai","model":"gpt-image-1","processingTimeMs":4500,"images":[{"imageId":"68b9b7c2f1a2b3c4d5e6f702","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/generated_openai_1788425993651_0.png","width":1792,"height":1024,"format":"png","prompt":"A photorealistic editorial photograph of a hospital diagnostics lab. Story: AI Revolution in Healthcare"}]},"message":"Image generated successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.storyId","type":"string","description":"The story"},{"name":"data.provider","type":"string","description":"Provider used"},{"name":"data.model","type":"string | null","description":"Model used"},{"name":"data.processingTimeMs","type":"number | null","description":"Generation time"},{"name":"data.images","type":"object[]","description":"Generated images: `{ imageId, url, width, height, format, prompt }`. Fetch the full public Image objects with `GET /stories/{storyId}/media`."},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"prompt is required","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["ai:generate","stories:write"]}],"x-required-scopes":["ai:generate","stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media/generate`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"prompt\": \"A photorealistic editorial photograph of a hospital diagnostics lab\",\n    \"provider\": \"openai\",\n    \"aspectRatio\": \"16:9\",\n    \"quality\": \"high\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media/generate\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prompt\":\"A photorealistic editorial photograph of a hospital diagnostics lab\",\"provider\":\"openai\",\"aspectRatio\":\"16:9\",\"quality\":\"high\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media/generate\"\npayload = {\n    \"prompt\": \"A photorealistic editorial photograph of a hospital diagnostics lab\",\n    \"provider\": \"openai\",\n    \"aspectRatio\": \"16:9\",\n    \"quality\": \"high\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{storyId}/media/thumbnail":{"post":{"operationId":"setThumbnail","summary":"Set Thumbnail","description":"Generate the story thumbnail from an attached image (`imageId`), from any image URL (`imageUrl`), or from the story's first attached image when both are omitted. The previous thumbnail image is deleted when no other story uses it.","tags":["Stories"],"x-group":"Thumbnail","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"All fields optional","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"imageId":{"type":"string","description":"Attached image to crop from"},"imageUrl":{"type":"string","description":"Image URL to crop from"},"preset":{"type":"string","description":"Size preset (default small, 400 by 225)","enum":["small","standard","large","square","og"]},"width":{"type":"number","description":"Custom width in pixels (overrides the preset)"},"height":{"type":"number","description":"Custom height in pixels"},"quality":{"type":"number","description":"JPEG quality 1 to 100 (default 80)"}}},"example":{"imageId":"68b9b7c2f1a2b3c4d5e6f702","preset":"standard","quality":85}}},"x-fields":[{"name":"imageId","type":"string","required":false,"description":"Attached image to crop from"},{"name":"imageUrl","type":"string","required":false,"description":"Image URL to crop from"},{"name":"preset","type":"string","required":false,"description":"Size preset (default small, 400 by 225)"},{"name":"width","type":"number","required":false,"description":"Custom width in pixels (overrides the preset)"},{"name":"height","type":"number","required":false,"description":"Custom height in pixels"},{"name":"quality","type":"number","required":false,"description":"JPEG quality 1 to 100 (default 80)"}]},"responses":{"200":{"description":"Thumbnail set","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"storyId":{"type":"string","description":"The story"},"thumbnailUrl":{"type":"string","description":"Public URL of the new thumbnail (also written to the story's `thumbnail`)"},"thumbnailImageId":{"type":"string","description":"Id of the new thumbnail Image (also written to `thumbnailImageId`)"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format"},"fileSize":{"type":"number","description":"Bytes"},"preset":{"type":"string","description":"Preset used"},"sourceImage":{"type":"string","description":"The image id or URL the thumbnail was cropped from"},"previousThumbnail":{"type":["object","null"],"description":"`{ imageId, deleted }` when a previous thumbnail existed"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"storyId":"68b9b7c2f1a2b3c4d5e6f700","thumbnailUrl":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/thumb_800x450.jpg","thumbnailImageId":"68b9b7c2f1a2b3c4d5e6f703","width":800,"height":450,"format":"jpeg","fileSize":61234,"preset":"standard","sourceImage":"68b9b7c2f1a2b3c4d5e6f702","previousThumbnail":{"imageId":"68b9b7c2f1a2b3c4d5e6f6ff","deleted":true}},"message":"Thumbnail set successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.storyId","type":"string","description":"The story"},{"name":"data.thumbnailUrl","type":"string","description":"Public URL of the new thumbnail (also written to the story's `thumbnail`)"},{"name":"data.thumbnailImageId","type":"string","description":"Id of the new thumbnail Image (also written to `thumbnailImageId`)"},{"name":"data.width","type":"number","description":"Width in pixels"},{"name":"data.height","type":"number","description":"Height in pixels"},{"name":"data.format","type":"string","description":"File format"},{"name":"data.fileSize","type":"number","description":"Bytes"},{"name":"data.preset","type":"string","description":"Preset used"},{"name":"data.sourceImage","type":"string","description":"The image id or URL the thumbnail was cropped from"},{"name":"data.previousThumbnail","type":"object | null","description":"`{ imageId, deleted }` when a previous thumbnail existed"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"preset must be one of: standard, small, large, square, og","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media/thumbnail`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"imageId\": \"68b9b7c2f1a2b3c4d5e6f702\",\n    \"preset\": \"standard\",\n    \"quality\": 85\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media/thumbnail\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"imageId\":\"68b9b7c2f1a2b3c4d5e6f702\",\"preset\":\"standard\",\"quality\":85}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media/thumbnail\"\npayload = {\n    \"imageId\": \"68b9b7c2f1a2b3c4d5e6f702\",\n    \"preset\": \"standard\",\n    \"quality\": 85\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"delete":{"operationId":"removeThumbnail","summary":"Remove Thumbnail","description":"Clear the story's `thumbnail` and `thumbnailImageId`. The image record itself is kept.","tags":["Stories"],"x-group":"Thumbnail","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Thumbnail cleared","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"message":"Thumbnail removed"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media/thumbnail`, {\n  method: 'DELETE',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.success);"},{"lang":"Shell","label":"cURL","source":"curl -X DELETE \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media/thumbnail\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media/thumbnail\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.delete(url, headers=headers)\nresult = response.json()\nprint(result.success)"}]}},"/stories/{storyId}/media/{imageId}":{"post":{"operationId":"attachStoryMedia","summary":"Attach Media","description":"Attach an image that already exists in the organization (for example one uploaded to another story) to this story.","tags":["Stories"],"x-group":"Media","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"imageId","in":"path","required":true,"description":"The image id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f702"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Image attached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"storyId":{"type":"string","description":"The story"},"imageId":{"type":"string","description":"The attached image"},"imageIds":{"type":"array","items":{"type":"string"},"description":"All image ids now attached to the story"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"storyId":"68b9b7c2f1a2b3c4d5e6f700","imageId":"68b9b7c2f1a2b3c4d5e6f702","imageIds":["68b9b7c2f1a2b3c4d5e6f701","68b9b7c2f1a2b3c4d5e6f702"]},"message":"Image attached to story"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.storyId","type":"string","description":"The story"},{"name":"data.imageId","type":"string","description":"The attached image"},{"name":"data.imageIds","type":"string[]","description":"All image ids now attached to the story"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story or image not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story or image not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst imageId = \"68b9b7c2f1a2b3c4d5e6f702\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media/${imageId}`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media/68b9b7c2f1a2b3c4d5e6f702\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nimage_id = \"68b9b7c2f1a2b3c4d5e6f702\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media/{image_id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"delete":{"operationId":"removeStoryMedia","summary":"Detach Media","description":"Detach an image from the story. The image record is kept and stays attached to any other story that uses it.","tags":["Stories"],"x-group":"Media","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"imageId","in":"path","required":true,"description":"The image id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f702"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Image detached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"message":"Image removed from story"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story or image not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story or image not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:write"]}],"x-required-scopes":["stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst imageId = \"68b9b7c2f1a2b3c4d5e6f702\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/media/${imageId}`, {\n  method: 'DELETE',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.success);"},{"lang":"Shell","label":"cURL","source":"curl -X DELETE \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/media/68b9b7c2f1a2b3c4d5e6f702\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nimage_id = \"68b9b7c2f1a2b3c4d5e6f702\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/media/{image_id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.delete(url, headers=headers)\nresult = response.json()\nprint(result.success)"}]}},"/stories/{storyId}/content/suggest-titles":{"post":{"operationId":"suggestTitles","summary":"Suggest Titles","description":"AI title suggestions grounded in the story's content sources. The story needs at least one content source. Costs AI credits.","tags":["Stories"],"x-group":"Content","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","description":"How many suggestions (1 to 10, default 3)"},"language":{"type":"string","description":"Language of the titles as a full locale code (default: the story language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"titles":{"type":"array","items":{"type":"string"},"description":"Titles to avoid repeating"}}},"example":{"count":3,"language":"en-US"}}},"x-fields":[{"name":"count","type":"number","required":false,"description":"How many suggestions (1 to 10, default 3)"},{"name":"language","type":"string","required":false,"description":"Language of the titles as a full locale code (default: the story language)"},{"name":"titles","type":"string[]","required":false,"description":"Titles to avoid repeating"}]},"responses":{"200":{"description":"Suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"storyTitle":{"type":"string","description":"Current title"},"count":{"type":"number","description":"Suggestions returned"},"titleSuggestions":{"type":"array","items":{"type":"string"},"description":"Suggested titles"},"language":{"type":"string","description":"Language used"},"model":{"type":"string","description":"LLM model used"}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","storyTitle":"AI Revolution in Healthcare","count":3,"titleSuggestions":["AI Revolution Reshapes Modern Healthcare","How Artificial Intelligence Is Transforming Medicine","The Future of Healthcare: AI-Driven Diagnostics"],"language":"en-US","model":"claude-sonnet-5"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"storyTitle","type":"string","description":"Current title"},{"name":"count","type":"number","description":"Suggestions returned"},{"name":"titleSuggestions","type":"string[]","description":"Suggested titles"},{"name":"language","type":"string","description":"Language used"},{"name":"model","type":"string","description":"LLM model used"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Story has no content sources for title suggestions","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/suggest-titles`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"count\": 3,\n    \"language\": \"en-US\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.titleSuggestions);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/suggest-titles\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"count\":3,\"language\":\"en-US\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/suggest-titles\"\npayload = {\n    \"count\": 3,\n    \"language\": \"en-US\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.titleSuggestions)"}]}},"/stories/{storyId}/content/suggest-summary":{"post":{"operationId":"suggestSummary","summary":"Suggest Summary","description":"AI summary suggestions grounded in the story's content sources. Costs AI credits.","tags":["Stories"],"x-group":"Content","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","description":"How many suggestions (1 to 5, default 3)"},"language":{"type":"string","description":"Language of the summaries as a full locale code (default: the story language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]}}},"example":{"count":3,"language":"en-US"}}},"x-fields":[{"name":"count","type":"number","required":false,"description":"How many suggestions (1 to 5, default 3)"},{"name":"language","type":"string","required":false,"description":"Language of the summaries as a full locale code (default: the story language)"}]},"responses":{"200":{"description":"Suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"storySummary":{"type":"string","description":"Current summary"},"count":{"type":"number","description":"Suggestions returned"},"summarySuggestions":{"type":"array","items":{"type":"string"},"description":"Suggested summaries"},"language":{"type":"string","description":"Language used"},"model":{"type":"string","description":"LLM model used"}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","storySummary":"How artificial intelligence is transforming medical diagnostics.","count":2,"summarySuggestions":["Hospitals across Europe report faster triage and lower costs after adopting AI diagnostics.","AI diagnostic tools now reach 95% accuracy in pilot programmes, cutting costs by 40%."],"language":"en-US","model":"claude-sonnet-5"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"storySummary","type":"string","description":"Current summary"},{"name":"count","type":"number","description":"Suggestions returned"},{"name":"summarySuggestions","type":"string[]","description":"Suggested summaries"},{"name":"language","type":"string","description":"Language used"},{"name":"model","type":"string","description":"LLM model used"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Story has no content sources for summary suggestions","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/suggest-summary`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"count\": 3,\n    \"language\": \"en-US\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.summarySuggestions);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/suggest-summary\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"count\":3,\"language\":\"en-US\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/suggest-summary\"\npayload = {\n    \"count\": 3,\n    \"language\": \"en-US\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.summarySuggestions)"}]}},"/stories/{storyId}/content/suggest-seo-title":{"post":{"operationId":"suggestSeoTitle","summary":"Suggest SEO Title","description":"Search engine optimised title suggestions (under 60 characters, keyword first). Write the pick to `metadata.title` with `PATCH /stories/{id}`. Costs AI credits.","tags":["Stories"],"x-group":"Content","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","description":"How many suggestions (1 to 5, default 3)"},"language":{"type":"string","description":"Language of the titles as a full locale code (default: the story language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]}}},"example":{"count":3}}},"x-fields":[{"name":"count","type":"number","required":false,"description":"How many suggestions (1 to 5, default 3)"},{"name":"language","type":"string","required":false,"description":"Language of the titles as a full locale code (default: the story language)"}]},"responses":{"200":{"description":"Suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"count":{"type":"number","description":"Suggestions returned"},"seoTitleSuggestions":{"type":"array","items":{"type":"string"},"description":"Suggested SEO titles"},"language":{"type":"string","description":"Language used"}}},"example":{"success":true,"count":3,"seoTitleSuggestions":["AI in Healthcare 2026: Diagnostics, Costs and Results","AI Diagnostics Hit 95% Accuracy in Hospital Pilots","How AI Is Cutting Hospital Costs by 40%"],"language":"en-US"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"count","type":"number","description":"Suggestions returned"},{"name":"seoTitleSuggestions","type":"string[]","description":"Suggested SEO titles"},{"name":"language","type":"string","description":"Language used"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Story has no content sources for SEO title suggestions","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/suggest-seo-title`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"count\": 3\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.seoTitleSuggestions);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/suggest-seo-title\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"count\":3}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/suggest-seo-title\"\npayload = {\n    \"count\": 3\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.seoTitleSuggestions)"}]}},"/stories/{storyId}/content/suggest-meta-description":{"post":{"operationId":"suggestMetaDescription","summary":"Suggest Meta Description","description":"Meta description suggestions (under 160 characters). Write the pick to `metadata.description` with `PATCH /stories/{id}`. Costs AI credits.","tags":["Stories"],"x-group":"Content","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","description":"How many suggestions (1 to 5, default 3)"},"language":{"type":"string","description":"Language of the descriptions as a full locale code (default: the story language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]}}},"example":{"count":3}}},"x-fields":[{"name":"count","type":"number","required":false,"description":"How many suggestions (1 to 5, default 3)"},{"name":"language","type":"string","required":false,"description":"Language of the descriptions as a full locale code (default: the story language)"}]},"responses":{"200":{"description":"Suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"count":{"type":"number","description":"Suggestions returned"},"metaDescriptionSuggestions":{"type":"array","items":{"type":"string"},"description":"Suggested meta descriptions"},"language":{"type":"string","description":"Language used"}}},"example":{"success":true,"count":2,"metaDescriptionSuggestions":["AI diagnostics reach 95% accuracy and cut hospital costs by 40%. See what the European pilots found.","How hospitals use AI for triage and diagnostics, and what it means for patients in 2026."],"language":"en-US"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"count","type":"number","description":"Suggestions returned"},{"name":"metaDescriptionSuggestions","type":"string[]","description":"Suggested meta descriptions"},{"name":"language","type":"string","description":"Language used"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Story has no content sources for meta description suggestions","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/suggest-meta-description`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"count\": 3\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.metaDescriptionSuggestions);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/suggest-meta-description\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"count\":3}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/suggest-meta-description\"\npayload = {\n    \"count\": 3\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.metaDescriptionSuggestions)"}]}},"/stories/{storyId}/content/social-post/{platform}":{"post":{"operationId":"generateSocialPost","summary":"Generate Social Post","description":"Platform specific social media posts for the story. Generated posts are also saved on the story for the app's social tab. Costs AI credits.","tags":["Stories"],"x-group":"Content","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"platform","in":"path","required":true,"description":"Target platform","schema":{"type":"string","enum":["facebook","linkedin","x","instagram"],"examples":["linkedin"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","description":"How many suggestions (1 to 5, default 3)"},"targetUrl":{"type":"string","description":"Link to include in the posts"},"additionalInstructions":{"type":"string","description":"Tone or content instructions"}}},"example":{"count":2,"targetUrl":"https://news.example.com/ai-revolution-in-healthcare"}}},"x-fields":[{"name":"count","type":"number","required":false,"description":"How many suggestions (1 to 5, default 3)"},{"name":"targetUrl","type":"string","required":false,"description":"Link to include in the posts"},{"name":"additionalInstructions","type":"string","required":false,"description":"Tone or content instructions"}]},"responses":{"200":{"description":"Posts","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"storyTitle":{"type":"string","description":"Current title"},"platform":{"type":"string","description":"Platform"},"count":{"type":"number","description":"Posts returned"},"targetURL":{"type":["string","null"],"description":"Link included"},"socialMediaPosts":{"type":"array","items":{"type":"string"},"description":"Generated posts"},"model":{"type":"string","description":"LLM model used"},"additionalInstructions":{"type":["string","null"],"description":"Instructions applied"}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","storyTitle":"AI Revolution in Healthcare","platform":"linkedin","count":2,"targetURL":"https://news.example.com/ai-revolution-in-healthcare","socialMediaPosts":["AI is no longer the future of healthcare. It is the present. From diagnostics to treatment planning, here is what European hospitals report. https://news.example.com/ai-revolution-in-healthcare","95% diagnostic accuracy. 40% lower costs. The numbers behind AI in hospitals: https://news.example.com/ai-revolution-in-healthcare"],"model":"claude-sonnet-5","additionalInstructions":null}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"storyTitle","type":"string","description":"Current title"},{"name":"platform","type":"string","description":"Platform"},{"name":"count","type":"number","description":"Posts returned"},{"name":"targetURL","type":"string | null","description":"Link included"},{"name":"socialMediaPosts","type":"string[]","description":"Generated posts"},{"name":"model","type":"string","description":"LLM model used"},{"name":"additionalInstructions","type":"string | null","description":"Instructions applied"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"platform must be one of: facebook, linkedin, x, instagram","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst platform = \"linkedin\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/social-post/${platform}`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"count\": 2,\n    \"targetUrl\": \"https://news.example.com/ai-revolution-in-healthcare\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.socialMediaPosts);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/social-post/linkedin\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"count\":2,\"targetUrl\":\"https://news.example.com/ai-revolution-in-healthcare\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nplatform = \"linkedin\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/social-post/{platform}\"\npayload = {\n    \"count\": 2,\n    \"targetUrl\": \"https://news.example.com/ai-revolution-in-healthcare\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.socialMediaPosts)"}]}},"/stories/{storyId}/content/translate":{"post":{"operationId":"translateStory","summary":"Translate Story","description":"Create a translated copy of the story as a new story linked through `parentId`. The target must be one of the organization's supported languages, given as a full locale code. Translating a language that already exists answers 409. Costs AI credits.","tags":["Stories"],"x-group":"Content","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Target language and options","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"languageCode":{"type":"string","description":"Target language as a full locale code (de-DE, es-ES, pt-BR, ...)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"effort":{"type":"string","description":"Model effort (default normal)","enum":["normal","high"]}},"required":["languageCode"]},"example":{"languageCode":"de-DE"}}},"x-fields":[{"name":"languageCode","type":"string","required":true,"description":"Target language as a full locale code (de-DE, es-ES, pt-BR, ...)"},{"name":"effort","type":"string","required":false,"description":"Model effort (default normal)"}]},"responses":{"200":{"description":"Translation created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"Id of the new translated story"},"translatedStory":{"type":"object","description":"`{ id, title, language, slug }` of the new story. Load it with `GET /stories/{id}`."}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f710","translatedStory":{"id":"68b9b7c2f1a2b3c4d5e6f710","title":"KI-Revolution im Gesundheitswesen","language":"de-DE","slug":"ki-revolution-im-gesundheitswesen"}}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"Id of the new translated story"},{"name":"translatedStory","type":"object","description":"`{ id, title, language, slug }` of the new story. Load it with `GET /stories/{id}`."}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"languageCode is required and must be a full locale code such as de-DE, es-ES or pt-BR","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"Forbidden: the credential cannot act on this organization or Topic Workspace Or: The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"FORBIDDEN","message":"User is not part of the specified organization.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-forbidden"}},"examples":{"first":{"value":{"error":{"code":"FORBIDDEN","message":"User is not part of the specified organization.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-forbidden"}}},"case2":{"value":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"CONFLICT","message":"Translation for de-DE already exists (story 68b9b7c2f1a2b3c4d5e6f710)","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-conflict"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:write"]}],"x-required-scopes":["ai:generate","stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/translate`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"languageCode\": \"de-DE\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.translatedStory);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/translate\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"languageCode\":\"de-DE\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/translate\"\npayload = {\n    \"languageCode\": \"de-DE\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.translatedStory)"}]}},"/stories/{storyId}/content/quality-check":{"post":{"operationId":"qualityCheck","summary":"Run Quality Check","description":"Run the editorial quality check: fact check against the content sources, writing quality, and topic match. The result is stored in the story's history (`GET /stories/{storyId}/content/quality-checks`) and each finding can be applied with the apply-fix endpoint. Stories under `minWords` words fail without calling the model. Costs AI credits.","tags":["Stories"],"x-group":"Quality","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"effort":{"type":"string","description":"Model effort (default normal)","enum":["normal","high"]},"language":{"type":"string","description":"Language of the report (verdict and explanations) as a full locale code (default: the story language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"minWords":{"type":"number","description":"Minimum story length (default 100)"},"factCheck":{"type":"object","description":"`{ enabled, customInstructions }` for the fact check (default enabled)"},"writingQuality":{"type":"object","description":"`{ enabled, customInstructions }` for the writing quality check (default enabled)"},"topicMatch":{"type":"object","description":"`{ enabled, customInstructions }` for the topic match check (default enabled)"},"globalInstructions":{"type":"string","description":"Instructions applied to every check"}}},"example":{"effort":"normal","language":"en-US"}}},"x-fields":[{"name":"effort","type":"string","required":false,"description":"Model effort (default normal)"},{"name":"language","type":"string","required":false,"description":"Language of the report (verdict and explanations) as a full locale code (default: the story language)"},{"name":"minWords","type":"number","required":false,"description":"Minimum story length (default 100)"},{"name":"factCheck","type":"object","required":false,"description":"`{ enabled, customInstructions }` for the fact check (default enabled)"},{"name":"writingQuality","type":"object","required":false,"description":"`{ enabled, customInstructions }` for the writing quality check (default enabled)"},{"name":"topicMatch","type":"object","required":false,"description":"`{ enabled, customInstructions }` for the topic match check (default enabled)"},{"name":"globalInstructions","type":"string","required":false,"description":"Instructions applied to every check"}]},"responses":{"200":{"description":"Quality report","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"overall":{"type":"string","description":"pass, warn or fail"},"summary":{"type":"string","description":"Short verdict in the report language"},"minimumContentOk":{"type":"boolean","description":"Whether the story met `minWords`"},"wordCounts":{"type":"object","properties":{"story":{"type":"number","description":"Story word count"},"minRequired":{"type":"number","description":"Minimum applied"},"sources":{"type":"array","items":{"type":"object"},"description":"`{ id, wordCount }` per content source"}}},"qualityFindings":{"type":"object","properties":{"summary":{"type":"string","description":"Report summary"},"factCheck":{"type":"object","properties":{"findings":{"type":"array","items":{"type":"object"},"description":"`{ field, severity, claim, sourceEvidence, action, suggestedFix }`"}}},"writingQuality":{"type":"object","properties":{"score":{"type":"number","description":"0 to 100"},"findings":{"type":"array","items":{"type":"object"},"description":"`{ field, severity, category, excerpt, rewrite }`"}}},"topicMatch":{"type":"object","description":"`{ expectedTopicKey, matches, modelTopicKey, confidence, recommendedAction, details }`"}}},"findings":{"type":"object","description":"Legacy flat shape: `discrepancies[]`, `writingQuality`, `topicMatch`"}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","wordCounts":{"story":640,"minRequired":100,"sources":[{"id":"68b9b7c2f1a2b3c4d5e6f760","wordCount":1250}]},"minimumContentOk":true,"qualityFindings":{"summary":"Two claims need a source and one paragraph repeats the lead.","factCheck":{"findings":[{"field":"content","severity":"high","claim":"AI diagnostics accuracy reaches 99%","sourceEvidence":"The source reports 95% accuracy in the pilot.","action":"rewrite","suggestedFix":"AI diagnostics accuracy reaches 95%"}]},"writingQuality":{"score":78,"findings":[{"field":"content","severity":"low","category":"repetition","excerpt":"AI is transforming healthcare.","rewrite":""}]},"topicMatch":{"expectedTopicKey":"tech","matches":true,"modelTopicKey":"tech","confidence":92,"recommendedAction":"keep","details":"The story is about AI in healthcare, which matches the technology topic."}},"findings":{"discrepancies":[{"description":"AI diagnostics accuracy reaches 99% (source: 95%)","severity":"high"}],"writingQuality":{"score":78,"issues":["Repeated lead paragraph"],"flags":[]},"topicMatch":{"confidence":92,"details":"Matches the technology topic."}},"overall":"warn","summary":"Two claims need a source and one paragraph repeats the lead."}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"overall","type":"string","description":"pass, warn or fail"},{"name":"summary","type":"string","description":"Short verdict in the report language"},{"name":"minimumContentOk","type":"boolean","description":"Whether the story met `minWords`"},{"name":"wordCounts.story","type":"number","description":"Story word count"},{"name":"wordCounts.minRequired","type":"number","description":"Minimum applied"},{"name":"wordCounts.sources","type":"object[]","description":"`{ id, wordCount }` per content source"},{"name":"qualityFindings.summary","type":"string","description":"Report summary"},{"name":"qualityFindings.factCheck.findings","type":"object[]","description":"`{ field, severity, claim, sourceEvidence, action, suggestedFix }`"},{"name":"qualityFindings.writingQuality.score","type":"number","description":"0 to 100"},{"name":"qualityFindings.writingQuality.findings","type":"object[]","description":"`{ field, severity, category, excerpt, rewrite }`"},{"name":"qualityFindings.topicMatch","type":"object","description":"`{ expectedTopicKey, matches, modelTopicKey, confidence, recommendedAction, details }`"},{"name":"findings","type":"object","description":"Legacy flat shape: `discrepancies[]`, `writingQuality`, `topicMatch`"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"effort must be normal or high","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/quality-check`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"effort\": \"normal\",\n    \"language\": \"en-US\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.qualityFindings);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/quality-check\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"effort\":\"normal\",\"language\":\"en-US\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/quality-check\"\npayload = {\n    \"effort\": \"normal\",\n    \"language\": \"en-US\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.qualityFindings)"}]}},"/stories/{storyId}/content/quality-checks":{"get":{"operationId":"listQualityChecks","summary":"Quality Check History","description":"Every quality check that ran on the story, newest first, including checks run by workflows. The same records are available inline with `include=qualityChecks` on the story endpoints.","tags":["Stories"],"x-group":"Quality","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"History","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"QualityCheck","properties":{"id":{"type":"string","description":"Record id"},"storyId":{"type":"string","description":"Story the check ran on"},"overall":{"type":"string","description":"pass, warn or fail"},"summary":{"type":"string","description":"Short verdict written in the report language"},"qualityFindings":{"type":"object","description":"Modular findings: `factCheck.findings[]`, `writingQuality.score` and `findings[]`, `topicMatch`. Each finding can be applied with `POST /stories/{id}/content/quality-check/apply-fix`"},"discrepancies":{"type":"array","items":{"type":"object"},"description":"Legacy flat list of `{ description, severity }` (kept for older integrations)"},"writingQuality":{"type":"object","description":"Legacy `{ score, issues[], flags[] }`"},"topicMatch":{"type":"object","description":"Legacy `{ confidence, details }`"},"effort":{"type":"string","description":"normal or high"},"checksRun":{"type":"array","items":{"type":"string"},"description":"Which checks were enabled: factCheck, writingQuality, topicMatch"},"provider":{"type":"string","description":"LLM provider used"},"model":{"type":"string","description":"LLM model used"},"credits":{"type":"number","description":"Credits charged"},"createdAt":{"type":"string","description":"ISO 8601 timestamp"}}},"description":"Quality check records"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"},"storyId":{"type":"string","description":"The story"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f740","storyId":"68b9b7c2f1a2b3c4d5e6f700","overall":"warn","summary":"Two claims need a source and one paragraph repeats the lead.","qualityFindings":{"summary":"Two claims need a source and one paragraph repeats the lead.","factCheck":{"findings":[{"field":"content","severity":"high","claim":"AI diagnostics accuracy reaches 99%","sourceEvidence":"The source reports 95% accuracy in the pilot.","action":"rewrite","suggestedFix":"AI diagnostics accuracy reaches 95%"}]},"writingQuality":{"score":78,"findings":[{"field":"content","severity":"low","category":"repetition","excerpt":"AI is transforming healthcare.","rewrite":""}]},"topicMatch":{"expectedTopicKey":"tech","matches":true,"modelTopicKey":"tech","confidence":92,"recommendedAction":"keep","details":"The story is about AI in healthcare, which matches the technology topic."}},"discrepancies":[{"description":"AI diagnostics accuracy reaches 99% (source: 95%)","severity":"high"}],"writingQuality":{"score":78,"issues":["Repeated lead paragraph"],"flags":[]},"topicMatch":{"confidence":92,"details":"Matches the technology topic."},"effort":"normal","checksRun":["factCheck","writingQuality","topicMatch"],"provider":"anthropic","model":"claude-sonnet-5","credits":4,"createdAt":"2026-08-16T09:00:00.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false,"storyId":"68b9b7c2f1a2b3c4d5e6f700"}}}},"x-fields":[{"name":"data","type":"QualityCheck[]","description":"Quality check records"},{"name":"data[].id","type":"string","description":"Record id"},{"name":"data[].storyId","type":"string","description":"Story the check ran on"},{"name":"data[].overall","type":"string","description":"pass, warn or fail"},{"name":"data[].summary","type":"string","description":"Short verdict written in the report language"},{"name":"data[].qualityFindings","type":"object","description":"Modular findings: `factCheck.findings[]`, `writingQuality.score` and `findings[]`, `topicMatch`. Each finding can be applied with `POST /stories/{id}/content/quality-check/apply-fix`"},{"name":"data[].discrepancies","type":"object[]","description":"Legacy flat list of `{ description, severity }` (kept for older integrations)"},{"name":"data[].writingQuality","type":"object","description":"Legacy `{ score, issues[], flags[] }`"},{"name":"data[].topicMatch","type":"object","description":"Legacy `{ confidence, details }`"},{"name":"data[].effort","type":"string","description":"normal or high"},{"name":"data[].checksRun","type":"string[]","description":"Which checks were enabled: factCheck, writingQuality, topicMatch"},{"name":"data[].provider","type":"string","description":"LLM provider used"},{"name":"data[].model","type":"string","description":"LLM model used"},{"name":"data[].credits","type":"number","description":"Credits charged"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 timestamp"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"},{"name":"meta.storyId","type":"string","description":"The story"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/quality-checks`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/quality-checks\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/quality-checks\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{storyId}/content/quality-check/apply-fix":{"post":{"operationId":"applyQualityFix","summary":"Apply Quality Fix","description":"Apply one finding from a quality check to the story. `direct` mode replaces the flagged text without a model; `rewrite` mode lets the model rewrite the field around the finding (costs credits). Topic findings with `recommendedAction: reassign` move the story to the suggested Topic Workspace.","tags":["Stories"],"x-group":"Quality","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"The finding and the mode","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"finding":{"type":"object","description":"A finding object from `qualityFindings`, with a `type` of factCheck, writingQuality or topicMatch"},"mode":{"type":"string","description":"direct (default) or rewrite","enum":["direct","rewrite"]},"effort":{"type":"string","description":"Model effort for rewrite mode","enum":["normal","high"]}},"required":["finding"]},"example":{"finding":{"type":"factCheck","field":"content","severity":"high","claim":"AI diagnostics accuracy reaches 99%","sourceEvidence":"The source reports 95% accuracy.","action":"rewrite","suggestedFix":"AI diagnostics accuracy reaches 95%"},"mode":"direct"}}},"x-fields":[{"name":"finding","type":"object","required":true,"description":"A finding object from `qualityFindings`, with a `type` of factCheck, writingQuality or topicMatch"},{"name":"mode","type":"string","required":false,"description":"direct (default) or rewrite"},{"name":"effort","type":"string","required":false,"description":"Model effort for rewrite mode"}]},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"applied":{"type":"boolean","description":"False when the text was already fixed or the claim was not found"},"method":{"type":"string","description":"direct or rewrite:<provider>"},"field":{"type":"string","description":"Field changed: title, summary, content or bulletPoints[n]"},"before":{"type":"string","description":"Field value before"},"after":{"type":"string","description":"Field value after"}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","applied":true,"method":"direct","field":"content","before":"<p>AI diagnostics accuracy reaches 99% in the pilot.</p>","after":"<p>AI diagnostics accuracy reaches 95% in the pilot.</p>"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"applied","type":"boolean","description":"False when the text was already fixed or the claim was not found"},{"name":"method","type":"string","description":"direct or rewrite:<provider>"},{"name":"field","type":"string","description":"Field changed: title, summary, content or bulletPoints[n]"},{"name":"before","type":"string","description":"Field value before"},{"name":"after","type":"string","description":"Field value after"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"finding is required (a factCheck, writingQuality or topicMatch finding from a quality check)","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:write"]}],"x-required-scopes":["ai:generate","stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/quality-check/apply-fix`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"finding\": {\n      \"type\": \"factCheck\",\n      \"field\": \"content\",\n      \"severity\": \"high\",\n      \"claim\": \"AI diagnostics accuracy reaches 99%\",\n      \"sourceEvidence\": \"The source reports 95% accuracy.\",\n      \"action\": \"rewrite\",\n      \"suggestedFix\": \"AI diagnostics accuracy reaches 95%\"\n    },\n    \"mode\": \"direct\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.after);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/quality-check/apply-fix\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"finding\":{\"type\":\"factCheck\",\"field\":\"content\",\"severity\":\"high\",\"claim\":\"AI diagnostics accuracy reaches 99%\",\"sourceEvidence\":\"The source reports 95% accuracy.\",\"action\":\"rewrite\",\"suggestedFix\":\"AI diagnostics accuracy reaches 95%\"},\"mode\":\"direct\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/quality-check/apply-fix\"\npayload = {\n    \"finding\": {\n        \"type\": \"factCheck\",\n        \"field\": \"content\",\n        \"severity\": \"high\",\n        \"claim\": \"AI diagnostics accuracy reaches 99%\",\n        \"sourceEvidence\": \"The source reports 95% accuracy.\",\n        \"action\": \"rewrite\",\n        \"suggestedFix\": \"AI diagnostics accuracy reaches 95%\"\n    },\n    \"mode\": \"direct\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.after)"}]}},"/stories/{storyId}/content/seo-audit":{"post":{"operationId":"seoAudit","summary":"Run SEO Audit","description":"Audit the story for search and answer engines: on-page checks, keyword placement against the Topic Workspace's SEO keywords, internal link candidates and AEO signals (direct answer, question headings, FAQs, entities). A model drafts concrete fixes unless `skipSuggestions` is true (then the audit is free). The report is stored in the story's history and each `fix` can be applied with the apply-fix endpoint.","tags":["Stories"],"x-group":"Quality","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"effort":{"type":"string","description":"Model effort (default normal)","enum":["normal","high"]},"language":{"type":"string","description":"Language of the report summary as a full locale code (default: the story language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"skipSuggestions":{"type":"string","description":"Skip the model pass and return the deterministic checks only (no credits)","enum":["false","true"]},"seoConfig":{"type":"object","description":"`{ groups: { onPage, keywords, links, aeo: { enabled } }, globalInstructions, maxLinkCandidates }`. All groups default to enabled."}}},"example":{"effort":"normal","language":"en-US"}}},"x-fields":[{"name":"effort","type":"string","required":false,"description":"Model effort (default normal)"},{"name":"language","type":"string","required":false,"description":"Language of the report summary as a full locale code (default: the story language)"},{"name":"skipSuggestions","type":"string","required":false,"description":"Skip the model pass and return the deterministic checks only (no credits)"},{"name":"seoConfig","type":"object","required":false,"description":"`{ groups: { onPage, keywords, links, aeo: { enabled } }, globalInstructions, maxLinkCandidates }`. All groups default to enabled."}]},"responses":{"200":{"description":"Audit report","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"overallScore":{"type":"number","description":"0 to 100"},"overall":{"type":"string","description":"pass, warn or fail"},"summary":{"type":"string","description":"Short summary in the report language"},"credits":{"type":"number","description":"Credits charged"},"report":{"type":"object","properties":{"overallScore":{"type":"number","description":"0 to 100 (also at the top level)"},"overall":{"type":"string","description":"pass, warn or fail (also at the top level)"},"summary":{"type":"string","description":"Short summary in the report language (also at the top level)"},"storySnapshot":{"type":"object","description":"Title, SEO title, meta description, slug, word count, headings, images, locale, topic"},"checks":{"type":"array","items":{"type":"object"},"description":"`{ id, group, status, impact, effort, data?, fix? }`. Apply a `fix` with the apply-fix endpoint."},"keywords":{"type":"array","items":{"type":"object"},"description":"`{ term, primary, bodyCount, present[], suggestion? }` per topic keyword"},"linkCandidates":{"type":"array","items":{"type":"object"},"description":"Stories worth linking: `{ storyId, title, slug, relevance, anchor, paragraphIndex }`"},"aeo":{"type":"object","description":"Answer engine signals: direct answer, question headings, FAQs, entities, cited claims"},"groupScores":{"type":"object","description":"Per group 0 to 100"},"suggestionsAvailable":{"type":"boolean","description":"Whether the model pass ran"}}}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","report":{"storySnapshot":{"title":"AI Revolution in Healthcare","seoTitle":"","metaDescription":"","slug":"ai-revolution-in-healthcare","wordCount":640,"locale":"en-US","topicKey":"tech"},"checks":[{"id":"seoTitlePresent","group":"onPage","status":"fail","impact":"high","effort":"instant","fix":{"kind":"setMetadataField","target":"title","after":"AI Revolution in Healthcare: What Changes in 2026"}},{"id":"metaDescriptionLength","group":"onPage","status":"warn","impact":"medium","effort":"instant"}],"keywords":[{"term":"ai diagnostics","primary":true,"bodyCount":3,"present":["h1","body"],"suggestion":{"placement":"seoTitle","before":"","after":"AI diagnostics"}}],"linkCandidates":[{"storyId":"68b9b7c2f1a2b3c4d5e6f790","title":"Hospitals adopt AI triage","slug":"hospitals-adopt-ai-triage","relevance":81,"inboundLinks":0,"anchor":"AI triage","paragraphIndex":2}],"aeo":{"hasDirectAnswer":false,"proposedDirectAnswer":"AI is now used for diagnostics, triage and treatment planning in most large hospitals.","questionHeadingCount":0,"proposedQuestionHeadings":[],"faqs":[],"entities":[],"citedClaims":2,"totalClaims":5},"groupScores":{"onPage":55,"keywords":60,"links":40,"aeo":30},"overallScore":48,"overall":"warn","summary":"Add an SEO title, a meta description and one internal link.","suggestionsAvailable":true},"overall":"warn","overallScore":48,"summary":"Add an SEO title, a meta description and one internal link.","credits":4}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"overallScore","type":"number","description":"0 to 100"},{"name":"overall","type":"string","description":"pass, warn or fail"},{"name":"summary","type":"string","description":"Short summary in the report language"},{"name":"credits","type":"number","description":"Credits charged"},{"name":"report.overallScore","type":"number","description":"0 to 100 (also at the top level)"},{"name":"report.overall","type":"string","description":"pass, warn or fail (also at the top level)"},{"name":"report.summary","type":"string","description":"Short summary in the report language (also at the top level)"},{"name":"report.storySnapshot","type":"object","description":"Title, SEO title, meta description, slug, word count, headings, images, locale, topic"},{"name":"report.checks","type":"object[]","description":"`{ id, group, status, impact, effort, data?, fix? }`. Apply a `fix` with the apply-fix endpoint."},{"name":"report.keywords","type":"object[]","description":"`{ term, primary, bodyCount, present[], suggestion? }` per topic keyword"},{"name":"report.linkCandidates","type":"object[]","description":"Stories worth linking: `{ storyId, title, slug, relevance, anchor, paragraphIndex }`"},{"name":"report.aeo","type":"object","description":"Answer engine signals: direct answer, question headings, FAQs, entities, cited claims"},{"name":"report.groupScores","type":"object","description":"Per group 0 to 100"},{"name":"report.suggestionsAvailable","type":"boolean","description":"Whether the model pass ran"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"effort must be normal or high","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:read"]}],"x-required-scopes":["ai:generate","stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/seo-audit`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"effort\": \"normal\",\n    \"language\": \"en-US\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.report.checks);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/seo-audit\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"effort\":\"normal\",\"language\":\"en-US\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/seo-audit\"\npayload = {\n    \"effort\": \"normal\",\n    \"language\": \"en-US\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.report.checks)"}]}},"/stories/{storyId}/content/seo-audits":{"get":{"operationId":"listSeoAudits","summary":"SEO Audit History","description":"Every SEO audit that ran on the story, newest first, with the full report. Also available inline with `include=seoAudits` on the story endpoints.","tags":["Stories"],"x-group":"Quality","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"History","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"SeoAudit","properties":{"id":{"type":"string","description":"Record id"},"storyId":{"type":"string","description":"Audited story"},"overallScore":{"type":"number","description":"0 to 100 readiness score"},"overall":{"type":"string","description":"pass, warn or fail"},"summary":{"type":"string","description":"Short summary written in the report language"},"groupScores":{"type":"object","description":"Per group 0 to 100 scores: onPage, keywords, links, aeo"},"checksRun":{"type":"array","items":{"type":"string"},"description":"Groups that were enabled"},"report":{"type":"object","description":"Full report: `storySnapshot`, `checks[]` (each may carry a `fix` you can apply with `POST /stories/{id}/content/seo-audit/apply-fix`), `keywords[]`, `linkCandidates[]`, `aeo`"},"effort":{"type":"string","description":"normal or high"},"provider":{"type":"string","description":"LLM provider used for suggestions"},"model":{"type":"string","description":"LLM model used"},"credits":{"type":"number","description":"Credits charged (0 when suggestions were skipped)"},"createdAt":{"type":"string","description":"ISO 8601 timestamp"}}},"description":"SEO audit records"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"},"storyId":{"type":"string","description":"The story"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f750","storyId":"68b9b7c2f1a2b3c4d5e6f700","overallScore":48,"overall":"warn","summary":"Add an SEO title, a meta description and one internal link.","groupScores":{"onPage":55,"keywords":60,"links":40,"aeo":30},"checksRun":["onPage","keywords","links","aeo"],"report":{"storySnapshot":{"title":"AI Revolution in Healthcare","seoTitle":"","metaDescription":"","slug":"ai-revolution-in-healthcare","wordCount":640,"locale":"en-US","topicKey":"tech"},"checks":[{"id":"seoTitlePresent","group":"onPage","status":"fail","impact":"high","effort":"instant","fix":{"kind":"setMetadataField","target":"title","after":"AI Revolution in Healthcare: What Changes in 2026"}},{"id":"metaDescriptionLength","group":"onPage","status":"warn","impact":"medium","effort":"instant"}],"keywords":[{"term":"ai diagnostics","primary":true,"bodyCount":3,"present":["h1","body"],"suggestion":{"placement":"seoTitle","before":"","after":"AI diagnostics"}}],"linkCandidates":[{"storyId":"68b9b7c2f1a2b3c4d5e6f790","title":"Hospitals adopt AI triage","slug":"hospitals-adopt-ai-triage","relevance":81,"inboundLinks":0,"anchor":"AI triage","paragraphIndex":2}],"aeo":{"hasDirectAnswer":false,"proposedDirectAnswer":"AI is now used for diagnostics, triage and treatment planning in most large hospitals.","questionHeadingCount":0,"proposedQuestionHeadings":[],"faqs":[],"entities":[],"citedClaims":2,"totalClaims":5},"groupScores":{"onPage":55,"keywords":60,"links":40,"aeo":30},"overallScore":48,"overall":"warn","summary":"Add an SEO title, a meta description and one internal link.","suggestionsAvailable":true},"effort":"normal","provider":"anthropic","model":"claude-sonnet-5","credits":4,"createdAt":"2026-08-16T09:30:00.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false,"storyId":"68b9b7c2f1a2b3c4d5e6f700"}}}},"x-fields":[{"name":"data","type":"SeoAudit[]","description":"SEO audit records"},{"name":"data[].id","type":"string","description":"Record id"},{"name":"data[].storyId","type":"string","description":"Audited story"},{"name":"data[].overallScore","type":"number","description":"0 to 100 readiness score"},{"name":"data[].overall","type":"string","description":"pass, warn or fail"},{"name":"data[].summary","type":"string","description":"Short summary written in the report language"},{"name":"data[].groupScores","type":"object","description":"Per group 0 to 100 scores: onPage, keywords, links, aeo"},{"name":"data[].checksRun","type":"string[]","description":"Groups that were enabled"},{"name":"data[].report","type":"object","description":"Full report: `storySnapshot`, `checks[]` (each may carry a `fix` you can apply with `POST /stories/{id}/content/seo-audit/apply-fix`), `keywords[]`, `linkCandidates[]`, `aeo`"},{"name":"data[].effort","type":"string","description":"normal or high"},{"name":"data[].provider","type":"string","description":"LLM provider used for suggestions"},{"name":"data[].model","type":"string","description":"LLM model used"},{"name":"data[].credits","type":"number","description":"Credits charged (0 when suggestions were skipped)"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 timestamp"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"},{"name":"meta.storyId","type":"string","description":"The story"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["stories:read"],"missing":["stories:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["stories:read"]}],"x-required-scopes":["stories:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/seo-audits`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/seo-audits\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/seo-audits\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/stories/{storyId}/content/seo-audit/apply-fix":{"post":{"operationId":"applySeoFix","summary":"Apply SEO Fix","description":"Apply one `fix` from an audit report. Fix kinds: setMetadataField, setSlug, setImageAlt, replaceText, insertParagraphTop, insertHeading, insertLink, setFaqs, setEntities. `direct` mode applies the fix without a model and answers 422 when the text changed since the audit; retry with `rewrite` mode to let the model apply it (costs credits).","tags":["Stories"],"x-group":"Quality","parameters":[{"name":"storyId","in":"path","required":true,"description":"The story id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f700"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"The fix and the mode","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fix":{"type":"object","description":"A `fix` object from `report.checks[]` or `report.keywords[].suggestion`: `{ kind, target?, before?, after, paragraphIndex?, payload? }`"},"mode":{"type":"string","description":"direct (default) or rewrite","enum":["direct","rewrite"]}},"required":["fix"]},"example":{"fix":{"kind":"setMetadataField","target":"title","after":"AI in Healthcare 2026: Diagnostics, Costs and Results"},"mode":"direct"}}},"x-fields":[{"name":"fix","type":"object","required":true,"description":"A `fix` object from `report.checks[]` or `report.keywords[].suggestion`: `{ kind, target?, before?, after, paragraphIndex?, payload? }`"},{"name":"mode","type":"string","required":false,"description":"direct (default) or rewrite"}]},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"storyId":{"type":"string","description":"The story"},"applied":{"type":"boolean","description":"Whether the story changed"},"method":{"type":"string","description":"direct or rewrite:<provider>"},"field":{"type":"string","description":"Field changed, e.g. metadata.title, slug, content"},"before":{"type":"string","description":"Value before"},"after":{"type":"string","description":"Value after"}}},"example":{"success":true,"storyId":"68b9b7c2f1a2b3c4d5e6f700","applied":true,"method":"direct","field":"metadata.title","before":"","after":"AI in Healthcare 2026: Diagnostics, Costs and Results"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"storyId","type":"string","description":"The story"},{"name":"applied","type":"boolean","description":"Whether the story changed"},{"name":"method","type":"string","description":"direct or rewrite:<provider>"},{"name":"field","type":"string","description":"Field changed, e.g. metadata.title, slug, content"},{"name":"before","type":"string","description":"Value before"},{"name":"after","type":"string","description":"Value after"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"fix is required (a fix object from an SEO audit report, with kind and after)","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Story not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Story not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"422":{"description":"The story text drifted since the audit; retry with mode rewrite","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Text to replace was not found in content","details":{"hint":"Retry with mode=rewrite for an LLM-based fix"},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}}},"security":[{"apiKey":["ai:generate","stories:write"]}],"x-required-scopes":["ai:generate","stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const storyId = \"68b9b7c2f1a2b3c4d5e6f700\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/stories/${storyId}/content/seo-audit/apply-fix`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"fix\": {\n      \"kind\": \"setMetadataField\",\n      \"target\": \"title\",\n      \"after\": \"AI in Healthcare 2026: Diagnostics, Costs and Results\"\n    },\n    \"mode\": \"direct\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.after);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/stories/68b9b7c2f1a2b3c4d5e6f700/content/seo-audit/apply-fix\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"fix\":{\"kind\":\"setMetadataField\",\"target\":\"title\",\"after\":\"AI in Healthcare 2026: Diagnostics, Costs and Results\"},\"mode\":\"direct\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nstory_id = \"68b9b7c2f1a2b3c4d5e6f700\"\nurl = f\"https://client-api.news-factory.app/v1/stories/{story_id}/content/seo-audit/apply-fix\"\npayload = {\n    \"fix\": {\n        \"kind\": \"setMetadataField\",\n        \"target\": \"title\",\n        \"after\": \"AI in Healthcare 2026: Diagnostics, Costs and Results\"\n    },\n    \"mode\": \"direct\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result.after)"}]}},"/content-sources":{"get":{"operationId":"listContentSources","summary":"List Content Sources","description":"Page through the Topic Workspace's content sources: articles saved from feeds, web pages, uploaded documents and images. `usedIn` tells how many stories were written from each source.","tags":["Content Sources"],"parameters":[{"name":"sourceType","in":"query","required":false,"description":"Only this kind of source","schema":{"type":"string","enum":["rss","website","image","document"],"examples":["rss"]}},{"name":"language","in":"query","required":false,"description":"Only sources in this language (full locale code)","schema":{"type":"string","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"],"examples":["en-US"]}},{"name":"flagged","in":"query","required":false,"description":"Only flagged (true) or unflagged (false) sources","schema":{"type":"boolean","examples":[false]}},{"name":"processed","in":"query","required":false,"description":"Only sources that were (true) or were not (false) analysed","schema":{"type":"boolean","examples":[true]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default createdAt DESC). Allowed fields: createdAt, pubDate, updatedAt, title.","schema":{"type":"string","enum":["createdAt DESC","createdAt ASC","pubDate DESC","updatedAt DESC","title ASC"],"examples":["createdAt DESC"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"A page of content sources","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"ContentSource","properties":{"id":{"type":"string","description":"Unique content source identifier"},"title":{"type":"string","description":"Title"},"link":{"type":"string","description":"Source URL (unique per Topic Workspace)"},"sourceType":{"type":"string","description":"rss, website, image or document"},"summary":{"type":"string","description":"Short summary or excerpt"},"author":{"type":["string","null"],"description":"Author of the source"},"thumbnail":{"type":["string","null"],"description":"Thumbnail image URL"},"language":{"type":"string","description":"Full locale code of the source text"},"topicKey":{"type":"string","description":"Topic Workspace the source belongs to"},"pubDate":{"type":"string","description":"ISO 8601 publication date"},"scraped":{"type":["string","null"],"description":"When the page was fetched and parsed"},"processed":{"type":["string","null"],"description":"When AI analysis (summary, embeddings) ran"},"repurposed":{"type":["string","null"],"description":"When a story was last written from it"},"flagged":{"type":"boolean","description":"Flagged by a user or a workflow"},"flagReason":{"type":["string","null"],"description":"Why it was flagged"},"sourceMeta":{"type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},"usedIn":{"type":"number","description":"How many stories reference this source"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"}}},"description":"Content sources"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f760","title":"Hospitals report 40% cost reduction with AI triage","link":"https://techcrunch.com/2026/07/30/ai-healthcare/","sourceType":"rss","summary":"Pilot programmes across Europe report faster triage and lower costs.","author":"Sarah Perez","thumbnail":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","language":"en-US","topicKey":"tech","pubDate":"2026-07-30T06:00:00.000Z","scraped":"2026-07-30T07:10:00.000Z","processed":"2026-07-30T07:12:00.000Z","repurposed":"2026-08-15T10:30:00.000Z","flagged":false,"flagReason":null,"sourceMeta":{"feedId":"68b9b7c2f1a2b3c4d5e6f770","feedName":"TechCrunch","feedUrl":"https://techcrunch.com/feed/","feedIcon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico"},"usedIn":1,"createdAt":"2026-07-30T07:00:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false}}}},"x-fields":[{"name":"data","type":"ContentSource[]","description":"Content sources"},{"name":"data[].id","type":"string","description":"Unique content source identifier"},{"name":"data[].title","type":"string","description":"Title"},{"name":"data[].link","type":"string","description":"Source URL (unique per Topic Workspace)"},{"name":"data[].sourceType","type":"string","description":"rss, website, image or document"},{"name":"data[].summary","type":"string","description":"Short summary or excerpt"},{"name":"data[].author","type":"string | null","description":"Author of the source"},{"name":"data[].thumbnail","type":"string | null","description":"Thumbnail image URL"},{"name":"data[].language","type":"string","description":"Full locale code of the source text"},{"name":"data[].topicKey","type":"string","description":"Topic Workspace the source belongs to"},{"name":"data[].pubDate","type":"string","description":"ISO 8601 publication date"},{"name":"data[].scraped","type":"string | null","description":"When the page was fetched and parsed"},{"name":"data[].processed","type":"string | null","description":"When AI analysis (summary, embeddings) ran"},{"name":"data[].repurposed","type":"string | null","description":"When a story was last written from it"},{"name":"data[].flagged","type":"boolean","description":"Flagged by a user or a workflow"},{"name":"data[].flagReason","type":"string | null","description":"Why it was flagged"},{"name":"data[].sourceMeta","type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},{"name":"data[].usedIn","type":"number","description":"How many stories reference this source"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"sourceType must be one of: rss, website, image, document","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope content-sources:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["content-sources:read"],"missing":["content-sources:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["content-sources:read"]}],"x-required-scopes":["content-sources:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/content-sources?sourceType=rss&limit=20`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/content-sources?sourceType=rss&limit=20\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/content-sources\"\nparams = {\n    \"sourceType\": \"rss\",\n    \"limit\": 20\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"post":{"operationId":"createContentSource","summary":"Create Content Source","description":"Register an article, page or document as a source in the Topic Workspace. Scrape it afterwards to fetch and parse the page, then repurpose it into a story.","tags":["Content Sources"],"parameters":[{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Content source data","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Title"},"link":{"type":"string","description":"Absolute URL of the article or page. Unique per Topic Workspace; a duplicate answers 409 with the existing id."},"sourceType":{"type":"string","description":"rss, website, image or document","enum":["rss","website","image","document"]},"summary":{"type":"string","description":"Summary or excerpt"},"content":{"type":"string","description":"Full text when you already have it (otherwise use the scrape endpoint)"},"author":{"type":"string","description":"Author"},"thumbnail":{"type":"string","description":"Thumbnail URL"},"language":{"type":"string","description":"Full locale code of the source text","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"pubDate":{"type":"string","format":"date-time","description":"Publication date (default now)"},"topicKey":{"type":"string","description":"Topic Workspace to save the source in (default: the key's Topic Workspace)"}},"required":["title","link","sourceType"]},"example":{"title":"Hospitals report 40% cost reduction with AI triage","link":"https://techcrunch.com/2026/07/30/ai-healthcare/","sourceType":"website","summary":"Pilot programmes across Europe report faster triage and lower costs.","language":"en-US"}}},"x-fields":[{"name":"title","type":"string","required":true,"description":"Title"},{"name":"link","type":"string","required":true,"description":"Absolute URL of the article or page. Unique per Topic Workspace; a duplicate answers 409 with the existing id."},{"name":"sourceType","type":"string","required":true,"description":"rss, website, image or document"},{"name":"summary","type":"string","required":false,"description":"Summary or excerpt"},{"name":"content","type":"string","required":false,"description":"Full text when you already have it (otherwise use the scrape endpoint)"},{"name":"author","type":"string","required":false,"description":"Author"},{"name":"thumbnail","type":"string","required":false,"description":"Thumbnail URL"},{"name":"language","type":"string","required":false,"description":"Full locale code of the source text"},{"name":"pubDate","type":"string (ISO 8601)","required":false,"description":"Publication date (default now)"},{"name":"topicKey","type":"string","required":false,"description":"Topic Workspace to save the source in (default: the key's Topic Workspace)"}]},"responses":{"201":{"description":"Content source created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","x-type":"ContentSource","description":"The created source","properties":{"id":{"type":"string","description":"Unique content source identifier"},"title":{"type":"string","description":"Title"},"link":{"type":"string","description":"Source URL (unique per Topic Workspace)"},"sourceType":{"type":"string","description":"rss, website, image or document"},"summary":{"type":"string","description":"Short summary or excerpt"},"author":{"type":["string","null"],"description":"Author of the source"},"thumbnail":{"type":["string","null"],"description":"Thumbnail image URL"},"language":{"type":"string","description":"Full locale code of the source text"},"topicKey":{"type":"string","description":"Topic Workspace the source belongs to"},"pubDate":{"type":"string","description":"ISO 8601 publication date"},"scraped":{"type":["string","null"],"description":"When the page was fetched and parsed"},"processed":{"type":["string","null"],"description":"When AI analysis (summary, embeddings) ran"},"repurposed":{"type":["string","null"],"description":"When a story was last written from it"},"flagged":{"type":"boolean","description":"Flagged by a user or a workflow"},"flagReason":{"type":["string","null"],"description":"Why it was flagged"},"sourceMeta":{"type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},"usedIn":{"type":"number","description":"How many stories reference this source"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f760","title":"Hospitals report 40% cost reduction with AI triage","link":"https://techcrunch.com/2026/07/30/ai-healthcare/","sourceType":"website","summary":"Pilot programmes across Europe report faster triage and lower costs.","author":"Sarah Perez","thumbnail":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","language":"en-US","topicKey":"tech","pubDate":"2026-07-30T06:00:00.000Z","scraped":null,"processed":null,"repurposed":null,"flagged":false,"flagReason":null,"sourceMeta":{},"usedIn":0,"createdAt":"2026-07-30T07:00:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"},"message":"Content source created successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"ContentSource","description":"The created source"},{"name":"data.id","type":"string","description":"Unique content source identifier"},{"name":"data.title","type":"string","description":"Title"},{"name":"data.link","type":"string","description":"Source URL (unique per Topic Workspace)"},{"name":"data.sourceType","type":"string","description":"rss, website, image or document"},{"name":"data.summary","type":"string","description":"Short summary or excerpt"},{"name":"data.author","type":"string | null","description":"Author of the source"},{"name":"data.thumbnail","type":"string | null","description":"Thumbnail image URL"},{"name":"data.language","type":"string","description":"Full locale code of the source text"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the source belongs to"},{"name":"data.pubDate","type":"string","description":"ISO 8601 publication date"},{"name":"data.scraped","type":"string | null","description":"When the page was fetched and parsed"},{"name":"data.processed","type":"string | null","description":"When AI analysis (summary, embeddings) ran"},{"name":"data.repurposed","type":"string | null","description":"When a story was last written from it"},{"name":"data.flagged","type":"boolean","description":"Flagged by a user or a workflow"},{"name":"data.flagReason","type":"string | null","description":"Why it was flagged"},{"name":"data.sourceMeta","type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},{"name":"data.usedIn","type":"number","description":"How many stories reference this source"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"link is required","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope content-sources:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["content-sources:write"],"missing":["content-sources:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"CONFLICT","message":"Content source already exists","details":{"existingContentSourceId":"68b9b7c2f1a2b3c4d5e6f760"},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-conflict"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["content-sources:write"]}],"x-required-scopes":["content-sources:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/content-sources`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"title\": \"Hospitals report 40% cost reduction with AI triage\",\n    \"link\": \"https://techcrunch.com/2026/07/30/ai-healthcare/\",\n    \"sourceType\": \"website\",\n    \"summary\": \"Pilot programmes across Europe report faster triage and lower costs.\",\n    \"language\": \"en-US\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/content-sources\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\":\"Hospitals report 40% cost reduction with AI triage\",\"link\":\"https://techcrunch.com/2026/07/30/ai-healthcare/\",\"sourceType\":\"website\",\"summary\":\"Pilot programmes across Europe report faster triage and lower costs.\",\"language\":\"en-US\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/content-sources\"\npayload = {\n    \"title\": \"Hospitals report 40% cost reduction with AI triage\",\n    \"link\": \"https://techcrunch.com/2026/07/30/ai-healthcare/\",\n    \"sourceType\": \"website\",\n    \"summary\": \"Pilot programmes across Europe report faster triage and lower costs.\",\n    \"language\": \"en-US\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/content-sources/{id}":{"get":{"operationId":"getContentSource","summary":"Get Content Source","description":"One content source with its scraped text, images, catalogue feed and the stories written from it.","tags":["Content Sources"],"parameters":[{"name":"id","in":"path","required":true,"description":"The content source id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f760"]}},{"name":"full","in":"query","required":false,"description":"Also return the raw HTML (`rawSource`) of scraped pages","schema":{"type":"boolean","examples":[false]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The content source","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"ContentSource","description":"The content source","properties":{"id":{"type":"string","description":"Unique content source identifier"},"title":{"type":"string","description":"Title"},"link":{"type":"string","description":"Source URL (unique per Topic Workspace)"},"sourceType":{"type":"string","description":"rss, website, image or document"},"summary":{"type":"string","description":"Short summary or excerpt"},"author":{"type":["string","null"],"description":"Author of the source"},"thumbnail":{"type":["string","null"],"description":"Thumbnail image URL"},"language":{"type":"string","description":"Full locale code of the source text"},"topicKey":{"type":"string","description":"Topic Workspace the source belongs to"},"pubDate":{"type":"string","description":"ISO 8601 publication date"},"scraped":{"type":["string","null"],"description":"When the page was fetched and parsed"},"processed":{"type":["string","null"],"description":"When AI analysis (summary, embeddings) ran"},"repurposed":{"type":["string","null"],"description":"When a story was last written from it"},"flagged":{"type":"boolean","description":"Flagged by a user or a workflow"},"flagReason":{"type":["string","null"],"description":"Why it was flagged"},"sourceMeta":{"type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},"usedIn":{"type":"number","description":"How many stories reference this source"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"content":{"type":["string","null"],"description":"Full text when available (RSS body or document text)"},"parsedSource":{"type":["object","null"],"description":"Scrape result: `title`, `contentHTML`, `contentText`, `mainImage`, `excerpt`, `wordCount`"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"Images attached to the source as public Image objects"},"feed":{"type":["object","null"],"x-type":"Feed","description":"The catalogue feed for RSS articles"},"stories":{"type":"array","items":{"type":"object"},"description":"Stories written from this source: `{ id, title, createdAt }`"}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f760","title":"Hospitals report 40% cost reduction with AI triage","link":"https://techcrunch.com/2026/07/30/ai-healthcare/","sourceType":"rss","summary":"Pilot programmes across Europe report faster triage and lower costs.","author":"Sarah Perez","thumbnail":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","language":"en-US","topicKey":"tech","pubDate":"2026-07-30T06:00:00.000Z","scraped":"2026-07-30T07:10:00.000Z","processed":"2026-07-30T07:12:00.000Z","repurposed":"2026-08-15T10:30:00.000Z","flagged":false,"flagReason":null,"sourceMeta":{"feedId":"68b9b7c2f1a2b3c4d5e6f770","feedName":"TechCrunch","feedUrl":"https://techcrunch.com/feed/","feedIcon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico"},"usedIn":1,"createdAt":"2026-07-30T07:00:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z","content":null,"parsedSource":{"title":"Hospitals report 40% cost reduction with AI triage","contentHTML":"<p>Pilot programmes across Europe...</p>","contentText":"Pilot programmes across Europe...","mainImage":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","excerpt":"Pilot programmes across Europe report faster triage and lower costs.","wordCount":1250},"images":[{"id":"68b9b7c2f1a2b3c4d5e6f701","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","originalUrl":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"main","origin":"source","aiGenerated":false,"generation":null,"sourceType":"story","parentId":null,"title":"AI Revolution in Healthcare","description":null,"altText":null,"caption":null,"author":null,"source":"https://techcrunch.com/2026/07/30/ai-healthcare/","license":null,"creditLine":null,"tags":[],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"}],"feed":{"id":"68b9b7c2f1a2b3c4d5e6f770","name":"TechCrunch","url":"https://techcrunch.com/feed/","favicon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico"},"stories":[{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","createdAt":"2026-08-15T10:30:00.000Z"}]}}}},"x-fields":[{"name":"data","type":"ContentSource","description":"The content source"},{"name":"data.id","type":"string","description":"Unique content source identifier"},{"name":"data.title","type":"string","description":"Title"},{"name":"data.link","type":"string","description":"Source URL (unique per Topic Workspace)"},{"name":"data.sourceType","type":"string","description":"rss, website, image or document"},{"name":"data.summary","type":"string","description":"Short summary or excerpt"},{"name":"data.author","type":"string | null","description":"Author of the source"},{"name":"data.thumbnail","type":"string | null","description":"Thumbnail image URL"},{"name":"data.language","type":"string","description":"Full locale code of the source text"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the source belongs to"},{"name":"data.pubDate","type":"string","description":"ISO 8601 publication date"},{"name":"data.scraped","type":"string | null","description":"When the page was fetched and parsed"},{"name":"data.processed","type":"string | null","description":"When AI analysis (summary, embeddings) ran"},{"name":"data.repurposed","type":"string | null","description":"When a story was last written from it"},{"name":"data.flagged","type":"boolean","description":"Flagged by a user or a workflow"},{"name":"data.flagReason","type":"string | null","description":"Why it was flagged"},{"name":"data.sourceMeta","type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},{"name":"data.usedIn","type":"number","description":"How many stories reference this source"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.content","type":"string | null","description":"Full text when available (RSS body or document text)"},{"name":"data.parsedSource","type":"object | null","description":"Scrape result: `title`, `contentHTML`, `contentText`, `mainImage`, `excerpt`, `wordCount`"},{"name":"data.images","type":"Image[]","description":"Images attached to the source as public Image objects"},{"name":"data.feed","type":"Feed | null","description":"The catalogue feed for RSS articles"},{"name":"data.stories","type":"object[]","description":"Stories written from this source: `{ id, title, createdAt }`"},{"name":"data.images[].id","type":"string","description":"Unique image identifier"},{"name":"data.images[].url","type":"string","description":"Public URL of the image file"},{"name":"data.images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.images[].width","type":"number","description":"Width in pixels"},{"name":"data.images[].height","type":"number","description":"Height in pixels"},{"name":"data.images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data.images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.images[].title","type":"string | null","description":"Image title"},{"name":"data.images[].description","type":"string | null","description":"Image description"},{"name":"data.images[].altText","type":"string | null","description":"Alt text"},{"name":"data.images[].caption","type":"string | null","description":"Caption"},{"name":"data.images[].author","type":"string | null","description":"Author or photographer"},{"name":"data.images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.images[].license","type":"string | null","description":"License (stock images)"},{"name":"data.images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope content-sources:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["content-sources:read"],"missing":["content-sources:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Content source not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Content source not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["content-sources:read"]}],"x-required-scopes":["content-sources:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f760\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/content-sources/${id}`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/content-sources/68b9b7c2f1a2b3c4d5e6f760\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f760\"\nurl = f\"https://client-api.news-factory.app/v1/content-sources/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"patch":{"operationId":"updateContentSource","summary":"Update Content Source","description":"Update editable fields (title, summary, content, author, thumbnail, language, pubDate, flagged, flagReason) and return the fresh record.","tags":["Content Sources"],"parameters":[{"name":"id","in":"path","required":true,"description":"The content source id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f760"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Fields to update (at least one)","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Title"},"summary":{"type":"string","description":"Summary or excerpt"},"content":{"type":"string","description":"Full text when you already have it (otherwise use the scrape endpoint)"},"author":{"type":"string","description":"Author"},"thumbnail":{"type":"string","description":"Thumbnail URL"},"language":{"type":"string","description":"Full locale code of the source text","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"pubDate":{"type":"string","format":"date-time","description":"Publication date (default now)"},"flagged":{"type":"boolean","description":"Flag the source so workflows skip it"},"flagReason":{"type":"string","description":"Why it is flagged"}}},"example":{"flagged":true,"flagReason":"Duplicate of another source"}}},"x-fields":[{"name":"title","type":"string","required":false,"description":"Title"},{"name":"summary","type":"string","required":false,"description":"Summary or excerpt"},{"name":"content","type":"string","required":false,"description":"Full text when you already have it (otherwise use the scrape endpoint)"},{"name":"author","type":"string","required":false,"description":"Author"},{"name":"thumbnail","type":"string","required":false,"description":"Thumbnail URL"},{"name":"language","type":"string","required":false,"description":"Full locale code of the source text"},{"name":"pubDate","type":"string (ISO 8601)","required":false,"description":"Publication date (default now)"},{"name":"flagged","type":"boolean","required":false,"description":"Flag the source so workflows skip it"},{"name":"flagReason","type":"string","required":false,"description":"Why it is flagged"}]},"responses":{"200":{"description":"Content source updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","x-type":"ContentSource","description":"The updated source","properties":{"id":{"type":"string","description":"Unique content source identifier"},"title":{"type":"string","description":"Title"},"link":{"type":"string","description":"Source URL (unique per Topic Workspace)"},"sourceType":{"type":"string","description":"rss, website, image or document"},"summary":{"type":"string","description":"Short summary or excerpt"},"author":{"type":["string","null"],"description":"Author of the source"},"thumbnail":{"type":["string","null"],"description":"Thumbnail image URL"},"language":{"type":"string","description":"Full locale code of the source text"},"topicKey":{"type":"string","description":"Topic Workspace the source belongs to"},"pubDate":{"type":"string","description":"ISO 8601 publication date"},"scraped":{"type":["string","null"],"description":"When the page was fetched and parsed"},"processed":{"type":["string","null"],"description":"When AI analysis (summary, embeddings) ran"},"repurposed":{"type":["string","null"],"description":"When a story was last written from it"},"flagged":{"type":"boolean","description":"Flagged by a user or a workflow"},"flagReason":{"type":["string","null"],"description":"Why it was flagged"},"sourceMeta":{"type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},"usedIn":{"type":"number","description":"How many stories reference this source"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"content":{"type":["string","null"],"description":"Full text when available (RSS body or document text)"},"parsedSource":{"type":["object","null"],"description":"Scrape result: `title`, `contentHTML`, `contentText`, `mainImage`, `excerpt`, `wordCount`"},"images":{"type":"array","items":{"type":"object","x-type":"Image","properties":{"id":{"type":"string","description":"Unique image identifier"},"url":{"type":"string","description":"Public URL of the image file"},"s3Url":{"type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"},"format":{"type":"string","description":"File format (PNG, JPEG, WEBP)"},"contentType":{"type":"string","description":"MIME type of the image"},"fileSize":{"type":["number","null"],"description":"File size in bytes"},"imageType":{"type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},"origin":{"type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},"aiGenerated":{"type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},"generation":{"type":["object","null"],"description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},"sourceType":{"type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},"parentId":{"type":["string","null"],"description":"Id of the image this one was cropped or resized from"},"originalUrl":{"type":["string","null"],"description":"Where the file was fetched from (article images, URL imports)"},"title":{"type":["string","null"],"description":"Image title"},"description":{"type":["string","null"],"description":"Image description"},"altText":{"type":["string","null"],"description":"Alt text"},"caption":{"type":["string","null"],"description":"Caption"},"author":{"type":["string","null"],"description":"Author or photographer"},"source":{"type":["string","null"],"description":"Attribution source: article URL, stock provider id, or AI provider id"},"license":{"type":["string","null"],"description":"License (stock images)"},"creditLine":{"type":["string","null"],"description":"Credit line to display (stock images)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags associated with the image"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":["string","null"],"description":"ISO 8601 timestamp of the last update"}}},"description":"Images attached to the source as public Image objects"},"feed":{"type":["object","null"],"x-type":"Feed","description":"The catalogue feed for RSS articles"},"stories":{"type":"array","items":{"type":"object"},"description":"Stories written from this source: `{ id, title, createdAt }`"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f760","title":"Hospitals report 40% cost reduction with AI triage","link":"https://techcrunch.com/2026/07/30/ai-healthcare/","sourceType":"rss","summary":"Pilot programmes across Europe report faster triage and lower costs.","author":"Sarah Perez","thumbnail":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","language":"en-US","topicKey":"tech","pubDate":"2026-07-30T06:00:00.000Z","scraped":"2026-07-30T07:10:00.000Z","processed":"2026-07-30T07:12:00.000Z","repurposed":"2026-08-15T10:30:00.000Z","flagged":true,"flagReason":"Duplicate of another source","sourceMeta":{"feedId":"68b9b7c2f1a2b3c4d5e6f770","feedName":"TechCrunch","feedUrl":"https://techcrunch.com/feed/","feedIcon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico"},"usedIn":1,"createdAt":"2026-07-30T07:00:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z","content":null,"parsedSource":{"title":"Hospitals report 40% cost reduction with AI triage","contentHTML":"<p>Pilot programmes across Europe...</p>","contentText":"Pilot programmes across Europe...","mainImage":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","excerpt":"Pilot programmes across Europe report faster triage and lower costs.","wordCount":1250},"images":[{"id":"68b9b7c2f1a2b3c4d5e6f701","url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","s3Url":"https://cdn.news-factory.app/stories/68b9b7c2f1a2b3c4d5e6f700/gettyimages-1445867611.jpg","originalUrl":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","width":1024,"height":683,"format":"JPEG","contentType":"image/jpeg","fileSize":214532,"imageType":"main","origin":"source","aiGenerated":false,"generation":null,"sourceType":"story","parentId":null,"title":"AI Revolution in Healthcare","description":null,"altText":null,"caption":null,"author":null,"source":"https://techcrunch.com/2026/07/30/ai-healthcare/","license":null,"creditLine":null,"tags":[],"createdAt":"2026-08-15T10:30:00.000Z","updatedAt":"2026-08-15T10:30:00.000Z"}],"feed":{"id":"68b9b7c2f1a2b3c4d5e6f770","name":"TechCrunch","url":"https://techcrunch.com/feed/","favicon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico"},"stories":[{"id":"68b9b7c2f1a2b3c4d5e6f700","title":"AI Revolution in Healthcare","createdAt":"2026-08-15T10:30:00.000Z"}]},"message":"Content source updated successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data","type":"ContentSource","description":"The updated source"},{"name":"data.id","type":"string","description":"Unique content source identifier"},{"name":"data.title","type":"string","description":"Title"},{"name":"data.link","type":"string","description":"Source URL (unique per Topic Workspace)"},{"name":"data.sourceType","type":"string","description":"rss, website, image or document"},{"name":"data.summary","type":"string","description":"Short summary or excerpt"},{"name":"data.author","type":"string | null","description":"Author of the source"},{"name":"data.thumbnail","type":"string | null","description":"Thumbnail image URL"},{"name":"data.language","type":"string","description":"Full locale code of the source text"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the source belongs to"},{"name":"data.pubDate","type":"string","description":"ISO 8601 publication date"},{"name":"data.scraped","type":"string | null","description":"When the page was fetched and parsed"},{"name":"data.processed","type":"string | null","description":"When AI analysis (summary, embeddings) ran"},{"name":"data.repurposed","type":"string | null","description":"When a story was last written from it"},{"name":"data.flagged","type":"boolean","description":"Flagged by a user or a workflow"},{"name":"data.flagReason","type":"string | null","description":"Why it was flagged"},{"name":"data.sourceMeta","type":"object","description":"Source specific metadata: `feedId`, `feedName`, `feedUrl`, `feedIcon` for RSS articles, `documentType` or `imageType` for uploads"},{"name":"data.usedIn","type":"number","description":"How many stories reference this source"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.content","type":"string | null","description":"Full text when available (RSS body or document text)"},{"name":"data.parsedSource","type":"object | null","description":"Scrape result: `title`, `contentHTML`, `contentText`, `mainImage`, `excerpt`, `wordCount`"},{"name":"data.images","type":"Image[]","description":"Images attached to the source as public Image objects"},{"name":"data.feed","type":"Feed | null","description":"The catalogue feed for RSS articles"},{"name":"data.stories","type":"object[]","description":"Stories written from this source: `{ id, title, createdAt }`"},{"name":"data.images[].id","type":"string","description":"Unique image identifier"},{"name":"data.images[].url","type":"string","description":"Public URL of the image file"},{"name":"data.images[].s3Url","type":"string","description":"Deprecated alias of `url` with the same value; use `url`"},{"name":"data.images[].width","type":"number","description":"Width in pixels"},{"name":"data.images[].height","type":"number","description":"Height in pixels"},{"name":"data.images[].format","type":"string","description":"File format (PNG, JPEG, WEBP)"},{"name":"data.images[].contentType","type":"string","description":"MIME type of the image"},{"name":"data.images[].fileSize","type":"number | null","description":"File size in bytes"},{"name":"data.images[].imageType","type":"string","description":"Role on the story: main, thumbnail, gallery, content, other"},{"name":"data.images[].origin","type":"string","description":"Normalized provenance: `generated` (made by News Factory with an AI image model), `stock` (licensed stock provider), `source` (taken from the original article, usually not yours to republish), `upload` (uploaded or imported by URL)"},{"name":"data.images[].aiGenerated","type":"boolean","description":"Authoritative flag: true only when News Factory produced the image with an AI model, including crops and thumbnails derived from one. Use this (not `sourceType`) to pick images you can publish as your own."},{"name":"data.images[].generation","type":"object | null","description":"Present when `aiGenerated` is true: `{ provider, model, prompt }` (each may be null for older images)"},{"name":"data.images[].sourceType","type":"string","description":"Raw storage source (generated, stock, story, content-source, scraped, user-upload, url). Kept for compatibility; not a reliable AI indicator"},{"name":"data.images[].parentId","type":"string | null","description":"Id of the image this one was cropped or resized from"},{"name":"data.images[].originalUrl","type":"string | null","description":"Where the file was fetched from (article images, URL imports)"},{"name":"data.images[].title","type":"string | null","description":"Image title"},{"name":"data.images[].description","type":"string | null","description":"Image description"},{"name":"data.images[].altText","type":"string | null","description":"Alt text"},{"name":"data.images[].caption","type":"string | null","description":"Caption"},{"name":"data.images[].author","type":"string | null","description":"Author or photographer"},{"name":"data.images[].source","type":"string | null","description":"Attribution source: article URL, stock provider id, or AI provider id"},{"name":"data.images[].license","type":"string | null","description":"License (stock images)"},{"name":"data.images[].creditLine","type":"string | null","description":"Credit line to display (stock images)"},{"name":"data.images[].tags","type":"string[]","description":"Tags associated with the image"},{"name":"data.images[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.images[].updatedAt","type":"string | null","description":"ISO 8601 timestamp of the last update"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Provide at least one updatable field: title, summary, content, author, thumbnail, language, pubDate, flagged, flagReason, sourceMeta","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope content-sources:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["content-sources:write"],"missing":["content-sources:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Content source not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Content source not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["content-sources:write"]}],"x-required-scopes":["content-sources:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f760\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/content-sources/${id}`, {\n  method: 'PATCH',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"flagged\": true,\n    \"flagReason\": \"Duplicate of another source\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X PATCH \"https://client-api.news-factory.app/v1/content-sources/68b9b7c2f1a2b3c4d5e6f760\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"flagged\":true,\"flagReason\":\"Duplicate of another source\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f760\"\nurl = f\"https://client-api.news-factory.app/v1/content-sources/{id}\"\npayload = {\n    \"flagged\": True,\n    \"flagReason\": \"Duplicate of another source\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.patch(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"delete":{"operationId":"deleteContentSource","summary":"Delete Content Source","description":"Delete a content source. Its images are removed from storage when no story uses them. Stories written from the source are kept.","tags":["Content Sources"],"parameters":[{"name":"id","in":"path","required":true,"description":"The content source id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f760"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Content source deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"message":"Content source deleted successfully"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope content-sources:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["content-sources:write"],"missing":["content-sources:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Content source not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Content source not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["content-sources:write"]}],"x-required-scopes":["content-sources:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f760\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/content-sources/${id}`, {\n  method: 'DELETE',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.success);"},{"lang":"Shell","label":"cURL","source":"curl -X DELETE \"https://client-api.news-factory.app/v1/content-sources/68b9b7c2f1a2b3c4d5e6f760\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f760\"\nurl = f\"https://client-api.news-factory.app/v1/content-sources/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.delete(url, headers=headers)\nresult = response.json()\nprint(result.success)"}]}},"/content-sources/{id}/scrape":{"post":{"operationId":"scrapeContentSource","summary":"Scrape Content Source","description":"Fetch the source page and extract its main text, title and image into `parsedSource`. Sources that were already scraped are left alone unless `force` is true. Read the result with `GET /content-sources/{id}`.","tags":["Content Sources"],"x-group":"Processing","parameters":[{"name":"id","in":"path","required":true,"description":"The content source id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f760"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"force":{"type":"boolean","description":"Fetch again even if the page was scraped before"}}},"example":{"force":false}}},"x-fields":[{"name":"force","type":"boolean","required":false,"description":"Fetch again even if the page was scraped before"}]},"responses":{"200":{"description":"Scrape result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"id":{"type":"string","description":"The content source"},"scraped":{"type":"boolean","description":"Whether a fetch happened in this call"},"scrapedAt":{"type":["string","null"],"description":"When the page was fetched"},"parsedSource":{"type":["object","null"],"description":"`{ title, contentHTML, contentText, mainImage, excerpt, wordCount }` when a fetch happened"}}},"message":{"type":"string","description":"What happened"}}},"example":{"success":true,"data":{"id":"68b9b7c2f1a2b3c4d5e6f760","scraped":true,"scrapedAt":"2026-07-30T07:10:00.000Z","parsedSource":{"title":"Hospitals report 40% cost reduction with AI triage","contentHTML":"<p>Pilot programmes across Europe...</p>","contentText":"Pilot programmes across Europe...","mainImage":"https://techcrunch.com/wp-content/uploads/gettyimages-1445867611.jpg","excerpt":"Pilot programmes across Europe report faster triage and lower costs.","wordCount":1250}},"message":"Source scraped"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.id","type":"string","description":"The content source"},{"name":"data.scraped","type":"boolean","description":"Whether a fetch happened in this call"},{"name":"data.scrapedAt","type":"string | null","description":"When the page was fetched"},{"name":"data.parsedSource","type":"object | null","description":"`{ title, contentHTML, contentText, mainImage, excerpt, wordCount }` when a fetch happened"},{"name":"message","type":"string","description":"What happened"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Failed to fetch the page after 3 attempts","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope content-sources:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["content-sources:write"],"missing":["content-sources:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Content source not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Content source not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["content-sources:write"]}],"x-required-scopes":["content-sources:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f760\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/content-sources/${id}/scrape`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"force\": false\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/content-sources/68b9b7c2f1a2b3c4d5e6f760/scrape\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"force\":false}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f760\"\nurl = f\"https://client-api.news-factory.app/v1/content-sources/{id}/scrape\"\npayload = {\n    \"force\": False\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/content-sources/{id}/repurpose":{"post":{"operationId":"repurposeContentSource","summary":"Repurpose into a Story","description":"Write a new story from the content source with the Topic Workspace's story settings (tone, length, structure). Scrape the source first so the model has the full text. Costs AI credits.","tags":["Content Sources"],"x-group":"Processing","parameters":[{"name":"id","in":"path","required":true,"description":"The content source id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f760"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"languageCode":{"type":"string","description":"Language of the new story as a full locale code (default: the Topic Workspace's default language)","enum":["en-US","en-GB","de-DE","fr-FR","es-ES","es-MX","it-IT","pt-BR","pt-PT","nl-NL","pl-PL","sv-SE","da-DK","nb-NO","fi-FI","cs-CZ","ro-RO","hr-HR","tr-TR","ja-JP"]},"extraInstructions":{"type":"string","description":"Extra editorial instructions for this story"},"authorId":{"type":"string","description":"Author to attribute the story to (also applies the author's writing style)"},"temperature":{"type":"number","description":"Model temperature 0 to 1"}}},"example":{"languageCode":"en-US"}}},"x-fields":[{"name":"languageCode","type":"string","required":false,"description":"Language of the new story as a full locale code (default: the Topic Workspace's default language)"},{"name":"extraInstructions","type":"string","required":false,"description":"Extra editorial instructions for this story"},{"name":"authorId","type":"string","required":false,"description":"Author to attribute the story to (also applies the author's writing style)"},{"name":"temperature","type":"number","required":false,"description":"Model temperature 0 to 1"}]},"responses":{"200":{"description":"Story written","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"contentSourceId":{"type":"string","description":"The source"},"storyId":{"type":"string","description":"Id of the new story (load it with `GET /stories/{id}`)"},"storyTitle":{"type":"string","description":"Title of the new story"},"contentSourceIds":{"type":"array","items":{"type":"string"},"description":"Sources the story was written from"},"authorId":{"type":["string","null"],"description":"Attributed author"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"contentSourceId":"68b9b7c2f1a2b3c4d5e6f760","storyId":"68b9b7c2f1a2b3c4d5e6f700","storyTitle":"AI Revolution in Healthcare","contentSourceIds":["68b9b7c2f1a2b3c4d5e6f760"],"authorId":null},"message":"Content source repurposed into a story"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.contentSourceId","type":"string","description":"The source"},{"name":"data.storyId","type":"string","description":"Id of the new story (load it with `GET /stories/{id}`)"},{"name":"data.storyTitle","type":"string","description":"Title of the new story"},{"name":"data.contentSourceIds","type":"string[]","description":"Sources the story was written from"},{"name":"data.authorId","type":"string | null","description":"Attributed author"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Content source has no scraped content","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope stories:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["ai:generate","content-sources:read","stories:write"],"missing":["stories:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Content source not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Content source not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"429":{"description":"The organization's AI credits are exhausted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LLM_QUOTA_EXCEEDED","message":"LLM credit quota exceeded for this organization","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-llm-quota-exceeded"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["ai:generate","content-sources:read","stories:write"]}],"x-required-scopes":["ai:generate","content-sources:read","stories:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f760\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/content-sources/${id}/repurpose`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"languageCode\": \"en-US\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/content-sources/68b9b7c2f1a2b3c4d5e6f760/repurpose\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"languageCode\":\"en-US\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f760\"\nurl = f\"https://client-api.news-factory.app/v1/content-sources/{id}/repurpose\"\npayload = {\n    \"languageCode\": \"en-US\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/feeds":{"get":{"operationId":"listFeeds","summary":"List Subscribed Feeds","description":"The feeds the Topic Workspace follows. Articles from these feeds are what the News Hub and workflows evaluate. Browse other feeds with the catalogue endpoint and subscribe with `POST /feeds`.","tags":["RSS Feeds"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size (default 50, max 100)","schema":{"type":"number","examples":[50]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The subscribed feeds","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"Feed","properties":{"id":{"type":"string","description":"Unique feed identifier"},"url":{"type":"string","description":"RSS or Atom feed URL"},"name":{"type":"string","description":"Feed name"},"website":{"type":["string","null"],"description":"Publisher website"},"favicon":{"type":["string","null"],"description":"Favicon URL"},"language":{"type":["string","null"],"description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},"country":{"type":["string","null"],"description":"ISO country code"},"category":{"type":["string","null"],"description":"news, technology, sports, business, science, ..."},"contentType":{"type":["string","null"],"description":"news, wire, magazine, trade, local or blog"},"trustTier":{"type":["number","null"],"description":"Editorial trust tier (1 is highest)"},"qualityScore":{"type":["number","null"],"description":"Catalogue quality score"},"articlesPerDay":{"type":["number","null"],"description":"Average publishing rate"},"enabled":{"type":"boolean","description":"Whether the platform fetches this feed"},"checkFrequency":{"type":"number","description":"Seconds between fetches"},"lastFetchedAt":{"type":["string","null"],"description":"Last successful fetch"},"nextFetchDue":{"type":["string","null"],"description":"Next scheduled fetch"},"subscribed":{"type":"boolean","description":"Whether the current Topic Workspace follows this feed"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"}}},"description":"Feeds (all with `subscribed: true`)"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"},"total":{"type":"number","description":"Number of subscribed feeds"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f770","url":"https://techcrunch.com/feed/","name":"TechCrunch","website":"https://techcrunch.com","favicon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico","language":"en","country":"US","category":"technology","contentType":"news","trustTier":2,"qualityScore":82,"articlesPerDay":38,"enabled":true,"checkFrequency":3600,"lastFetchedAt":"2026-09-05T10:00:00.000Z","nextFetchDue":"2026-09-05T11:00:00.000Z","subscribed":true,"createdAt":"2026-01-15T09:00:00.000Z"}],"meta":{"page":0,"limit":50,"hasMore":false,"total":1}}}},"x-fields":[{"name":"data","type":"Feed[]","description":"Feeds (all with `subscribed: true`)"},{"name":"data[].id","type":"string","description":"Unique feed identifier"},{"name":"data[].url","type":"string","description":"RSS or Atom feed URL"},{"name":"data[].name","type":"string","description":"Feed name"},{"name":"data[].website","type":"string | null","description":"Publisher website"},{"name":"data[].favicon","type":"string | null","description":"Favicon URL"},{"name":"data[].language","type":"string | null","description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},{"name":"data[].country","type":"string | null","description":"ISO country code"},{"name":"data[].category","type":"string | null","description":"news, technology, sports, business, science, ..."},{"name":"data[].contentType","type":"string | null","description":"news, wire, magazine, trade, local or blog"},{"name":"data[].trustTier","type":"number | null","description":"Editorial trust tier (1 is highest)"},{"name":"data[].qualityScore","type":"number | null","description":"Catalogue quality score"},{"name":"data[].articlesPerDay","type":"number | null","description":"Average publishing rate"},{"name":"data[].enabled","type":"boolean","description":"Whether the platform fetches this feed"},{"name":"data[].checkFrequency","type":"number","description":"Seconds between fetches"},{"name":"data[].lastFetchedAt","type":"string | null","description":"Last successful fetch"},{"name":"data[].nextFetchDue","type":"string | null","description":"Next scheduled fetch"},{"name":"data[].subscribed","type":"boolean","description":"Whether the current Topic Workspace follows this feed"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"},{"name":"meta.total","type":"number","description":"Number of subscribed feeds"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:read"],"missing":["feeds:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["feeds:read"]}],"x-required-scopes":["feeds:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/feeds`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/feeds\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/feeds\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"post":{"operationId":"subscribeFeed","summary":"Subscribe to Feed","description":"Follow a catalogue feed for the Topic Workspace, by `feedId` or by its feed `url`. Feeds outside the catalogue answer 404: the catalogue is curated, ask support to add a source. Subscribing twice is harmless.","tags":["RSS Feeds"],"parameters":[{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"feedId or url","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"feedId":{"type":"string","description":"Catalogue feed id"},"url":{"type":"string","description":"Feed URL exactly as listed in the catalogue"}}},"example":{"feedId":"68b9b7c2f1a2b3c4d5e6f770"}}},"x-fields":[{"name":"feedId","type":"string","required":false,"description":"Catalogue feed id"},{"name":"url","type":"string","required":false,"description":"Feed URL exactly as listed in the catalogue"}]},"responses":{"200":{"description":"Subscribed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"feed":{"type":"object","x-type":"Feed","description":"The feed","properties":{"id":{"type":"string","description":"Unique feed identifier"},"url":{"type":"string","description":"RSS or Atom feed URL"},"name":{"type":"string","description":"Feed name"},"website":{"type":["string","null"],"description":"Publisher website"},"favicon":{"type":["string","null"],"description":"Favicon URL"},"language":{"type":["string","null"],"description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},"country":{"type":["string","null"],"description":"ISO country code"},"category":{"type":["string","null"],"description":"news, technology, sports, business, science, ..."},"contentType":{"type":["string","null"],"description":"news, wire, magazine, trade, local or blog"},"trustTier":{"type":["number","null"],"description":"Editorial trust tier (1 is highest)"},"qualityScore":{"type":["number","null"],"description":"Catalogue quality score"},"articlesPerDay":{"type":["number","null"],"description":"Average publishing rate"},"enabled":{"type":"boolean","description":"Whether the platform fetches this feed"},"checkFrequency":{"type":"number","description":"Seconds between fetches"},"lastFetchedAt":{"type":["string","null"],"description":"Last successful fetch"},"nextFetchDue":{"type":["string","null"],"description":"Next scheduled fetch"},"subscribed":{"type":"boolean","description":"Whether the current Topic Workspace follows this feed"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"}}},"topicKey":{"type":"string","description":"Topic Workspace that now follows the feed"},"followedFeeds":{"type":"array","items":{"type":"string"},"description":"All feed ids the Topic Workspace follows"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"feed":{"id":"68b9b7c2f1a2b3c4d5e6f770","url":"https://techcrunch.com/feed/","name":"TechCrunch","website":"https://techcrunch.com","favicon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico","language":"en","country":"US","category":"technology","contentType":"news","trustTier":2,"qualityScore":82,"articlesPerDay":38,"enabled":true,"checkFrequency":3600,"lastFetchedAt":"2026-09-05T10:00:00.000Z","nextFetchDue":"2026-09-05T11:00:00.000Z","subscribed":true,"createdAt":"2026-01-15T09:00:00.000Z"},"topicKey":"tech","followedFeeds":["68b9b7c2f1a2b3c4d5e6f770"]},"message":"Successfully followed feed"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.feed","type":"Feed","description":"The feed"},{"name":"data.feed.id","type":"string","description":"Unique feed identifier"},{"name":"data.feed.url","type":"string","description":"RSS or Atom feed URL"},{"name":"data.feed.name","type":"string","description":"Feed name"},{"name":"data.feed.website","type":"string | null","description":"Publisher website"},{"name":"data.feed.favicon","type":"string | null","description":"Favicon URL"},{"name":"data.feed.language","type":"string | null","description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},{"name":"data.feed.country","type":"string | null","description":"ISO country code"},{"name":"data.feed.category","type":"string | null","description":"news, technology, sports, business, science, ..."},{"name":"data.feed.contentType","type":"string | null","description":"news, wire, magazine, trade, local or blog"},{"name":"data.feed.trustTier","type":"number | null","description":"Editorial trust tier (1 is highest)"},{"name":"data.feed.qualityScore","type":"number | null","description":"Catalogue quality score"},{"name":"data.feed.articlesPerDay","type":"number | null","description":"Average publishing rate"},{"name":"data.feed.enabled","type":"boolean","description":"Whether the platform fetches this feed"},{"name":"data.feed.checkFrequency","type":"number","description":"Seconds between fetches"},{"name":"data.feed.lastFetchedAt","type":"string | null","description":"Last successful fetch"},{"name":"data.feed.nextFetchDue","type":"string | null","description":"Next scheduled fetch"},{"name":"data.feed.subscribed","type":"boolean","description":"Whether the current Topic Workspace follows this feed"},{"name":"data.feed.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.topicKey","type":"string","description":"Topic Workspace that now follows the feed"},{"name":"data.followedFeeds","type":"string[]","description":"All feed ids the Topic Workspace follows"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Provide feedId or url","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:write"],"missing":["feeds:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Feed not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Feed not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["feeds:write"]}],"x-required-scopes":["feeds:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/feeds`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    \"feedId\": \"68b9b7c2f1a2b3c4d5e6f770\"\n  })\n});\n\nconst result = await response.json();\nconsole.log(result.data.feed);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/feeds\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"feedId\":\"68b9b7c2f1a2b3c4d5e6f770\"}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/feeds\"\npayload = {\n    \"feedId\": \"68b9b7c2f1a2b3c4d5e6f770\"\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"].feed)"}]}},"/feeds/catalog":{"get":{"operationId":"searchFeedCatalog","summary":"Search Feed Catalog","description":"Search the curated catalogue of feeds by name, URL, language, country or category. Use a result's `id` with `POST /feeds` to subscribe.","tags":["RSS Feeds"],"x-group":"Catalog","parameters":[{"name":"search","in":"query","required":false,"description":"Matches the feed name, URL or website (case insensitive)","schema":{"type":"string","examples":["techcrunch"]}},{"name":"language","in":"query","required":false,"description":"Catalogue language code (bare ISO code such as en, de, pt)","schema":{"type":"string","examples":["en"]}},{"name":"country","in":"query","required":false,"description":"ISO country code","schema":{"type":"string","examples":["US"]}},{"name":"category","in":"query","required":false,"description":"news, technology, business, science, sports, ...","schema":{"type":"string","examples":["technology"]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default name ASC). Allowed fields: name, qualityScore, articlesPerDay, trustTier.","schema":{"type":"string","enum":["name ASC","qualityScore DESC","articlesPerDay DESC","trustTier ASC"],"examples":["name ASC"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Matching feeds","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"Feed","properties":{"id":{"type":"string","description":"Unique feed identifier"},"url":{"type":"string","description":"RSS or Atom feed URL"},"name":{"type":"string","description":"Feed name"},"website":{"type":["string","null"],"description":"Publisher website"},"favicon":{"type":["string","null"],"description":"Favicon URL"},"language":{"type":["string","null"],"description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},"country":{"type":["string","null"],"description":"ISO country code"},"category":{"type":["string","null"],"description":"news, technology, sports, business, science, ..."},"contentType":{"type":["string","null"],"description":"news, wire, magazine, trade, local or blog"},"trustTier":{"type":["number","null"],"description":"Editorial trust tier (1 is highest)"},"qualityScore":{"type":["number","null"],"description":"Catalogue quality score"},"articlesPerDay":{"type":["number","null"],"description":"Average publishing rate"},"enabled":{"type":"boolean","description":"Whether the platform fetches this feed"},"checkFrequency":{"type":"number","description":"Seconds between fetches"},"lastFetchedAt":{"type":["string","null"],"description":"Last successful fetch"},"nextFetchDue":{"type":["string","null"],"description":"Next scheduled fetch"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"}}},"description":"Catalogue feeds (`subscribed` is not included here)"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f770","url":"https://techcrunch.com/feed/","name":"TechCrunch","website":"https://techcrunch.com","favicon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico","language":"en","country":"US","category":"technology","contentType":"news","trustTier":2,"qualityScore":82,"articlesPerDay":38,"enabled":true,"checkFrequency":3600,"lastFetchedAt":"2026-09-05T10:00:00.000Z","nextFetchDue":"2026-09-05T11:00:00.000Z","createdAt":"2026-01-15T09:00:00.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false}}}},"x-fields":[{"name":"data","type":"Feed[]","description":"Catalogue feeds (`subscribed` is not included here)"},{"name":"data[].id","type":"string","description":"Unique feed identifier"},{"name":"data[].url","type":"string","description":"RSS or Atom feed URL"},{"name":"data[].name","type":"string","description":"Feed name"},{"name":"data[].website","type":"string | null","description":"Publisher website"},{"name":"data[].favicon","type":"string | null","description":"Favicon URL"},{"name":"data[].language","type":"string | null","description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},{"name":"data[].country","type":"string | null","description":"ISO country code"},{"name":"data[].category","type":"string | null","description":"news, technology, sports, business, science, ..."},{"name":"data[].contentType","type":"string | null","description":"news, wire, magazine, trade, local or blog"},{"name":"data[].trustTier","type":"number | null","description":"Editorial trust tier (1 is highest)"},{"name":"data[].qualityScore","type":"number | null","description":"Catalogue quality score"},{"name":"data[].articlesPerDay","type":"number | null","description":"Average publishing rate"},{"name":"data[].enabled","type":"boolean","description":"Whether the platform fetches this feed"},{"name":"data[].checkFrequency","type":"number","description":"Seconds between fetches"},{"name":"data[].lastFetchedAt","type":"string | null","description":"Last successful fetch"},{"name":"data[].nextFetchDue","type":"string | null","description":"Next scheduled fetch"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:read"],"missing":["feeds:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["feeds:read"]}],"x-required-scopes":["feeds:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/feeds/catalog?search=techcrunch&language=en&limit=10`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/feeds/catalog?search=techcrunch&language=en&limit=10\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/feeds/catalog\"\nparams = {\n    \"search\": \"techcrunch\",\n    \"language\": \"en\",\n    \"limit\": 10\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/feeds/{id}":{"get":{"operationId":"getFeed","summary":"Get Feed","description":"One catalogue feed with its fetch state and whether the Topic Workspace follows it.","tags":["RSS Feeds"],"parameters":[{"name":"id","in":"path","required":true,"description":"The feed id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f770"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The feed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"Feed","description":"The feed","properties":{"id":{"type":"string","description":"Unique feed identifier"},"url":{"type":"string","description":"RSS or Atom feed URL"},"name":{"type":"string","description":"Feed name"},"website":{"type":["string","null"],"description":"Publisher website"},"favicon":{"type":["string","null"],"description":"Favicon URL"},"language":{"type":["string","null"],"description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},"country":{"type":["string","null"],"description":"ISO country code"},"category":{"type":["string","null"],"description":"news, technology, sports, business, science, ..."},"contentType":{"type":["string","null"],"description":"news, wire, magazine, trade, local or blog"},"trustTier":{"type":["number","null"],"description":"Editorial trust tier (1 is highest)"},"qualityScore":{"type":["number","null"],"description":"Catalogue quality score"},"articlesPerDay":{"type":["number","null"],"description":"Average publishing rate"},"enabled":{"type":"boolean","description":"Whether the platform fetches this feed"},"checkFrequency":{"type":"number","description":"Seconds between fetches"},"lastFetchedAt":{"type":["string","null"],"description":"Last successful fetch"},"nextFetchDue":{"type":["string","null"],"description":"Next scheduled fetch"},"subscribed":{"type":"boolean","description":"Whether the current Topic Workspace follows this feed"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f770","url":"https://techcrunch.com/feed/","name":"TechCrunch","website":"https://techcrunch.com","favicon":"https://cdn.news-factory.app/feeds/techcrunch/favicon.ico","language":"en","country":"US","category":"technology","contentType":"news","trustTier":2,"qualityScore":82,"articlesPerDay":38,"enabled":true,"checkFrequency":3600,"lastFetchedAt":"2026-09-05T10:00:00.000Z","nextFetchDue":"2026-09-05T11:00:00.000Z","subscribed":true,"createdAt":"2026-01-15T09:00:00.000Z"}}}},"x-fields":[{"name":"data","type":"Feed","description":"The feed"},{"name":"data.id","type":"string","description":"Unique feed identifier"},{"name":"data.url","type":"string","description":"RSS or Atom feed URL"},{"name":"data.name","type":"string","description":"Feed name"},{"name":"data.website","type":"string | null","description":"Publisher website"},{"name":"data.favicon","type":"string | null","description":"Favicon URL"},{"name":"data.language","type":"string | null","description":"Language of the feed as published by the catalogue (bare ISO code, the catalogue does not carry regions)"},{"name":"data.country","type":"string | null","description":"ISO country code"},{"name":"data.category","type":"string | null","description":"news, technology, sports, business, science, ..."},{"name":"data.contentType","type":"string | null","description":"news, wire, magazine, trade, local or blog"},{"name":"data.trustTier","type":"number | null","description":"Editorial trust tier (1 is highest)"},{"name":"data.qualityScore","type":"number | null","description":"Catalogue quality score"},{"name":"data.articlesPerDay","type":"number | null","description":"Average publishing rate"},{"name":"data.enabled","type":"boolean","description":"Whether the platform fetches this feed"},{"name":"data.checkFrequency","type":"number","description":"Seconds between fetches"},{"name":"data.lastFetchedAt","type":"string | null","description":"Last successful fetch"},{"name":"data.nextFetchDue","type":"string | null","description":"Next scheduled fetch"},{"name":"data.subscribed","type":"boolean","description":"Whether the current Topic Workspace follows this feed"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:read"],"missing":["feeds:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Feed not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Feed not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["feeds:read"]}],"x-required-scopes":["feeds:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f770\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/feeds/${id}`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/feeds/68b9b7c2f1a2b3c4d5e6f770\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f770\"\nurl = f\"https://client-api.news-factory.app/v1/feeds/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]},"delete":{"operationId":"unsubscribeFeed","summary":"Unsubscribe from Feed","description":"Stop following a feed for the Topic Workspace. Articles already saved as content sources are kept.","tags":["RSS Feeds"],"parameters":[{"name":"id","in":"path","required":true,"description":"The feed id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f770"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Unsubscribed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"message":"Unsubscribed from feed"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:write"],"missing":["feeds:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"The Topic Workspace does not follow this feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Not subscribed to this feed","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["feeds:write"]}],"x-required-scopes":["feeds:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f770\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/feeds/${id}`, {\n  method: 'DELETE',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.success);"},{"lang":"Shell","label":"cURL","source":"curl -X DELETE \"https://client-api.news-factory.app/v1/feeds/68b9b7c2f1a2b3c4d5e6f770\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f770\"\nurl = f\"https://client-api.news-factory.app/v1/feeds/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.delete(url, headers=headers)\nresult = response.json()\nprint(result.success)"}]}},"/feeds/{id}/fetch":{"post":{"operationId":"fetchFeedArticles","summary":"Fetch Feed Now","description":"Fetch the feed immediately instead of waiting for the scheduler, and store new articles in the catalogue. Unchanged feeds (HTTP 304) answer with `modified: false`.","tags":["RSS Feeds"],"x-group":"Processing","parameters":[{"name":"id","in":"path","required":true,"description":"The feed id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f770"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Fetch result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"feedId":{"type":"string","description":"The feed"},"modified":{"type":"boolean","description":"False when the publisher answered 304 Not Modified"},"statusCode":{"type":["number","null"],"description":"HTTP status from the publisher"},"feedTitle":{"type":["string","null"],"description":"Title from the feed"},"itemCount":{"type":"number","description":"Items in the feed"},"items":{"type":"array","items":{"type":"object"},"description":"Parsed items: `{ title, link, pubDate, description, author, thumbnail }`"},"persistence":{"type":["object","null"],"description":"How many items were created or updated in the catalogue"},"linkedToContentSources":{"type":"number","description":"Items matched to existing content sources"}}},"message":{"type":"string","description":"What happened"}}},"example":{"success":true,"data":{"feedId":"68b9b7c2f1a2b3c4d5e6f770","modified":true,"statusCode":200,"feedTitle":"TechCrunch","itemCount":20,"items":[{"title":"OpenAI announces new medical reasoning model","link":"https://techcrunch.com/2026/09/04/openai-medical-model/","pubDate":"2026-09-04T15:00:00.000Z","description":"The company says the model is tuned for clinical documentation.","author":"Kyle Wiggers","thumbnail":"https://techcrunch.com/wp-content/uploads/openai-medical.jpg"}],"persistence":{"created":3,"updated":17},"linkedToContentSources":0},"message":"Feed fetched"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.feedId","type":"string","description":"The feed"},{"name":"data.modified","type":"boolean","description":"False when the publisher answered 304 Not Modified"},{"name":"data.statusCode","type":"number | null","description":"HTTP status from the publisher"},{"name":"data.feedTitle","type":"string | null","description":"Title from the feed"},{"name":"data.itemCount","type":"number","description":"Items in the feed"},{"name":"data.items","type":"object[]","description":"Parsed items: `{ title, link, pubDate, description, author, thumbnail }`"},{"name":"data.persistence","type":"object | null","description":"How many items were created or updated in the catalogue"},{"name":"data.linkedToContentSources","type":"number","description":"Items matched to existing content sources"},{"name":"message","type":"string","description":"What happened"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:write needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:write"],"missing":["feeds:write"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Feed not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Feed not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["feeds:write"]}],"x-required-scopes":["feeds:write"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f770\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/feeds/${id}/fetch`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/feeds/68b9b7c2f1a2b3c4d5e6f770/fetch\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f770\"\nurl = f\"https://client-api.news-factory.app/v1/feeds/{id}/fetch\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/feeds/articles":{"get":{"operationId":"listFeedArticles","summary":"List Articles","description":"Articles from the Topic Workspace's subscribed feeds together with their topic evaluation. By default returns evaluated articles (newest evaluation first), filterable by `minConfidence`; `feedId` pages through one feed's articles evaluated or not; `unevaluated=true` returns what the Topic Workspace has not looked at yet.","tags":["RSS Feeds"],"x-group":"Articles","parameters":[{"name":"feedId","in":"query","required":false,"description":"Only this feed's articles (evaluated and unevaluated)","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f770"]}},{"name":"minConfidence","in":"query","required":false,"description":"Only articles evaluated at this topic match or higher (0 to 100)","schema":{"type":"number","examples":[50]}},{"name":"evaluatedBy","in":"query","required":false,"description":"Only evaluations by the agent or by a person","schema":{"type":"string","enum":["agent","human"],"examples":["agent"]}},{"name":"unevaluated","in":"query","required":false,"description":"Only articles without an evaluation for this Topic Workspace","schema":{"type":"boolean","examples":[false]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default createdAt DESC). Allowed fields: createdAt, pubDate, matchConfidence.","schema":{"type":"string","enum":["createdAt DESC","pubDate DESC","matchConfidence DESC"],"examples":["createdAt DESC"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"A page of articles","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"Article","properties":{"id":{"type":"string","description":"Unique article identifier"},"title":{"type":"string","description":"Article title"},"url":{"type":"string","description":"Article URL"},"description":{"type":["string","null"],"description":"Description or summary from the feed"},"author":{"type":["string","null"],"description":"Author"},"thumbnail":{"type":["string","null"],"description":"Image URL from the feed"},"pubDate":{"type":"string","description":"ISO 8601 publication date"},"language":{"type":["string","null"],"description":"Language of the article as published by the feed"},"feedId":{"type":"string","description":"Feed the article came from"},"matchConfidence":{"type":["number","null"],"description":"Topic match from 0 to 100. `null` means not evaluated yet for this Topic Workspace; `0` means evaluated as no match"},"evaluatedBy":{"type":["string","null"],"description":"agent, human or null"},"evaluatedAt":{"type":["string","null"],"description":"When the evaluation was recorded"},"createdAt":{"type":["string","null"],"description":"When the article entered the catalogue"}}},"description":"Articles"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"},"unevaluated":{"type":"boolean","description":"Whether the unevaluated view was returned"},"feedId":{"type":"string","description":"Feed filter applied (feed view only)"},"total":{"type":"number","description":"Total unevaluated articles (unevaluated view only)"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f780","title":"OpenAI announces new medical reasoning model","url":"https://techcrunch.com/2026/09/04/openai-medical-model/","description":"The company says the model is tuned for clinical documentation.","author":"Kyle Wiggers","thumbnail":"https://techcrunch.com/wp-content/uploads/openai-medical.jpg","pubDate":"2026-09-04T15:00:00.000Z","language":"en","feedId":"68b9b7c2f1a2b3c4d5e6f770","matchConfidence":88,"evaluatedBy":"agent","evaluatedAt":"2026-09-04T15:30:00.000Z","createdAt":"2026-09-04T15:05:00.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false,"unevaluated":false}}}},"x-fields":[{"name":"data","type":"Article[]","description":"Articles"},{"name":"data[].id","type":"string","description":"Unique article identifier"},{"name":"data[].title","type":"string","description":"Article title"},{"name":"data[].url","type":"string","description":"Article URL"},{"name":"data[].description","type":"string | null","description":"Description or summary from the feed"},{"name":"data[].author","type":"string | null","description":"Author"},{"name":"data[].thumbnail","type":"string | null","description":"Image URL from the feed"},{"name":"data[].pubDate","type":"string","description":"ISO 8601 publication date"},{"name":"data[].language","type":"string | null","description":"Language of the article as published by the feed"},{"name":"data[].feedId","type":"string","description":"Feed the article came from"},{"name":"data[].matchConfidence","type":"number | null","description":"Topic match from 0 to 100. `null` means not evaluated yet for this Topic Workspace; `0` means evaluated as no match"},{"name":"data[].evaluatedBy","type":"string | null","description":"agent, human or null"},{"name":"data[].evaluatedAt","type":"string | null","description":"When the evaluation was recorded"},{"name":"data[].createdAt","type":"string | null","description":"When the article entered the catalogue"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"},{"name":"meta.unevaluated","type":"boolean","description":"Whether the unevaluated view was returned"},{"name":"meta.feedId","type":"string","description":"Feed filter applied (feed view only)"},{"name":"meta.total","type":"number","description":"Total unevaluated articles (unevaluated view only)"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"minConfidence must be between 0 and 100","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:read"],"missing":["feeds:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["feeds:read"]}],"x-required-scopes":["feeds:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/feeds/articles?minConfidence=50&limit=20`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/feeds/articles?minConfidence=50&limit=20\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/feeds/articles\"\nparams = {\n    \"minConfidence\": 50,\n    \"limit\": 20\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/feeds/articles/{id}":{"get":{"operationId":"getArticleDetails","summary":"Get Article","description":"One article with its evaluation for the Topic Workspace. Articles the Topic Workspace has not evaluated yet are only found when you also pass their `feedId`.","tags":["RSS Feeds"],"x-group":"Articles","parameters":[{"name":"id","in":"path","required":true,"description":"The article id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f780"]}},{"name":"feedId","in":"query","required":false,"description":"The article's feed, needed for unevaluated articles","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f770"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The article","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"Article","description":"The article","properties":{"id":{"type":"string","description":"Unique article identifier"},"title":{"type":"string","description":"Article title"},"url":{"type":"string","description":"Article URL"},"description":{"type":["string","null"],"description":"Description or summary from the feed"},"author":{"type":["string","null"],"description":"Author"},"thumbnail":{"type":["string","null"],"description":"Image URL from the feed"},"pubDate":{"type":"string","description":"ISO 8601 publication date"},"language":{"type":["string","null"],"description":"Language of the article as published by the feed"},"feedId":{"type":"string","description":"Feed the article came from"},"matchConfidence":{"type":["number","null"],"description":"Topic match from 0 to 100. `null` means not evaluated yet for this Topic Workspace; `0` means evaluated as no match"},"evaluatedBy":{"type":["string","null"],"description":"agent, human or null"},"evaluatedAt":{"type":["string","null"],"description":"When the evaluation was recorded"},"createdAt":{"type":["string","null"],"description":"When the article entered the catalogue"}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f780","title":"OpenAI announces new medical reasoning model","url":"https://techcrunch.com/2026/09/04/openai-medical-model/","description":"The company says the model is tuned for clinical documentation.","author":"Kyle Wiggers","thumbnail":"https://techcrunch.com/wp-content/uploads/openai-medical.jpg","pubDate":"2026-09-04T15:00:00.000Z","language":"en","feedId":"68b9b7c2f1a2b3c4d5e6f770","matchConfidence":88,"evaluatedBy":"agent","evaluatedAt":"2026-09-04T15:30:00.000Z","createdAt":"2026-09-04T15:05:00.000Z"}}}},"x-fields":[{"name":"data","type":"Article","description":"The article"},{"name":"data.id","type":"string","description":"Unique article identifier"},{"name":"data.title","type":"string","description":"Article title"},{"name":"data.url","type":"string","description":"Article URL"},{"name":"data.description","type":"string | null","description":"Description or summary from the feed"},{"name":"data.author","type":"string | null","description":"Author"},{"name":"data.thumbnail","type":"string | null","description":"Image URL from the feed"},{"name":"data.pubDate","type":"string","description":"ISO 8601 publication date"},{"name":"data.language","type":"string | null","description":"Language of the article as published by the feed"},{"name":"data.feedId","type":"string","description":"Feed the article came from"},{"name":"data.matchConfidence","type":"number | null","description":"Topic match from 0 to 100. `null` means not evaluated yet for this Topic Workspace; `0` means evaluated as no match"},{"name":"data.evaluatedBy","type":"string | null","description":"agent, human or null"},{"name":"data.evaluatedAt","type":"string | null","description":"When the evaluation was recorded"},{"name":"data.createdAt","type":"string | null","description":"When the article entered the catalogue"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:read"],"missing":["feeds:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Not found, or not evaluated yet and no feedId given","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Article not found. Unevaluated articles can be looked up with the feedId query parameter.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["feeds:read"]}],"x-required-scopes":["feeds:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f780\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/feeds/articles/${id}`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/feeds/articles/68b9b7c2f1a2b3c4d5e6f780\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f780\"\nurl = f\"https://client-api.news-factory.app/v1/feeds/articles/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/feeds/scheduler/status":{"get":{"operationId":"schedulerStatus","summary":"Scheduler Status","description":"State of the platform's feed fetch scheduler: whether it runs, how often, and when the next global fetch is due.","tags":["RSS Feeds"],"x-group":"Scheduler","parameters":[{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Scheduler state","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":"boolean","description":"Whether the scheduler job is registered"},"nextRun":{"type":["string","null"],"description":"ISO 8601 time of the next global fetch"},"intervalMinutes":{"type":["number","null"],"description":"Minutes between fetch rounds"},"jobCount":{"type":"number","description":"Scheduled jobs in the queue"},"timestamp":{"type":"string","description":"When this status was read"}}}}},"example":{"data":{"active":true,"nextRun":"2026-09-05T13:00:00.000Z","intervalMinutes":30,"jobCount":2,"timestamp":"2026-09-05T12:41:03.000Z"}}}},"x-fields":[{"name":"data.active","type":"boolean","description":"Whether the scheduler job is registered"},{"name":"data.nextRun","type":"string | null","description":"ISO 8601 time of the next global fetch"},{"name":"data.intervalMinutes","type":"number | null","description":"Minutes between fetch rounds"},{"name":"data.jobCount","type":"number","description":"Scheduled jobs in the queue"},{"name":"data.timestamp","type":"string","description":"When this status was read"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope feeds:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["feeds:read"],"missing":["feeds:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["feeds:read"]}],"x-required-scopes":["feeds:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/feeds/scheduler/status`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/feeds/scheduler/status\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/feeds/scheduler/status\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflows":{"get":{"operationId":"listWorkflows","summary":"List Workflows","description":"The Topic Workspace's workflows with their node list. Workflows are designed in the app; the API lets you run them and follow their runs.","tags":["Workflows"],"parameters":[{"name":"isActive","in":"query","required":false,"description":"Only active (true) or inactive (false) workflows","schema":{"type":"boolean","examples":[true]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default createdAt DESC). Allowed fields: createdAt, updatedAt, name.","schema":{"type":"string","enum":["createdAt DESC","updatedAt DESC","name ASC"],"examples":["createdAt DESC"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"A page of workflows","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"Workflow","properties":{"id":{"type":"string","description":"Unique workflow identifier"},"name":{"type":"string","description":"Workflow name"},"description":{"type":["string","null"],"description":"Description"},"isActive":{"type":"boolean","description":"Inactive workflows cannot be run"},"isDefault":{"type":"boolean","description":"The Topic Workspace's default workflow"},"topicKey":{"type":"string","description":"Topic Workspace the workflow belongs to"},"feedIds":{"type":"array","items":{"type":"string"},"description":"The workflow's own feed list, up to 10 feed ids. Absent when the workflow reads the Topic Workspace's followed feeds"},"triggerConfig":{"type":"object","description":"`{ type: manual | scheduled, schedule?: { frequency, days, times, timezone } }`"},"scheduleState":{"type":["object","null"],"description":"Server managed schedule state: `nextRunAt`, `lastTriggeredAt`, `lastRunId`, `lastSkipReason`"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"nodes":{"type":"array","items":{"type":"object","x-type":"Node","properties":{"id":{"type":"string","description":"Node id"},"type":{"type":"string","description":"Node type"},"label":{"type":"string","description":"Display label"},"order":{"type":"number","description":"Execution order"},"enabled":{"type":"boolean","description":"Disabled nodes are skipped"}}},"description":"Nodes in execution order. `type` is one of evaluate, process, repurpose, quality-check, seo, image-generation, translate, publish, social-distribution"}}},"description":"Workflows"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f7a0","name":"Daily AI digest","description":"Evaluate new articles, write stories, check quality, translate to German.","isActive":true,"isDefault":true,"topicKey":"tech","triggerConfig":{"type":"scheduled","schedule":{"frequency":"daily","days":["mon","tue","wed","thu","fri","sat","sun"],"times":["06:00"],"timezone":"Europe/London"}},"scheduleState":{"nextRunAt":"2026-09-06T05:00:00.000Z","lastTriggeredAt":"2026-09-05T05:00:00.000Z","lastRunId":"68b9b7c2f1a2b3c4d5e6f7b0","lastSkipReason":null},"nodes":[{"id":"68b9b7c2f1a2b3c4d5e6f7a1","type":"evaluate","label":"Evaluate articles","order":0,"enabled":true},{"id":"68b9b7c2f1a2b3c4d5e6f7a2","type":"repurpose","label":"Write stories","order":1,"enabled":true},{"id":"68b9b7c2f1a2b3c4d5e6f7a3","type":"quality-check","label":"Quality check","order":2,"enabled":true},{"id":"68b9b7c2f1a2b3c4d5e6f7a4","type":"translate","label":"Translate","order":3,"enabled":true}],"createdAt":"2026-08-01T09:00:00.000Z","updatedAt":"2026-09-02T18:00:00.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false}}}},"x-fields":[{"name":"data","type":"Workflow[]","description":"Workflows"},{"name":"data[].id","type":"string","description":"Unique workflow identifier"},{"name":"data[].name","type":"string","description":"Workflow name"},{"name":"data[].description","type":"string | null","description":"Description"},{"name":"data[].isActive","type":"boolean","description":"Inactive workflows cannot be run"},{"name":"data[].isDefault","type":"boolean","description":"The Topic Workspace's default workflow"},{"name":"data[].topicKey","type":"string","description":"Topic Workspace the workflow belongs to"},{"name":"data[].feedIds","type":"string[] | undefined","description":"The workflow's own feed list, up to 10 feed ids. Absent when the workflow reads the Topic Workspace's followed feeds"},{"name":"data[].triggerConfig","type":"object","description":"`{ type: manual | scheduled, schedule?: { frequency, days, times, timezone } }`"},{"name":"data[].scheduleState","type":"object | null","description":"Server managed schedule state: `nextRunAt`, `lastTriggeredAt`, `lastRunId`, `lastSkipReason`"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data[].updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data[].nodes","type":"Node[]","description":"Nodes in execution order. `type` is one of evaluate, process, repurpose, quality-check, seo, image-generation, translate, publish, social-distribution"},{"name":"data[].nodes[].id","type":"string","description":"Node id"},{"name":"data[].nodes[].type","type":"string","description":"Node type"},{"name":"data[].nodes[].label","type":"string","description":"Display label"},{"name":"data[].nodes[].order","type":"number","description":"Execution order"},{"name":"data[].nodes[].enabled","type":"boolean","description":"Disabled nodes are skipped"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:read"],"missing":["workflows:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["workflows:read"]}],"x-required-scopes":["workflows:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const response = await fetch(`https://client-api.news-factory.app/v1/workflows?isActive=true`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/workflows?isActive=true\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nurl = \"https://client-api.news-factory.app/v1/workflows\"\nparams = {\n    \"isActive\": True\n}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, params=params, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflows/{id}":{"get":{"operationId":"getWorkflow","summary":"Get Workflow","description":"One workflow with its full graph: nodes (with their configuration and canvas position) and the edges connecting them.","tags":["Workflows"],"parameters":[{"name":"id","in":"path","required":true,"description":"The workflow id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7a0"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The workflow graph","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"Workflow","description":"The workflow","properties":{"id":{"type":"string","description":"Unique workflow identifier"},"name":{"type":"string","description":"Workflow name"},"description":{"type":["string","null"],"description":"Description"},"isActive":{"type":"boolean","description":"Inactive workflows cannot be run"},"isDefault":{"type":"boolean","description":"The Topic Workspace's default workflow"},"topicKey":{"type":"string","description":"Topic Workspace the workflow belongs to"},"feedIds":{"type":"array","items":{"type":"string"},"description":"The workflow's own feed list, up to 10 feed ids. Absent when the workflow reads the Topic Workspace's followed feeds"},"triggerConfig":{"type":"object","description":"`{ type: manual | scheduled, schedule?: { frequency, days, times, timezone } }`"},"scheduleState":{"type":["object","null"],"description":"Server managed schedule state: `nextRunAt`, `lastTriggeredAt`, `lastRunId`, `lastSkipReason`"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update"},"nodes":{"type":"array","items":{"type":"object","x-type":"Node","properties":{"id":{"type":"string","description":"Node id"},"type":{"type":"string","description":"Node type"},"label":{"type":"string","description":"Display label"},"order":{"type":"number","description":"Execution order"},"enabled":{"type":"boolean","description":"Disabled nodes are skipped"},"config":{"type":"object","description":"Node configuration (thresholds, languages, providers, prompts)"},"position":{"type":"object","description":"Canvas position `{ x, y }`"}}},"description":"Nodes in execution order. `type` is one of evaluate, process, repurpose, quality-check, seo, image-generation, translate, publish, social-distribution"},"edges":{"type":"array","items":{"type":"object","x-type":"Edge"},"description":"Connections: `{ id, sourceNodeId, targetNodeId }`"}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f7a0","name":"Daily AI digest","description":"Evaluate new articles, write stories, check quality, translate to German.","isActive":true,"isDefault":true,"topicKey":"tech","triggerConfig":{"type":"scheduled","schedule":{"frequency":"daily","days":["mon","tue","wed","thu","fri","sat","sun"],"times":["06:00"],"timezone":"Europe/London"}},"scheduleState":{"nextRunAt":"2026-09-06T05:00:00.000Z","lastTriggeredAt":"2026-09-05T05:00:00.000Z","lastRunId":"68b9b7c2f1a2b3c4d5e6f7b0","lastSkipReason":null},"nodes":[{"id":"68b9b7c2f1a2b3c4d5e6f7a1","type":"evaluate","label":"Evaluate articles","order":0,"enabled":true,"position":{"x":0,"y":0},"config":{"scoreThreshold":60,"processLimit":25}},{"id":"68b9b7c2f1a2b3c4d5e6f7a2","type":"repurpose","label":"Write stories","order":1,"enabled":true,"position":{"x":320,"y":0},"config":{"languageCode":"en-US"}},{"id":"68b9b7c2f1a2b3c4d5e6f7a3","type":"quality-check","label":"Quality check","order":2,"enabled":true,"position":{"x":640,"y":0},"config":{"effort":"normal"}},{"id":"68b9b7c2f1a2b3c4d5e6f7a4","type":"translate","label":"Translate","order":3,"enabled":true,"position":{"x":960,"y":0},"config":{"targetLanguages":["de-DE"]}}],"createdAt":"2026-08-01T09:00:00.000Z","updatedAt":"2026-09-02T18:00:00.000Z","edges":[{"id":"68b9b7c2f1a2b3c4d5e6f7c1","sourceNodeId":"68b9b7c2f1a2b3c4d5e6f7a1","targetNodeId":"68b9b7c2f1a2b3c4d5e6f7a2"},{"id":"68b9b7c2f1a2b3c4d5e6f7c2","sourceNodeId":"68b9b7c2f1a2b3c4d5e6f7a2","targetNodeId":"68b9b7c2f1a2b3c4d5e6f7a3"},{"id":"68b9b7c2f1a2b3c4d5e6f7c3","sourceNodeId":"68b9b7c2f1a2b3c4d5e6f7a3","targetNodeId":"68b9b7c2f1a2b3c4d5e6f7a4"}]}}}},"x-fields":[{"name":"data","type":"Workflow","description":"The workflow"},{"name":"data.id","type":"string","description":"Unique workflow identifier"},{"name":"data.name","type":"string","description":"Workflow name"},{"name":"data.description","type":"string | null","description":"Description"},{"name":"data.isActive","type":"boolean","description":"Inactive workflows cannot be run"},{"name":"data.isDefault","type":"boolean","description":"The Topic Workspace's default workflow"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the workflow belongs to"},{"name":"data.feedIds","type":"string[] | undefined","description":"The workflow's own feed list, up to 10 feed ids. Absent when the workflow reads the Topic Workspace's followed feeds"},{"name":"data.triggerConfig","type":"object","description":"`{ type: manual | scheduled, schedule?: { frequency, days, times, timezone } }`"},{"name":"data.scheduleState","type":"object | null","description":"Server managed schedule state: `nextRunAt`, `lastTriggeredAt`, `lastRunId`, `lastSkipReason`"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"data.updatedAt","type":"string","description":"ISO 8601 timestamp of the last update"},{"name":"data.nodes","type":"Node[]","description":"Nodes in execution order. `type` is one of evaluate, process, repurpose, quality-check, seo, image-generation, translate, publish, social-distribution"},{"name":"data.nodes[].id","type":"string","description":"Node id"},{"name":"data.nodes[].type","type":"string","description":"Node type"},{"name":"data.nodes[].label","type":"string","description":"Display label"},{"name":"data.nodes[].order","type":"number","description":"Execution order"},{"name":"data.nodes[].enabled","type":"boolean","description":"Disabled nodes are skipped"},{"name":"data.nodes[].config","type":"object","description":"Node configuration (thresholds, languages, providers, prompts)"},{"name":"data.nodes[].position","type":"object","description":"Canvas position `{ x, y }`"},{"name":"data.edges","type":"Edge[]","description":"Connections: `{ id, sourceNodeId, targetNodeId }`"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:read"],"missing":["workflows:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["workflows:read"]}],"x-required-scopes":["workflows:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f7a0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflows/${id}`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/workflows/68b9b7c2f1a2b3c4d5e6f7a0\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f7a0\"\nurl = f\"https://client-api.news-factory.app/v1/workflows/{id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflows/{id}/run":{"post":{"operationId":"runWorkflow","summary":"Run Workflow","description":"Start a run. The run reads the workflow's own feeds (or the Topic Workspace's followed feeds when it has no list) and snapshots their unevaluated articles as its input unless you pass explicit ids. Only one run can be active per organization: starting another answers 409 with the active run id. Follow progress with the workflow run endpoints. Runs that write stories cost AI credits.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"id","in":"path","required":true,"description":"The workflow id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7a0"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"requestBody":{"description":"Optional input","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"input":{"type":"object","description":"`{ feedArticleIds?: string[], storyIds?: string[] }` to run on specific items instead of the unevaluated pool"}}},"example":{}}},"x-fields":[{"name":"input","type":"object","required":false,"description":"`{ feedArticleIds?: string[], storyIds?: string[] }` to run on specific items instead of the unevaluated pool"}]},"responses":{"202":{"description":"Run started","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"runId":{"type":"string","description":"The new run"},"workflowId":{"type":"string","description":"The workflow"},"status":{"type":"string","description":"queued or running"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"runId":"68b9b7c2f1a2b3c4d5e6f7b0","workflowId":"68b9b7c2f1a2b3c4d5e6f7a0","status":"running"},"message":"Workflow run started"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.runId","type":"string","description":"The new run"},{"name":"data.workflowId","type":"string","description":"The workflow"},{"name":"data.status","type":"string","description":"queued or running"},{"name":"message","type":"string","description":"Success message"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"Workflow is not active","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:run needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:run"],"missing":["workflows:run"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"CONFLICT","message":"A workflow run is already in progress","details":{"code":"run_in_progress","runId":"68b9b7c2f1a2b3c4d5e6f7b0","workflowId":"68b9b7c2f1a2b3c4d5e6f7a0"},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-conflict"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["workflows:run"]}],"x-required-scopes":["workflows:run"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f7a0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflows/${id}/run`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...',\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({})\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/workflows/68b9b7c2f1a2b3c4d5e6f7a0/run\" \\\n  -H \"Authorization: Bearer nfk_xxx...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f7a0\"\nurl = f\"https://client-api.news-factory.app/v1/workflows/{id}/run\"\npayload = {}\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, json=payload, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflows/{id}/runs":{"get":{"operationId":"listWorkflowRuns","summary":"List Workflow Runs","description":"Runs of one workflow, newest first.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"id","in":"path","required":true,"description":"The workflow id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7a0"]}},{"name":"status","in":"query","required":false,"description":"Only runs in this status","schema":{"type":"string","enum":["queued","running","paused","completed","failed","cancelled"],"examples":["completed"]}},{"name":"limit","in":"query","required":false,"description":"Page size (default 20, max 100)","schema":{"type":"number","examples":[20]}},{"name":"page","in":"query","required":false,"description":"Zero-based page number (default 0)","schema":{"type":"number","examples":[0]}},{"name":"order","in":"query","required":false,"description":"Sort order as `field ASC|DESC` (default createdAt DESC). Allowed fields: createdAt, startedAt, completedAt.","schema":{"type":"string","enum":["createdAt DESC","startedAt DESC","completedAt DESC"],"examples":["createdAt DESC"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"A page of runs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"WorkflowRun","properties":{"id":{"type":"string","description":"Run id"},"workflowId":{"type":"string","description":"Workflow that ran"},"status":{"type":"string","description":"queued, running, paused, completed, failed or cancelled"},"triggeredBy":{"type":"string","description":"manual or scheduled"},"topicKey":{"type":"string","description":"Topic Workspace the run worked on"},"input":{"type":"object","description":"Snapshot the run started from: `feedArticleIds[]`, `storyIds[]`"},"output":{"type":["object","null"],"description":"`processedCount`, `filteredCount`, `failedCount`, and on a completed run whose steps lost items `issues[]` (`nodeId`, `label`, `failed`, `passed`): the run still produced output, so it is completed, not failed"},"error":{"type":["string","null"],"description":"Failure reason"},"metadata":{"type":"object","description":"`totalDuration` in seconds and per node overrides"},"startedAt":{"type":["string","null"],"description":"ISO 8601 start time"},"completedAt":{"type":["string","null"],"description":"ISO 8601 end time"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"}}},"description":"Runs"},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","description":"Zero-based page returned"},"limit":{"type":"number","description":"Page size used"},"hasMore":{"type":"boolean","description":"Whether a next page may exist (the page was full)"},"workflowId":{"type":"string","description":"The workflow"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f7b0","workflowId":"68b9b7c2f1a2b3c4d5e6f7a0","status":"completed","triggeredBy":"scheduled","topicKey":"tech","input":{"feedArticleIds":["68b9b7c2f1a2b3c4d5e6f780","68b9b7c2f1a2b3c4d5e6f781"]},"output":{"processedCount":2,"filteredCount":1,"failedCount":0},"error":null,"metadata":{"totalDuration":509},"startedAt":"2026-09-05T05:00:02.000Z","completedAt":"2026-09-05T05:08:31.000Z","createdAt":"2026-09-05T05:00:01.000Z"}],"meta":{"page":0,"limit":20,"hasMore":false,"workflowId":"68b9b7c2f1a2b3c4d5e6f7a0"}}}},"x-fields":[{"name":"data","type":"WorkflowRun[]","description":"Runs"},{"name":"data[].id","type":"string","description":"Run id"},{"name":"data[].workflowId","type":"string","description":"Workflow that ran"},{"name":"data[].status","type":"string","description":"queued, running, paused, completed, failed or cancelled"},{"name":"data[].triggeredBy","type":"string","description":"manual or scheduled"},{"name":"data[].topicKey","type":"string","description":"Topic Workspace the run worked on"},{"name":"data[].input","type":"object","description":"Snapshot the run started from: `feedArticleIds[]`, `storyIds[]`"},{"name":"data[].output","type":"object | null","description":"`processedCount`, `filteredCount`, `failedCount`, and on a completed run whose steps lost items `issues[]` (`nodeId`, `label`, `failed`, `passed`): the run still produced output, so it is completed, not failed"},{"name":"data[].error","type":"string | null","description":"Failure reason"},{"name":"data[].metadata","type":"object","description":"`totalDuration` in seconds and per node overrides"},{"name":"data[].startedAt","type":"string | null","description":"ISO 8601 start time"},{"name":"data[].completedAt","type":"string | null","description":"ISO 8601 end time"},{"name":"data[].createdAt","type":"string","description":"ISO 8601 creation timestamp"},{"name":"meta","type":"object","description":"Pagination metadata"},{"name":"meta.page","type":"number","description":"Zero-based page returned"},{"name":"meta.limit","type":"number","description":"Page size used"},{"name":"meta.hasMore","type":"boolean","description":"Whether a next page may exist (the page was full)"},{"name":"meta.workflowId","type":"string","description":"The workflow"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"status must be one of: queued, running, paused, completed, failed, cancelled","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:read"],"missing":["workflows:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}}},"security":[{"apiKey":["workflows:read"]}],"x-required-scopes":["workflows:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const id = \"68b9b7c2f1a2b3c4d5e6f7a0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflows/${id}/runs`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/workflows/68b9b7c2f1a2b3c4d5e6f7a0/runs\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nid = \"68b9b7c2f1a2b3c4d5e6f7a0\"\nurl = f\"https://client-api.news-factory.app/v1/workflows/{id}/runs\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflow-runs/{runId}":{"get":{"operationId":"getWorkflowRun","summary":"Get Workflow Run","description":"One run with its status, input snapshot, output counts and timing. Poll it while a run is active; list its node runs for per-step progress.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"runId","in":"path","required":true,"description":"The workflow run id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7b0"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"The run","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","x-type":"WorkflowRun","description":"The run","properties":{"id":{"type":"string","description":"Run id"},"workflowId":{"type":"string","description":"Workflow that ran"},"status":{"type":"string","description":"queued, running, paused, completed, failed or cancelled"},"triggeredBy":{"type":"string","description":"manual or scheduled"},"topicKey":{"type":"string","description":"Topic Workspace the run worked on"},"input":{"type":"object","description":"Snapshot the run started from: `feedArticleIds[]`, `storyIds[]`"},"output":{"type":["object","null"],"description":"`processedCount`, `filteredCount`, `failedCount`, and on a completed run whose steps lost items `issues[]` (`nodeId`, `label`, `failed`, `passed`): the run still produced output, so it is completed, not failed"},"error":{"type":["string","null"],"description":"Failure reason"},"metadata":{"type":"object","description":"`totalDuration` in seconds and per node overrides"},"startedAt":{"type":["string","null"],"description":"ISO 8601 start time"},"completedAt":{"type":["string","null"],"description":"ISO 8601 end time"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp"}}}}},"example":{"data":{"id":"68b9b7c2f1a2b3c4d5e6f7b0","workflowId":"68b9b7c2f1a2b3c4d5e6f7a0","status":"completed","triggeredBy":"scheduled","topicKey":"tech","input":{"feedArticleIds":["68b9b7c2f1a2b3c4d5e6f780","68b9b7c2f1a2b3c4d5e6f781"]},"output":{"processedCount":2,"filteredCount":1,"failedCount":0},"error":null,"metadata":{"totalDuration":509},"startedAt":"2026-09-05T05:00:02.000Z","completedAt":"2026-09-05T05:08:31.000Z","createdAt":"2026-09-05T05:00:01.000Z"}}}},"x-fields":[{"name":"data","type":"WorkflowRun","description":"The run"},{"name":"data.id","type":"string","description":"Run id"},{"name":"data.workflowId","type":"string","description":"Workflow that ran"},{"name":"data.status","type":"string","description":"queued, running, paused, completed, failed or cancelled"},{"name":"data.triggeredBy","type":"string","description":"manual or scheduled"},{"name":"data.topicKey","type":"string","description":"Topic Workspace the run worked on"},{"name":"data.input","type":"object","description":"Snapshot the run started from: `feedArticleIds[]`, `storyIds[]`"},{"name":"data.output","type":"object | null","description":"`processedCount`, `filteredCount`, `failedCount`, and on a completed run whose steps lost items `issues[]` (`nodeId`, `label`, `failed`, `passed`): the run still produced output, so it is completed, not failed"},{"name":"data.error","type":"string | null","description":"Failure reason"},{"name":"data.metadata","type":"object","description":"`totalDuration` in seconds and per node overrides"},{"name":"data.startedAt","type":"string | null","description":"ISO 8601 start time"},{"name":"data.completedAt","type":"string | null","description":"ISO 8601 end time"},{"name":"data.createdAt","type":"string","description":"ISO 8601 creation timestamp"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:read"],"missing":["workflows:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow run not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow run not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["workflows:read"]}],"x-required-scopes":["workflows:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const runId = \"68b9b7c2f1a2b3c4d5e6f7b0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflow-runs/${runId}`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/workflow-runs/68b9b7c2f1a2b3c4d5e6f7b0\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nrun_id = \"68b9b7c2f1a2b3c4d5e6f7b0\"\nurl = f\"https://client-api.news-factory.app/v1/workflow-runs/{run_id}\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflow-runs/{runId}/node-runs":{"get":{"operationId":"listNodeRuns","summary":"List Node Runs","description":"Per node progress of a run in execution order, with item counts and the produced items (article and story ids) in `metadata.outputItems`.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"runId","in":"path","required":true,"description":"The workflow run id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7b0"]}},{"name":"status","in":"query","required":false,"description":"Only node runs in this status","schema":{"type":"string","enum":["pending","draft","preparing","executing","paused","finished","failed","skipped","cancelled"],"examples":["finished"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Node runs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","x-type":"NodeRun","properties":{"id":{"type":"string","description":"Node run id"},"workflowRunId":{"type":"string","description":"Parent run"},"workflowNodeId":{"type":"string","description":"Node definition"},"nodeType":{"type":"string","description":"evaluate, process, repurpose, quality-check, seo, image-generation, translate, publish, social-distribution"},"nodeLabel":{"type":"string","description":"Node label at run time"},"status":{"type":"string","description":"pending, draft, preparing, executing, paused, finished, failed, skipped or cancelled"},"progress":{"type":"number","description":"0 to 100"},"itemsIn":{"type":"number","description":"Items received"},"itemsOut":{"type":"number","description":"Items produced"},"itemsPassed":{"type":"number","description":"Items that passed the node's filter"},"itemsFailed":{"type":"number","description":"Items that failed"},"error":{"type":["string","null"],"description":"Failure reason"},"metadata":{"type":"object","description":"`duration` in seconds, `llmTokensUsed`, and `outputItems[]` (the produced articles or stories with their ids)"},"startedAt":{"type":["string","null"],"description":"ISO 8601 start time"},"completedAt":{"type":["string","null"],"description":"ISO 8601 end time"}}},"description":"Node runs"},"meta":{"type":"object","properties":{"count":{"type":"number","description":"Node runs returned"},"runId":{"type":"string","description":"The run"}}}}},"example":{"data":[{"id":"68b9b7c2f1a2b3c4d5e6f7d0","workflowRunId":"68b9b7c2f1a2b3c4d5e6f7b0","workflowNodeId":"68b9b7c2f1a2b3c4d5e6f7a2","nodeType":"repurpose","nodeLabel":"Write stories","status":"finished","progress":100,"itemsIn":2,"itemsOut":2,"itemsPassed":2,"itemsFailed":0,"error":null,"metadata":{"duration":301,"llmTokensUsed":18402,"outputItems":[{"id":"68b9b7c2f1a2b3c4d5e6f700","type":"story","data":{"title":"AI Revolution in Healthcare"}}]},"startedAt":"2026-09-05T05:01:00.000Z","completedAt":"2026-09-05T05:06:01.000Z"}],"meta":{"count":1,"runId":"68b9b7c2f1a2b3c4d5e6f7b0"}}}},"x-fields":[{"name":"data","type":"NodeRun[]","description":"Node runs"},{"name":"data[].id","type":"string","description":"Node run id"},{"name":"data[].workflowRunId","type":"string","description":"Parent run"},{"name":"data[].workflowNodeId","type":"string","description":"Node definition"},{"name":"data[].nodeType","type":"string","description":"evaluate, process, repurpose, quality-check, seo, image-generation, translate, publish, social-distribution"},{"name":"data[].nodeLabel","type":"string","description":"Node label at run time"},{"name":"data[].status","type":"string","description":"pending, draft, preparing, executing, paused, finished, failed, skipped or cancelled"},{"name":"data[].progress","type":"number","description":"0 to 100"},{"name":"data[].itemsIn","type":"number","description":"Items received"},{"name":"data[].itemsOut","type":"number","description":"Items produced"},{"name":"data[].itemsPassed","type":"number","description":"Items that passed the node's filter"},{"name":"data[].itemsFailed","type":"number","description":"Items that failed"},{"name":"data[].error","type":"string | null","description":"Failure reason"},{"name":"data[].metadata","type":"object","description":"`duration` in seconds, `llmTokensUsed`, and `outputItems[]` (the produced articles or stories with their ids)"},{"name":"data[].startedAt","type":"string | null","description":"ISO 8601 start time"},{"name":"data[].completedAt","type":"string | null","description":"ISO 8601 end time"},{"name":"meta.count","type":"number","description":"Node runs returned"},{"name":"meta.runId","type":"string","description":"The run"}]},"400":{"description":"Validation error: the request is missing or misusing a field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_ERROR","message":"status must be one of: pending, draft, preparing, executing, paused, finished, failed, skipped, cancelled","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-validation-error"}}}}},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:read needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:read"],"missing":["workflows:read"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow run not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow run not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}}},"security":[{"apiKey":["workflows:read"]}],"x-required-scopes":["workflows:read"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const runId = \"68b9b7c2f1a2b3c4d5e6f7b0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflow-runs/${runId}/node-runs`, {\n  method: 'GET',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data);"},{"lang":"Shell","label":"cURL","source":"curl -X GET \"https://client-api.news-factory.app/v1/workflow-runs/68b9b7c2f1a2b3c4d5e6f7b0/node-runs\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nrun_id = \"68b9b7c2f1a2b3c4d5e6f7b0\"\nurl = f\"https://client-api.news-factory.app/v1/workflow-runs/{run_id}/node-runs\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.get(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"])"}]}},"/workflow-runs/{runId}/cancel":{"post":{"operationId":"cancelWorkflowRun","summary":"Stop Run","description":"Stop a queued, running or paused run. The current node finishes its in-flight operation, then the run stops and its status becomes `cancelled`. Finished runs answer 409.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"runId","in":"path","required":true,"description":"The workflow run id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7b0"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Workflow run cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"runId":{"type":"string","description":"The run"},"status":{"type":"string","description":"New run status"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"runId":"68b9b7c2f1a2b3c4d5e6f7b0","status":"cancelled"},"message":"Workflow run cancelled"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.runId","type":"string","description":"The run"},{"name":"data.status","type":"string","description":"New run status"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:run needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:run"],"missing":["workflows:run"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow run not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow run not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"CONFLICT","message":"Run already finished (completed)","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-conflict"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["workflows:run"]}],"x-required-scopes":["workflows:run"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const runId = \"68b9b7c2f1a2b3c4d5e6f7b0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflow-runs/${runId}/cancel`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data.status);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/workflow-runs/68b9b7c2f1a2b3c4d5e6f7b0/cancel\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nrun_id = \"68b9b7c2f1a2b3c4d5e6f7b0\"\nurl = f\"https://client-api.news-factory.app/v1/workflow-runs/{run_id}/cancel\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"].status)"}]}},"/workflow-runs/{runId}/pause":{"post":{"operationId":"pauseWorkflowRun","summary":"Pause Run","description":"Pause a running run before its next node starts. Resume it later or stop it.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"runId","in":"path","required":true,"description":"The workflow run id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7b0"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Workflow run paused","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"runId":{"type":"string","description":"The run"},"status":{"type":"string","description":"New run status"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"runId":"68b9b7c2f1a2b3c4d5e6f7b0","status":"paused"},"message":"Workflow run paused"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.runId","type":"string","description":"The run"},{"name":"data.status","type":"string","description":"New run status"},{"name":"message","type":"string","description":"Success message"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:run needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:run"],"missing":["workflows:run"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow run not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow run not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["workflows:run"]}],"x-required-scopes":["workflows:run"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const runId = \"68b9b7c2f1a2b3c4d5e6f7b0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflow-runs/${runId}/pause`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data.status);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/workflow-runs/68b9b7c2f1a2b3c4d5e6f7b0/pause\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nrun_id = \"68b9b7c2f1a2b3c4d5e6f7b0\"\nurl = f\"https://client-api.news-factory.app/v1/workflow-runs/{run_id}/pause\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"].status)"}]}},"/workflow-runs/{runId}/resume":{"post":{"operationId":"resumeWorkflowRun","summary":"Resume Run","description":"Resume a paused run from the node it paused on.","tags":["Workflows"],"x-group":"Runs","parameters":[{"name":"runId","in":"path","required":true,"description":"The workflow run id","schema":{"type":"string","examples":["68b9b7c2f1a2b3c4d5e6f7b0"]}},{"name":"X-Topic-Key","in":"header","required":false,"description":"Topic Workspace to act on. Not needed with a key bound to one Topic Workspace (sending another one is refused with INVALID_TOPIC). With a key for all Topic Workspaces it picks one; without it the organization's first Topic Workspace is used. `GET /me` lists the Topic Workspaces a key can use.","schema":{"type":"string","examples":["tech"]}}],"responses":{"200":{"description":"Workflow run resumed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always true"},"data":{"type":"object","properties":{"runId":{"type":"string","description":"The run"},"status":{"type":"string","description":"New run status"},"workflowId":{"type":"string","description":"The workflow"},"resumeNodeId":{"type":"string","description":"Node the run continues from"}}},"message":{"type":"string","description":"Success message"}}},"example":{"success":true,"data":{"runId":"68b9b7c2f1a2b3c4d5e6f7b0","status":"running","workflowId":"68b9b7c2f1a2b3c4d5e6f7a0","resumeNodeId":"68b9b7c2f1a2b3c4d5e6f7a3"},"message":"Workflow run resumed"}}},"x-fields":[{"name":"success","type":"boolean","description":"Always true"},{"name":"data.runId","type":"string","description":"The run"},{"name":"data.status","type":"string","description":"New run status"},{"name":"message","type":"string","description":"Success message"},{"name":"data.workflowId","type":"string","description":"The workflow"},{"name":"data.resumeNodeId","type":"string","description":"Node the run continues from"}]},"401":{"description":"Unauthorized: missing, invalid, revoked or expired credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"UNAUTHORIZED","message":"Invalid, revoked or expired API key. Create a new key in the app under Organization settings, API keys.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-unauthorized"}}}}},"403":{"description":"The API key lacks a scope this endpoint needs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"INSUFFICIENT_SCOPE","message":"This API key lacks the scope workflows:run needed for this endpoint. Edit the key's access in the app or use another key.","details":{"required":["workflows:run"],"missing":["workflows:run"],"granted":["stories:read"]},"requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-insufficient-scope"}}}}},"404":{"description":"Workflow run not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Workflow run not found","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-not-found"}}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"CONFLICT","message":"Run is not paused","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-conflict"}}}}},"503":{"description":"API keys cannot be verified or signed right now; retry in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"GATEWAY_MISCONFIGURED","message":"API keys cannot be verified right now. Retry in a minute; if it persists, contact support with the requestId.","requestId":"6f1c2a9e-3b7d-4c41-9f0e-2d8a5b7c9e10","docs":"https://client-api.news-factory.app/docs#error-gateway-misconfigured"}}}}}},"security":[{"apiKey":["workflows:run"]}],"x-required-scopes":["workflows:run"],"x-codeSamples":[{"lang":"JavaScript","label":"JavaScript (fetch)","source":"const runId = \"68b9b7c2f1a2b3c4d5e6f7b0\";\nconst response = await fetch(`https://client-api.news-factory.app/v1/workflow-runs/${runId}/resume`, {\n  method: 'POST',\n  headers: {\n    'Authorization': 'Bearer nfk_xxx...'\n  }\n});\n\nconst result = await response.json();\nconsole.log(result.data.status);"},{"lang":"Shell","label":"cURL","source":"curl -X POST \"https://client-api.news-factory.app/v1/workflow-runs/68b9b7c2f1a2b3c4d5e6f7b0/resume\" \\\n  -H \"Authorization: Bearer nfk_xxx...\""},{"lang":"Python","label":"Python (requests)","source":"import requests\n\nrun_id = \"68b9b7c2f1a2b3c4d5e6f7b0\"\nurl = f\"https://client-api.news-factory.app/v1/workflow-runs/{run_id}/resume\"\nheaders = {\n    \"Authorization\": \"Bearer nfk_xxx...\"\n}\n\nresponse = requests.post(url, headers=headers)\nresult = response.json()\nprint(result[\"data\"].status)"}]}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"nfk_ API key","description":"An API key from the app (Organization settings, API keys). Scopes: `stories:read` Read stories, their media, quality-check and SEO audit history; `stories:write` Create, update and delete stories; upload, attach and detach media; set thumbnails; `content-sources:read` Read content sources; `content-sources:write` Create, update, delete and scrape content sources; `feeds:read` Read followed feeds, the feed catalogue, feed articles and the scheduler status; `feeds:write` Follow and unfollow feeds, fetch a feed now; `workflows:read` Read workflows, workflow runs and node runs; `workflows:run` Start, pause, resume and stop workflow runs; `ai:generate` Use AI tools that spend AI credits (always together with the scope of what they read or change)."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","docs"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","INSUFFICIENT_SCOPE","PLAN_REQUIRED","INVALID_TOPIC","NO_TOPIC_WORKSPACE","NOT_FOUND","METHOD_NOT_ALLOWED","VALIDATION_ERROR","CONFLICT","PLAN_LIMIT_REACHED","RATE_LIMIT_EXCEEDED","DAILY_QUOTA_EXCEEDED","LLM_QUOTA_EXCEEDED","UPSTREAM_ERROR","GATEWAY_MISCONFIGURED","INTERNAL_ERROR"]},"message":{"type":"string","description":"What went wrong and what to do, in English"},"details":{"type":"object","description":"Machine-readable specifics: missing scopes, valid topics, limits, the conflicting record's id"},"requestId":{"type":"string","description":"Quote it to support; also in the X-Request-Id header"},"docs":{"type":"string","format":"uri","description":"Where this error is explained"}}}}}}},"x-scopes":{"stories:read":"Read stories, their media, quality-check and SEO audit history","stories:write":"Create, update and delete stories; upload, attach and detach media; set thumbnails","content-sources:read":"Read content sources","content-sources:write":"Create, update, delete and scrape content sources","feeds:read":"Read followed feeds, the feed catalogue, feed articles and the scheduler status","feeds:write":"Follow and unfollow feeds, fetch a feed now","workflows:read":"Read workflows, workflow runs and node runs","workflows:run":"Start, pause, resume and stop workflow runs","ai:generate":"Use AI tools that spend AI credits (always together with the scope of what they read or change)"}}