{"openapi":"3.1.0","info":{"title":"Content Gateway Prototype API (beta)","version":"0.5.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://content-gateway.bmj-cloudflare-hackathon.com"}],"paths":{"/health":{"get":{"summary":"Service health","responses":{"200":{"description":"Healthy"}}}},"/v1/customer/context":{"get":{"summary":"Resolve the authenticated customer and active policy","security":[{"customerApiKey":[]}],"responses":{"200":{"description":"Authenticated context"},"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 tools/resources for articles, collections and trust markers. Requires the same metadata rights as customer catalogue reads.","security":[{"customerApiKey":[]}],"parameters":[{"in":"header","name":"MCP-Protocol-Version","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"MCP JSON or event-stream response"},"401":{"description":"Invalid or revoked customer key"},"403":{"description":"Metadata entitlement denied"}}}},"/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/search":{"post":{"summary":"Search the active governed evidence generation","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"},"400":{"description":"Invalid request"},"401":{"description":"Invalid or revoked key"},"403":{"description":"Customer or scope denied"},"429":{"description":"Customer quota denied"},"503":{"description":"Configured physical generation unavailable or not ready"}}}},"/v1/answers":{"post":{"summary":"Generate an answer through the decomposed RAG pipeline","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 and usage"},"403":{"description":"Scope, entitlement or content grant denied"},"429":{"description":"Answer reservation denied"},"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/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"}}}},"/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":{"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"},"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"}},"published_from":{"type":"string","format":"date"},"published_to":{"type":"string","format":"date"}},"description":"Optional narrower filters. They cannot broaden contract scope."}}},"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."}}}}}}