{"openapi":"3.1.0","info":{"title":"Content Gateway Prototype API (beta)","version":"0.6.0","description":"Experimental service developed by BMJ using BMJ's open corpus. Provides customer-scoped collections, article trust transparency, retrieval, grounded and rolling answers, entitled BMJ chunks and authenticated ingestion. This is not a live BMJ product or author service."},"servers":[{"url":"https://dev.bmj-content-hub.com"}],"paths":{"/v1/articles":{"get":{"summary":"Search or browse articles","security":[{"customerApiKey":[]}],"parameters":[{"in":"query","name":"query","description":"Optional lexical query over normalized title, abstract, body, and publisher keywords; bibliography and identifiable inline bibliographic citations are excluded. Matches any unquoted term or complete quoted phrase. Explicit uppercase Boolean operators are rejected; no automatic typo correction or quorum relaxation.","schema":{"type":"string","maxLength":2000}},{"in":"query","name":"rank","schema":{"type":"string","enum":["bm25","vector","hybrid"],"default":"bm25"}},{"in":"query","name":"search_mode","description":"terms retains lexical semantics. bibliographic interprets author/year syntax such as Akiyama 2025 or author:\"Akiyama\" year:2025 plus optional topic words. auto uses only permitted metadata to recognise names and asks for clarification on ambiguous queries. No silent constraint relaxation.","schema":{"type":"string","enum":["terms","bibliographic","auto"],"default":"terms"}},{"in":"query","name":"order","schema":{"type":"string","enum":["desc","asc"],"default":"desc"}},{"in":"query","name":"include","schema":{"type":"string","enum":["frontmatter"]}},{"in":"query","name":"include_unmatched","schema":{"type":"boolean","default":false}},{"in":"query","name":"collection_id","schema":{"type":"string","maxLength":160}},{"in":"query","name":"journal_ids","description":"Comma-separated journal IDs.","schema":{"type":"string","maxLength":2000}},{"in":"query","name":"author","description":"Literal match within one publisher author, including structured surname, given-name initials and reversed-name aliases; no stemming or identity guessing.","schema":{"type":"string","maxLength":128}},{"in":"query","name":"year","description":"Article publication year; never a year mentioned only in text or references.","schema":{"type":"integer","minimum":1000,"maximum":2199}},{"in":"query","name":"date_basis","description":"Basis for year: any publisher publication date (including primary), online, print, or the primary published_at. Date-range filters always constrain the primary date.","schema":{"type":"string","enum":["any","online","print","primary"],"default":"any"}},{"in":"query","name":"keyword","description":"Normalized literal match over publisher keywords; no stemming or synonym expansion.","schema":{"type":"string","maxLength":128}},{"in":"query","name":"published_from","schema":{"type":"string","format":"date"}},{"in":"query","name":"published_to","schema":{"type":"string","format":"date"}},{"in":"query","name":"page_size","schema":{"type":"integer","minimum":1,"maximum":300,"default":100}},{"in":"query","name":"cursor","schema":{"type":"string","minLength":1,"maxLength":2048}}],"responses":{"200":{"description":"Scoped article hits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleSearchResponse"}}}},"400":{"description":"Invalid query, filter, format, or cursor."},"401":{"description":"Missing or invalid customer key."},"403":{"description":"Operation or scope denied."},"404":{"description":"Article or version not found in scope."},"409":{"description":"Cursor snapshot is stale; restart from the first page."},"413":{"description":"Representation exceeds the response limit."},"429":{"description":"Rate or quota limit reached; inspect Retry-After."},"501":{"description":"Requested rank is reserved and unavailable."},"503":{"description":"Publication service unavailable."}}}},"/v1/frontmatter":{"get":{"summary":"Read article front matter","security":[{"customerApiKey":[]}],"parameters":[{"in":"query","name":"ids","description":"Comma-separated IDs, maximum 50; preserves order and omits unknown/out-of-scope IDs.","schema":{"type":"string","maxLength":4000}},{"in":"query","name":"collection_id","schema":{"type":"string","maxLength":160}},{"in":"query","name":"journal_ids","description":"Comma-separated journal IDs.","schema":{"type":"string","maxLength":2000}},{"in":"query","name":"author","description":"Literal match within one publisher author, including structured surname, given-name initials and reversed-name aliases; no stemming or identity guessing.","schema":{"type":"string","maxLength":128}},{"in":"query","name":"year","description":"Article publication year; never a year mentioned only in text or references.","schema":{"type":"integer","minimum":1000,"maximum":2199}},{"in":"query","name":"date_basis","description":"Basis for year: any publisher publication date (including primary), online, print, or the primary published_at. Date-range filters always constrain the primary date.","schema":{"type":"string","enum":["any","online","print","primary"],"default":"any"}},{"in":"query","name":"keyword","description":"Normalized literal match over publisher keywords; no stemming or synonym expansion.","schema":{"type":"string","maxLength":128}},{"in":"query","name":"published_from","schema":{"type":"string","format":"date"}},{"in":"query","name":"published_to","schema":{"type":"string","format":"date"}},{"in":"query","name":"page_size","schema":{"type":"integer","minimum":1,"maximum":300,"default":100}},{"in":"query","name":"cursor","schema":{"type":"string","minLength":1,"maxLength":2048}}],"responses":{"200":{"description":"Front matter page or ordered ID lookup.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrontmatterPage"}}}},"400":{"description":"Invalid query, filter, format, or cursor."},"401":{"description":"Missing or invalid customer key."},"403":{"description":"Operation or scope denied."},"404":{"description":"Article or version not found in scope."},"409":{"description":"Cursor snapshot is stale; restart from the first page."},"413":{"description":"Representation exceeds the response limit."},"429":{"description":"Rate or quota limit reached; inspect Retry-After."},"501":{"description":"Requested rank is reserved and unavailable."},"503":{"description":"Publication service unavailable."}}}},"/v1/frontmatter/{id}":{"get":{"summary":"Read one article's front matter","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"Scoped article front matter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleFrontmatter"}}}},"400":{"description":"Invalid query, filter, format, or cursor."},"401":{"description":"Missing or invalid customer key."},"403":{"description":"Operation or scope denied."},"404":{"description":"Article or version not found in scope."},"409":{"description":"Cursor snapshot is stale; restart from the first page."},"413":{"description":"Representation exceeds the response limit."},"429":{"description":"Rate or quota limit reached; inspect Retry-After."},"501":{"description":"Requested rank is reserved and unavailable."},"503":{"description":"Publication service unavailable."}}}},"/v1/articles/{id}":{"get":{"summary":"Read a complete article","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","minLength":1,"maxLength":200}},{"in":"query","name":"format","schema":{"type":"string","enum":["jats","json","markdown"],"default":"jats"}},{"in":"query","name":"version_id","schema":{"type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"Complete machine-readable article; never silently truncated.","content":{"application/jats+xml":{},"application/json":{},"text/markdown":{}}},"400":{"description":"Invalid query, filter, format, or cursor."},"401":{"description":"Missing or invalid customer key."},"403":{"description":"Operation or scope denied."},"404":{"description":"Article or version not found in scope."},"409":{"description":"Cursor snapshot is stale; restart from the first page."},"413":{"description":"Representation exceeds the response limit."},"429":{"description":"Rate or quota limit reached; inspect Retry-After."},"501":{"description":"Requested rank is reserved and unavailable."},"503":{"description":"Publication service unavailable."}}}},"/v1/articles/search":{"post":{"summary":"Legacy article search","description":"Compatibility route; new integrations should use GET /v1/articles.","security":[{"customerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleSearchRequest"}}}},"responses":{"200":{"description":"Legacy search response."},"400":{"description":"Invalid query, filter, format, or cursor."},"401":{"description":"Missing or invalid customer key."},"403":{"description":"Operation or scope denied."},"404":{"description":"Article or version not found in scope."},"409":{"description":"Cursor snapshot is stale; restart from the first page."},"413":{"description":"Representation exceeds the response limit."},"429":{"description":"Rate or quota limit reached; inspect Retry-After."},"501":{"description":"Requested rank is reserved and unavailable."},"503":{"description":"Publication service unavailable."}}}},"/v1/articles/{id}/content":{"get":{"summary":"Legacy article content","description":"Compatibility route; new integrations should use GET /v1/articles/{id}.","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","minLength":1,"maxLength":200}},{"in":"query","name":"version_id","schema":{"type":"string","minLength":1,"maxLength":200}},{"in":"query","name":"section_id","schema":{"type":"string","minLength":1,"maxLength":200}},{"in":"query","name":"cursor","schema":{"type":"integer","minimum":0}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":100}},{"in":"query","name":"format","schema":{"type":"string","enum":["structured","markdown"],"default":"structured"}}],"responses":{"200":{"description":"Legacy content response."},"400":{"description":"Invalid query, filter, format, or cursor."},"401":{"description":"Missing or invalid customer key."},"403":{"description":"Operation or scope denied."},"404":{"description":"Article or version not found in scope."},"409":{"description":"Cursor snapshot is stale; restart from the first page."},"413":{"description":"Representation exceeds the response limit."},"429":{"description":"Rate or quota limit reached; inspect Retry-After."},"501":{"description":"Requested rank is reserved and unavailable."},"503":{"description":"Publication service unavailable."}}}},"/health":{"get":{"summary":"Service health","responses":{"200":{"description":"Healthy"}}}},"/v1/customer/context":{"get":{"summary":"Resolve the authenticated customer and active policy","description":"Reports the presenting key's selected operation_ids, transports and mcp_tools, plus an access array with the effective package, key and content-grant decision for every application operation. Inspect it before attempting a mode.","security":[{"customerApiKey":[]}],"responses":{"200":{"description":"Authenticated context, including the access array"},"401":{"description":"Invalid or revoked key"},"403":{"description":"Customer or scope denied"}}}},"/v1/customer/quota-check":{"post":{"summary":"Inspect customer-scoped search allowance without consuming it","security":[{"customerApiKey":[]}],"parameters":[{"in":"header","name":"X-Request-Id","schema":{"type":"string","minLength":8,"maxLength":128}}],"responses":{"200":{"description":"Quota available; probe refunded"},"429":{"description":"Quota denied"}}}},"/v1/customer/corpus-summary":{"get":{"summary":"Summarise the customer-scoped corpus","description":"Journal and topic facets, document/evidence counts, date coverage and the effective contract scope.","security":[{"customerApiKey":[]}],"parameters":[{"$ref":"#/components/parameters/JournalId"},{"$ref":"#/components/parameters/PublishedFrom"},{"$ref":"#/components/parameters/PublishedTo"}],"responses":{"200":{"description":"Scoped corpus summary"},"403":{"description":"Metadata entitlement denied"}}}},"/v1/collections":{"get":{"summary":"List published static and dynamic collections","description":"Counts are resolved against the authenticated customer's release and contract scope.","security":[{"customerApiKey":[]}],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"Published collections with current scoped counts"},"403":{"description":"Metadata entitlement denied"}}}},"/v1/collections/{collection_id}":{"get":{"summary":"Read one published collection definition","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"collection_id","required":true,"description":"Stable collection ID or slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"Collection and current scoped count"},"404":{"description":"Published collection not found"}}}},"/v1/collections/{collection_id}/documents":{"get":{"summary":"Resolve customer-scoped articles in a collection","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"collection_id","required":true,"description":"Stable collection ID or slug.","schema":{"type":"string"}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"Canonical article catalogue records"},"404":{"description":"Published collection not found"}}}},"/v1/articles/{document_id}/trust":{"get":{"summary":"Read one customer-scoped effective article trust profile","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"document_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"BMJ Article Trust Profile v1"},"404":{"description":"Missing or outside customer scope"}}}},"/v1/trust/schema":{"get":{"summary":"Read the public BMJ Article Trust Profile JSON Schema","responses":{"200":{"description":"JSON Schema draft 2020-12"}}}},"/v1/trust/markers":{"get":{"summary":"List the public typed trust-marker registry","responses":{"200":{"description":"Active trust marker definitions"}}}},"/v1/trust/articles/{document_id}":{"get":{"summary":"Read a public effective trust profile for an admitted article","parameters":[{"in":"path","name":"document_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"BMJ Article Trust Profile v1"},"404":{"description":"Admitted article not found"}}}},"/mcp":{"post":{"summary":"Call the authenticated stateless Streamable HTTP MCP server","description":"Implements MCP protocol negotiation and customer-scoped tools/resources. The active agreement and key must both permit MCP; tools are further limited by the key's selected operations and optional named-tool allowlist.","security":[{"customerApiKey":[]}],"externalDocs":{"url":"https://dev.bmj-content-hub.com/docs/mcp.md"},"parameters":[{"in":"header","name":"MCP-Protocol-Version","description":"Required for modern requests; use 2026-07-28.","schema":{"type":"string"}},{"in":"header","name":"Mcp-Method","description":"Required for modern requests and must equal the JSON-RPC method.","schema":{"type":"string"}},{"in":"header","name":"Mcp-Name","description":"Required for modern tools/call, prompts/get and resources/read requests.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"MCP JSON or event-stream response"},"400":{"description":"Invalid protocol envelope or routing headers"},"401":{"description":"Invalid or revoked customer key"},"403":{"description":"MCP connection method or operation denied"},"405":{"description":"Use POST for request-scoped MCP calls"}}}},"/v1/documents":{"get":{"summary":"Browse customer-scoped document metadata","security":[{"customerApiKey":[]}],"parameters":[{"$ref":"#/components/parameters/JournalId"},{"$ref":"#/components/parameters/PublishedFrom"},{"$ref":"#/components/parameters/PublishedTo"},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"in":"query","name":"cursor","schema":{"type":"string","pattern":"^[0-9]+$"}}],"responses":{"200":{"description":"Documents and a next cursor"},"403":{"description":"Metadata entitlement denied"}}}},"/v1/documents/{document_id}":{"get":{"summary":"Read one scoped document catalogue record","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"document_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Article metadata and topic assignments"},"404":{"description":"Missing or outside customer scope"}}}},"/v1/find":{"post":{"summary":"Lexical (BM25) search over article or paragraph text","description":"D1 FTS5 keyword search over titles+abstracts (level: article) or full-text paragraphs (level: paragraph), with highlighted snippets. No embedding, no AI Search hop; typically 100-300ms. Requires search:lexical.","security":[{"customerApiKey":[]}],"parameters":[{"in":"header","name":"X-Request-Id","schema":{"type":"string","minLength":8,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindRequest"}}}},"responses":{"200":{"description":"BM25-ranked hits with snippets and timings_ms"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvalidRequestError"}}}},"401":{"description":"Invalid or revoked key"},"403":{"description":"Customer or scope denied"},"429":{"description":"rate_limited (burst limiter, Retry-After: 10) or quota_denied (per-minute/day quota, Retry-After: 60 or 86400)","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaDeniedError"}}}},"503":{"description":"The search index could not complete the request"}}}},"/v1/search":{"post":{"summary":"Search the active governed evidence generation","description":"retrieval selects keyword (D1 FTS5), vector (Workers AI embedding + Vectorize) or hybrid (both, reciprocal-rank fused). backend can override the Worker's default retrieval stack (gateway or ai-search). The response reports retrieval_backend, retrieval_cache, per-stage timings_ms and any warnings (for example a degraded, unranked reranker fallback).","security":[{"customerApiKey":[]}],"parameters":[{"in":"header","name":"X-Request-Id","schema":{"type":"string","minLength":8,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Ranked evidence and execution provenance, including retrieval_backend, retrieval_cache, timings_ms and warnings"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvalidRequestError"}}}},"401":{"description":"Invalid or revoked key"},"403":{"description":"Customer or scope denied"},"429":{"description":"rate_limited (burst limiter, Retry-After: 10) or quota_denied (per-minute/day quota, Retry-After: 60 or 86400)","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaDeniedError"}}}},"503":{"description":"Configured physical generation unavailable or not ready"}}}},"/v1/answers":{"post":{"summary":"Generate an answer through the decomposed RAG pipeline","description":"Runs the same retrieval as /v1/search, then Workers AI generation with [E1]-style citations and a grounding check. Set stream: true to receive server-sent events (sources, token..., then done or error) instead of one JSON body. Empty retrieval returns 404 no_evidence rather than generating from no evidence.","security":[{"customerApiKey":[]}],"parameters":[{"in":"header","name":"X-Request-Id","schema":{"type":"string","minLength":8,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Grounded answer, sources, usage and timings_ms (or a text/event-stream body when stream: true)"},"403":{"description":"Scope, entitlement or content grant denied"},"404":{"description":"no_evidence: no evidence inside the customer's content scope matched the question"},"429":{"description":"rate_limited (burst limiter, Retry-After: 10) or quota_denied (per-minute/day quota, Retry-After: 60 or 86400)","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaDeniedError"}}}},"503":{"description":"RAG pipeline unavailable; reservation refunded"}}}},"/v1/rolling-answers":{"post":{"summary":"Start a monthly rolling-window grounded-answer timeline","description":"Requires answer:generate and answer:rolling. Returns 202 with a pollable customer-scoped status URL.","security":[{"customerApiKey":[]}],"parameters":[{"in":"header","name":"X-Request-Id","schema":{"type":"string","minLength":8,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RollingAnswerRequest"}}}},"responses":{"202":{"description":"Job admitted or the existing idempotent job returned"},"400":{"description":"Invalid window or checkpoint range"},"403":{"description":"Rolling-answer entitlement denied"},"409":{"description":"No permitted dated evidence"},"429":{"description":"Job admission reservation denied"},"503":{"description":"Workflow or physical index unavailable"}}}},"/v1/rolling-answers/{job_id}":{"get":{"summary":"Poll a customer-scoped rolling-answer job","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"job_id","required":true,"schema":{"type":"string","pattern":"^roll_[a-f0-9]{32}$"}}],"responses":{"200":{"description":"Progress, monthly answers/no-evidence states and trend"},"403":{"description":"Rolling-answer entitlement denied"},"404":{"description":"Job absent or owned by another customer"}}}},"/v1/chunks/{evidence_id}":{"get":{"summary":"Read an entitled canonical BMJ source chunk","security":[{"customerApiKey":[]}],"parameters":[{"in":"path","name":"evidence_id","required":true,"schema":{"type":"string","pattern":"^ev_[a-f0-9]{24}$"}}],"responses":{"200":{"description":"Canonical chunk and content-unit settlement"},"403":{"description":"Scope, entitlement or content grant denied"},"404":{"description":"Chunk not found in active generation"},"409":{"description":"Execution profile is not paragraph based"}}}},"/api/admin/content-pulls":{"get":{"summary":"List administrator content-pull sources and dev JATS feeds","description":"In dev, feeds[] contains the configured JNIS JATS XML Workflow, its 05:10 UTC schedule, latest run and latest cron run. The legacy JSON sources[] field is retained for compatibility. Source creation/test/run writes are disabled in dev.","security":[{"adminToken":[]}],"responses":{"200":{"description":"Content-pull sources and, in dev, JATS feed status"},"401":{"description":"Administrator authentication required"},"503":{"description":"Control database or feed status unavailable"}}}},"/api/admin/content-pulls/jnis/status":{"get":{"summary":"Read JNIS JATS feed health and recent runs (dev only)","description":"Returns scheduled_utc, last_success_at, latest_run and recent_runs. A completed manual run can be healthy without proving that the cron fired; inspect feeds[].latest_cron_run in GET /api/admin/content-pulls for that evidence. This endpoint does not approve or resolve held items.","security":[{"adminToken":[]}],"responses":{"200":{"description":"Fresh last success and completed latest run"},"401":{"description":"Administrator authentication required"},"404":{"description":"JNIS JATS feed is dev only"},"503":{"description":"Latest run failed/running or last success is stale (over 36 hours)"}}}},"/api/admin/collections":{"get":{"summary":"List every collection definition","security":[{"adminToken":[]}],"responses":{"200":{"description":"Draft, published and retired collections"},"401":{"description":"Administrator authentication required"}}},"post":{"summary":"Create a static or dynamic collection","security":[{"adminToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionWrite"}}}},"responses":{"201":{"description":"Collection created"},"400":{"description":"Invalid definition or duplicate slug"}}}},"/api/admin/collections/{collection_id}":{"put":{"summary":"Replace a collection definition and increment its revision","security":[{"adminToken":[]}],"parameters":[{"in":"path","name":"collection_id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionWrite"}}}},"responses":{"200":{"description":"Collection definition updated"},"404":{"description":"Collection not found"}}}},"/api/admin/collections/{collection_id}/documents":{"put":{"summary":"Replace ordered membership of a static collection","security":[{"adminToken":[]}],"parameters":[{"in":"path","name":"collection_id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaticCollectionMembership"}}}},"responses":{"200":{"description":"Membership replaced"},"400":{"description":"Invalid membership or collection is dynamic"},"404":{"description":"Collection not found"}}}},"/api/admin/trust/markers":{"get":{"summary":"Read all trust definitions and the effective-profile schema","security":[{"adminToken":[]}],"responses":{"200":{"description":"Active and retired marker definitions"}}}},"/api/admin/trust/journals/{journal_id}":{"put":{"summary":"Create a new versioned journal trust profile","security":[{"adminToken":[]}],"parameters":[{"in":"path","name":"journal_id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustProfileWrite"}}}},"responses":{"201":{"description":"Draft saved or new profile published"},"400":{"description":"Invalid marker or typed value"},"404":{"description":"Journal not found"}}}},"/api/admin/trust/articles/{document_id}":{"put":{"summary":"Replace all published article-level trust overrides","security":[{"adminToken":[]}],"parameters":[{"in":"path","name":"document_id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustProfileWrite"}}}},"responses":{"200":{"description":"Article overrides replaced"},"400":{"description":"Invalid marker or typed value"},"404":{"description":"Article not found"}}},"post":{"summary":"Merge supplied published article-level trust overrides","security":[{"adminToken":[]}],"parameters":[{"in":"path","name":"document_id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustProfileWrite"}}}},"responses":{"200":{"description":"Supplied article markers merged"},"400":{"description":"Invalid marker or typed value"},"404":{"description":"Article not found"}}}},"/api/admin/ingest":{"post":{"summary":"Submit one full-text document for ingestion","description":"Administrator-only multipart API. Topic assignments are provenance-labelled catalogue hints; source bytes remain immutable.","security":[{"adminToken":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["document","release_id","generation_id"],"properties":{"document":{"type":"string","format":"binary"},"release_id":{"type":"string"},"generation_id":{"type":"string"},"duplicate_policy":{"type":"string","enum":["skip","overwrite"],"default":"skip"},"journal_id":{"type":"string"},"licence":{"type":"string"},"published_at":{"type":"string","format":"date-time"},"topics_json":{"type":"string","description":"JSON array of label, scheme, provenance, model_version and confidence."}}}}}},"responses":{"200":{"description":"Duplicate skipped"},"202":{"description":"Workflow accepted with status URL"},"401":{"description":"Administrator authentication required"},"409":{"description":"A stale accepted job has no recorded Workflow; reconcile existing_job_id before retrying"}}}},"/api/admin/jobs/{job_id}":{"get":{"summary":"Poll an ingestion job","security":[{"adminToken":[]}],"parameters":[{"in":"path","name":"job_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job, index progress, timing and events"},"401":{"description":"Administrator authentication required"},"404":{"description":"Job not found"}}}}},"components":{"securitySchemes":{"customerApiKey":{"type":"http","scheme":"bearer","bearerFormat":"bmjp_<prefix>_<secret>","description":"Customer API key. Do not use ADMIN_TOKEN for customer endpoints."},"adminToken":{"type":"http","scheme":"bearer","bearerFormat":"ADMIN_TOKEN","description":"Administrator control-plane secret. Never use a customer key here."}},"parameters":{"JournalId":{"in":"query","name":"journal_id","schema":{"type":"string"},"description":"One journal ID or comma-separated IDs, intersected with contract scope."},"PublishedFrom":{"in":"query","name":"published_from","schema":{"type":"string","format":"date"}},"PublishedTo":{"in":"query","name":"published_to","schema":{"type":"string","format":"date"}},"Limit":{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},"Cursor":{"in":"query","name":"cursor","schema":{"type":"string","pattern":"^[0-9]+$"}}},"schemas":{"ArticleHit":{"type":"object","required":["id","doi","title","updated","published_at","version_id","identifiers"],"properties":{"id":{"type":"string","minLength":1,"maxLength":200},"doi":{"type":["string","null"]},"title":{"type":"string"},"updated":{"type":"string","format":"date-time"},"published_at":{"type":["string","null"]},"version_id":{"type":"string","minLength":1,"maxLength":200},"identifiers":{"type":"object","required":["content_id","doi","publisher_id","publisher_url"],"properties":{"content_id":{"type":"string","minLength":1,"maxLength":200},"doi":{"type":["string","null"]},"publisher_id":{"type":["string","null"]},"publisher_url":{"type":["string","null"],"format":"uri"}}},"score":{"type":"number","description":"Present for query results; omitted for browse results."},"abstract":{"type":"string"},"authors":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"journal":{"type":["string","null"]},"licence":{"type":"object"},"source":{"type":"object"},"version":{"type":"object"},"url":{"type":"string","format":"uri"},"contributors":{"type":"array","items":{"type":"object","properties":{"display":{"type":"string"},"given":{"type":["string","null"]},"family":{"type":["string","null"]},"initials":{"type":["string","null"]},"orcid":{"type":["string","null"]},"collective":{"type":"boolean"}}}},"publication_dates":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["online","print","other"]},"date":{"type":"string","description":"Publisher date with original precision: YYYY, YYYY-MM or YYYY-MM-DD."},"year":{"type":"integer"},"precision":{"type":"string","enum":["year","month","day"]},"source_type":{"type":["string","null"]}}}}}},"ArticleFrontmatter":{"type":"object","properties":{"abstract":{"type":"string"},"authors":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"journal":{"type":["string","null"]},"licence":{"type":"object"},"source":{"type":"object"},"version":{"type":"object"},"contributors":{"type":"array","items":{"type":"object","properties":{"display":{"type":"string"},"given":{"type":["string","null"]},"family":{"type":["string","null"]},"initials":{"type":["string","null"]},"orcid":{"type":["string","null"]},"collective":{"type":"boolean"}}}},"publication_dates":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["online","print","other"]},"date":{"type":"string","description":"Publisher date with original precision: YYYY, YYYY-MM or YYYY-MM-DD."},"year":{"type":"integer"},"precision":{"type":"string","enum":["year","month","day"]},"source_type":{"type":["string","null"]}}}}}},"ArticleSearchResponse":{"type":"object","required":["hits","total","matched_total","next_cursor","snapshot"],"properties":{"hits":{"type":"array","items":{"type":"object","required":["id","doi","title","updated","published_at","version_id","identifiers"],"properties":{"id":{"type":"string","minLength":1,"maxLength":200},"doi":{"type":["string","null"]},"title":{"type":"string"},"updated":{"type":"string","format":"date-time"},"published_at":{"type":["string","null"]},"version_id":{"type":"string","minLength":1,"maxLength":200},"identifiers":{"type":"object","required":["content_id","doi","publisher_id","publisher_url"],"properties":{"content_id":{"type":"string","minLength":1,"maxLength":200},"doi":{"type":["string","null"]},"publisher_id":{"type":["string","null"]},"publisher_url":{"type":["string","null"],"format":"uri"}}},"score":{"type":"number","description":"Present for query results; omitted for browse results."},"abstract":{"type":"string"},"authors":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"journal":{"type":["string","null"]},"licence":{"type":"object"},"source":{"type":"object"},"version":{"type":"object"},"url":{"type":"string","format":"uri"},"contributors":{"type":"array","items":{"type":"object","properties":{"display":{"type":"string"},"given":{"type":["string","null"]},"family":{"type":["string","null"]},"initials":{"type":["string","null"]},"orcid":{"type":["string","null"]},"collective":{"type":"boolean"}}}},"publication_dates":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["online","print","other"]},"date":{"type":"string","description":"Publisher date with original precision: YYYY, YYYY-MM or YYYY-MM-DD."},"year":{"type":"integer"},"precision":{"type":"string","enum":["year","month","day"]},"source_type":{"type":["string","null"]}}}}}}},"total":{"type":"integer","description":"Complete logical result-set count."},"matched_total":{"type":"integer","description":"Query matches across the result set."},"returned":{"type":"integer","description":"Rows returned on this page."},"next_cursor":{"type":["string","null"],"maxLength":2048},"snapshot":{"type":"string"},"query":{"type":"object","description":"Original query, effective clauses, strategy and optional interpretation. clarification_required returns empty hits until the caller chooses a mode.","properties":{"strategy":{"type":"string","enum":["browse","identifier","any_term_or_phrase","bibliographic","clarification_required"]},"interpretation":{"type":"object","properties":{"status":{"type":"string","enum":["applied","needs_clarification"]},"reason":{"type":"string"},"author":{"type":["string","null"]},"year":{"type":["integer","null"]},"date_basis":{"type":"string"},"topic_query":{"type":["string","null"]}}}}}}},"FrontmatterPage":{"type":"object","required":["items","total","next_cursor","snapshot"],"properties":{"items":{"type":"array","items":{"type":"object","properties":{"abstract":{"type":"string"},"authors":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}},"journal":{"type":["string","null"]},"licence":{"type":"object"},"source":{"type":"object"},"version":{"type":"object"},"contributors":{"type":"array","items":{"type":"object","properties":{"display":{"type":"string"},"given":{"type":["string","null"]},"family":{"type":["string","null"]},"initials":{"type":["string","null"]},"orcid":{"type":["string","null"]},"collective":{"type":"boolean"}}}},"publication_dates":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["online","print","other"]},"date":{"type":"string","description":"Publisher date with original precision: YYYY, YYYY-MM or YYYY-MM-DD."},"year":{"type":"integer"},"precision":{"type":"string","enum":["year","month","day"]},"source_type":{"type":["string","null"]}}}}}}},"total":{"type":"integer"},"next_cursor":{"type":["string","null"],"maxLength":2048},"snapshot":{"type":"string"}}},"ArticleContentResponse":{"type":"object","description":"Complete article content or requested representation, with metadata and references."},"ArticleSearchRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string","minLength":2,"maxLength":500}}},"DynamicCollectionFilter":{"type":"object","additionalProperties":false,"properties":{"journal_ids":{"type":"array","maxItems":30,"uniqueItems":true,"items":{"type":"string"}},"article_types":{"type":"array","maxItems":30,"uniqueItems":true,"items":{"type":"string"}},"topic_ids":{"type":"array","maxItems":30,"uniqueItems":true,"items":{"type":"string"}},"trust_marker_codes":{"type":"array","maxItems":30,"uniqueItems":true,"items":{"type":"string","pattern":"^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+$"}},"published_from":{"type":["string","null"],"format":"date"},"published_to":{"type":["string","null"],"format":"date"}},"description":"At least one field must narrow a dynamic collection. Fields combine with AND; values in one array combine with OR."},"CollectionWrite":{"type":"object","additionalProperties":false,"required":["slug","name","collection_type"],"properties":{"slug":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string","minLength":2,"maxLength":160},"description":{"type":"string","maxLength":2000},"collection_type":{"enum":["static","dynamic"]},"status":{"enum":["draft","published","retired"],"default":"draft"},"dynamic_filter":{"$ref":"#/components/schemas/DynamicCollectionFilter"}}},"StaticCollectionMembership":{"type":"object","additionalProperties":false,"required":["documents"],"properties":{"documents":{"type":"array","maxItems":2000,"items":{"oneOf":[{"type":"string"},{"type":"object","additionalProperties":false,"required":["document_id"],"properties":{"document_id":{"type":"string"},"position":{"type":"integer","minimum":0},"note":{"type":["string","null"],"maxLength":500}}}]}}}},"TrustEvidenceWrite":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string"},"label":{"type":["string","null"],"maxLength":300},"url":{"type":["string","null"],"format":"uri"},"identifier":{"type":["string","null"],"maxLength":300}},"anyOf":[{"properties":{"url":{"type":"string"}},"required":["url"]},{"properties":{"identifier":{"type":"string"}},"required":["identifier"]}]},"TrustValueWrite":{"type":"object","additionalProperties":false,"required":["code","value"],"properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)+$"},"value":{},"evidence":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/TrustEvidenceWrite"}},"provenance":{"type":"object"},"note":{"type":["string","null"],"maxLength":2000}}},"TrustProfileWrite":{"type":"object","additionalProperties":false,"required":["values"],"properties":{"schema_version":{"const":"1.0","default":"1.0"},"status":{"enum":["draft","published"],"default":"published","description":"Article writes accept published only; journal profiles may be drafts."},"reviewed_by":{"type":["string","null"],"maxLength":320},"source_system":{"type":"string","maxLength":120},"values":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/TrustValueWrite"}}}},"ArticleTrustProfile":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://content.bmj.com/schemas/article-trust-profile/v1","title":"BMJ Article Trust Profile","description":"Effective, evidence-bearing BMJ quality markers derived from a versioned journal profile plus article-level overrides.","type":"object","additionalProperties":false,"required":["$schema","schema_version","article","profile","assertions","coverage"],"properties":{"$schema":{"const":"https://content.bmj.com/schemas/article-trust-profile/v1"},"schema_version":{"const":"1.0"},"article":{"type":"object","additionalProperties":false,"required":["document_id","source_uid","title","doi","journal_id","journal_title","published_at"],"properties":{"document_id":{"type":"string"},"source_uid":{"type":"string"},"title":{"type":"string"},"doi":{"type":["string","null"]},"journal_id":{"type":["string","null"]},"journal_title":{"type":["string","null"]},"published_at":{"type":["string","null"]}}},"profile":{"type":"object","additionalProperties":false,"required":["generated_at","journal_profile_id","journal_profile_revision","journal_profile_published_at","article_override_count"],"properties":{"generated_at":{"type":"string","format":"date-time"},"journal_profile_id":{"type":["string","null"]},"journal_profile_revision":{"type":["integer","null"],"minimum":1},"journal_profile_published_at":{"type":["string","null"],"format":"date-time"},"article_override_count":{"type":"integer","minimum":0}}},"assertions":{"type":"array","uniqueItems":true,"items":{"type":"object","additionalProperties":false,"required":["code","kind","category","label","description","value_type","value","unit","note","evidence","provenance"],"properties":{"code":{"type":"string"},"kind":{"enum":["marker","process","metric"]},"category":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"value_type":{"enum":["boolean","integer","number","string","date","enum","object"]},"value":{},"unit":{"type":["string","null"]},"note":{"type":["string","null"]},"evidence":{"type":"array","maxItems":10,"items":{"type":"object","additionalProperties":false,"required":["type","label","url","identifier"],"properties":{"type":{"type":"string"},"label":{"type":["string","null"]},"url":{"type":["string","null"],"format":"uri"},"identifier":{"type":["string","null"]}},"anyOf":[{"properties":{"url":{"type":"string"}},"required":["url"]},{"properties":{"identifier":{"type":"string"}},"required":["identifier"]}]}},"provenance":{"type":"object","required":["scope","inherited"],"properties":{"scope":{"enum":["journal","article"]},"inherited":{"type":"boolean"},"record_id":{"type":"string"},"recorded_at":{"type":"string","format":"date-time"},"source_system":{"type":"string"}}}}}},"coverage":{"type":"object","additionalProperties":false,"required":["available_marker_definitions","asserted_markers","journal_defaults","article_overrides"],"properties":{"available_marker_definitions":{"type":"integer","minimum":0},"asserted_markers":{"type":"integer","minimum":0},"journal_defaults":{"type":"integer","minimum":0},"article_overrides":{"type":"integer","minimum":0}}}}},"SearchRequest":{"type":"object","required":["query"],"additionalProperties":false,"properties":{"query":{"type":"string","minLength":2,"maxLength":2000},"max_results":{"type":"integer","minimum":1,"maximum":50,"default":5},"retrieval":{"type":"string","enum":["hybrid","vector","keyword"],"default":"hybrid"},"backend":{"type":"string","enum":["ai-search","gateway"],"description":"Overrides the Worker's default retrieval stack (RETRIEVAL_BACKEND) for this request only."},"rerank":{"type":"boolean","default":true},"match_threshold":{"type":"number","minimum":0,"maximum":1,"default":0.35},"stream":{"type":"boolean","default":false,"description":"/v1/answers only: return the pipeline as server-sent events (sources, token..., done|error) instead of one JSON body. Ignored by /v1/search."},"filters":{"type":"object","additionalProperties":false,"properties":{"journal_ids":{"type":"array","maxItems":20,"items":{"type":"string"}},"published_from":{"type":"string","format":"date"},"published_to":{"type":"string","format":"date"}},"description":"Optional narrower filters. They cannot broaden contract scope."}}},"FindRequest":{"type":"object","required":["query"],"additionalProperties":false,"properties":{"query":{"type":"string","minLength":2,"maxLength":500},"level":{"type":"string","enum":["article","paragraph"],"default":"article","description":"article searches titles+abstracts; paragraph searches full-text paragraphs."},"limit":{"type":"integer","minimum":1,"maximum":50,"default":10},"cursor":{"type":"integer","minimum":0,"maximum":1000,"default":0},"filters":{"type":"object","additionalProperties":false,"properties":{"journal_ids":{"type":"array","maxItems":20,"items":{"type":"string"}},"published_from":{"type":"string","format":"date"},"published_to":{"type":"string","format":"date"}},"description":"Optional narrower filters. They cannot broaden contract scope."}}},"InvalidRequestError":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"code":{"type":"string","enum":["invalid_request"]},"error":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}}}}}},"QuotaDeniedError":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"code":{"type":"string","enum":["rate_limited","quota_denied","d1_budget_exhausted","d1_budget_guard_unavailable"]},"error":{"type":"string"},"request_id":{"type":"string"},"decision":{"type":"object"}},"description":"429 rate_limited carries Retry-After: 10 (per-key burst limiter); 429 quota_denied carries Retry-After: 60 (minute_limit) or 86400 (day_limit); 503 d1_budget_exhausted/d1_budget_guard_unavailable carry Retry-After: 3600 or 60."},"RollingAnswerRequest":{"type":"object","required":["query","window_months"],"additionalProperties":false,"properties":{"query":{"type":"string","minLength":2,"maxLength":2000},"window_months":{"type":"integer","minimum":1,"maximum":60,"description":"Rolling evidence lookback. The active entitlement may narrow this range."},"checkpoint_from":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"checkpoint_to":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"max_results":{"type":"integer","minimum":1,"maximum":20,"default":6},"retrieval":{"type":"string","enum":["hybrid","vector","keyword"],"default":"hybrid"},"rerank":{"type":"boolean","default":true},"match_threshold":{"type":"number","minimum":0,"maximum":1,"default":0.35},"filters":{"type":"object","additionalProperties":false,"properties":{"journal_ids":{"type":"array","maxItems":20,"items":{"type":"string"}}},"description":"Optional journal narrowing. Date bounds are derived from each checkpoint."}}}}}}