---
name: bmj-evidence-search
description: Use the Content Gateway Prototype REST or MCP interfaces to discover scoped static/dynamic collections, inspect per-article BMJ quality and trust markers, retrieve evidence, request grounded or longitudinal rolling answers, and read entitled canonical chunks while preserving source, execution and usage provenance. Use when an agent must browse governed BMJ open-corpus content, resolve editorial collections, explain journal-inherited versus article-specific trust assertions, run grounded retrieval, handle rolling-answer jobs, or report which release, generation, segmenter and models produced a result.
---

# Content Gateway Prototype Search (beta)

Use the stable customer API. Do not call Cloudflare AI Search, D1, R2 or the
Durable Object directly.

## Configuration

Use:

```text
BMJ_EVIDENCE_BASE_URL=https://content-gateway.bmj-cloudflare-hackathon.com
BMJ_EVIDENCE_API_KEY=<customer key supplied securely>
BMJ_EVIDENCE_MCP_URL=${BMJ_EVIDENCE_BASE_URL}/mcp
```

Never request, use or expose `ADMIN_TOKEN` for evidence search. That credential
belongs to the human control plane.

Treat `BMJ_EVIDENCE_API_KEY` as a secret:

- send it only as `Authorization: Bearer <key>`;
- never put it in a URL, prompt, report, log or source file;
- never display or repeat it;
- stop on HTTP 401 rather than guessing or rotating credentials.

## Discover capabilities first

1. Read `${BMJ_EVIDENCE_BASE_URL}/openapi.json` when the current contract is
   needed.
2. Call `GET /v1/customer/context` to discover the customer’s contract,
   entitlements, content grants, execution profile, limits and enforced corpus
   scope. The plan-derived scope includes permitted `content_types` and an
   all-time, fixed or rolling date policy; journal IDs may narrow it further.
3. Call `GET /v1/customer/corpus-summary` before exploratory work. Use its
   journal facets, publication range, document/evidence counts and topic
   coverage to understand only the corpus this customer may see.
4. Call `GET /v1/collections?limit=25` when a curated or live editorial set
   may answer the request more precisely. Follow `next_cursor`.
5. When general enumeration is useful, call `GET /v1/documents?limit=25`. Follow
   `next_cursor`; use `journal_id`, `published_from` and `published_to` only to
   narrow the permitted corpus. Use `GET /v1/documents/:document_id` for one
   record.
6. Generate a stable, unique request ID for the attempt.
7. Call `POST /v1/search`:

```bash
curl -X POST "${BMJ_EVIDENCE_BASE_URL}/v1/search" \
  -H "Authorization: Bearer ${BMJ_EVIDENCE_API_KEY}" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: agent-request-unique-id" \
  --data '{
    "query": "What evidence discusses treatment adherence?",
    "max_results": 5,
    "retrieval": "hybrid",
    "rerank": true
  }'
```

8. Use only returned evidence. Preserve each `evidence_id` and its score when
   citing or synthesising.
9. Report the returned `execution_context`, especially `content_release`,
   `index_generation`, `segmenter`, `embedding_provider` and
   `embedding_model`.
10. State that evidence was insufficient when results are empty or do not
   support the requested conclusion.

Discovery and search are customer-scoped. Do not infer that a journal, content
type or date absent from `corpus-summary` is absent from BMJ; it may simply be
outside this contract. Search metadata is hydrated from the canonical
catalogue and may include DOI, journal title, publication date, locator and
provenance-labelled topics.

## MCP and collection workflow

Use the authenticated Streamable HTTP endpoint at
`${BMJ_EVIDENCE_BASE_URL}/mcp` when the host application supports MCP. Send the
same customer bearer key. Discover tools before calling them; the server
currently exposes:

- `list_articles` and `get_article`;
- `list_collections`, `get_collection` and `list_collection_articles`;
- `list_trust_markers` and `get_article_trust`.

It also exposes `bmj://collections` and
`bmj://schemas/article-trust-profile/v1` resources. Use the collection
catalogue resource or `list_collections` before selecting a collection. Tool
descriptions may include the currently published collection names and slugs.

For REST clients:

- use `GET /v1/collections/:id_or_slug` for one definition;
- use `GET /v1/collections/:id_or_slug/documents` for its current articles;
- follow `next_cursor` until it is null.

A static collection retains an editorially ordered document list. A dynamic
collection retains a validated filter and is re-evaluated when read. Both are
intersected with the authenticated customer’s release, content-type, journal
and publication-date rights. A collection count of zero can therefore mean
that the collection is outside this contract; do not describe it as globally
empty.

## Article trust and quality workflow

Use `get_article_trust` over MCP or
`GET /v1/articles/:document_id/trust` with the customer key. A document record
from `GET /v1/documents/:document_id` also embeds its effective trust profile.

The following public transparency routes require no customer credential:

```text
GET /v1/trust/schema
GET /v1/trust/markers
GET /v1/trust/articles/:document_id
```

Interpret BMJ Article Trust Profile v1 carefully:

- `assertions` contains only supplied values; absence never means `false`;
- `provenance.scope: journal` with `inherited: true` is a versioned journal
  default;
- `provenance.scope: article` is an article-specific value that overrides the
  matching journal marker;
- `evidence` identifies the supporting policy, workflow record or published
  object;
- `kind` distinguishes editorial processes, descriptive markers and metrics;
- `coverage` describes populated schema coverage, not a quality score.

Report marker values and provenance without converting them into an
unsupported ranking. Do not infer that an editorial process guarantees a
research outcome. For integrity markers such as `integrity.status`, report the
current publisher-maintained state exactly.

## Grounded answer workflow

Use `POST /v1/answers` only when the context includes
`answer:generate`. Send the same request shape as search. The response contains
the answer, sources, grounding status, component-level execution context and
settled usage. Preserve source evidence IDs and report any grounding warnings.
An answer entitlement is an independent add-on to the base search service; do
not infer source-chunk access from it.

## Rolling answer workflow

Use `POST /v1/rolling-answers` only when the context includes both
`answer:generate` and `answer:rolling`. This is asynchronous: the initial
response is HTTP 202 with a `job_id` and relative `status_url`.

```bash
curl -X POST "${BMJ_EVIDENCE_BASE_URL}/v1/rolling-answers" \
  -H "Authorization: Bearer ${BMJ_EVIDENCE_API_KEY}" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: agent-rolling-unique-id" \
  --data '{
    "query": "What does the evidence report about diabetes prevention, treatment and outcomes?",
    "window_months": 1,
    "checkpoint_from": "2025-08",
    "checkpoint_to": "2026-07",
    "max_results": 5,
    "retrieval": "hybrid",
    "rerank": true
  }'
```

Poll `GET ${BMJ_EVIDENCE_BASE_URL}<status_url>` with the same customer key at
a measured interval (for example 5–10 seconds). Do not resubmit the POST while
the job is queued or running. Reusing the same request ID returns the same
customer-owned job.

`window_months: 1` means each checkpoint uses only evidence published in that
calendar month. It does not mean “run once a month.” The example above is
deliberately broad because the current POC catalogue contains
diabetes-tagged articles in every month from July 2025 through July 2026.
Always check `GET /v1/customer/corpus-summary` first because another customer
may have a narrower journal, content-type or publication-date scope.

The completed response includes:

- each monthly checkpoint and exact evidence period;
- `completed`, `no_evidence`, `failed` or quota-denied status per window;
- grounded monthly answers and sources where evidence exists;
- cache-use and token telemetry;
- deterministic coverage, answer-similarity and evidence-overlap signals; and
- a `stable`, `converging`, `diverging`, `evolving`, `mixed` or
  `insufficient_evidence` trend plus a narrative when available.

Treat `no_evidence` as a valid temporal observation. Do not fill the gap with
another corpus or web search. The trend classification is a transparent POC
heuristic, not a statistical or clinical conclusion. Report warnings and
failed windows rather than silently omitting them.

Rolling answers are packaged through the existing composable offer model.
They do not require a new API-key type or a separate product class. A named
plan enables base search, `answer:generate` and `answer:rolling`, then a
versioned customer contract snapshots those rights. If either entitlement is
absent, stop with the 403 and ask a human administrator to assign an
appropriate plan; never switch customer, key or corpus to route around it.

## Canonical source chunks

Use `GET /v1/chunks/:evidence_id` only when the context includes
`source:chunk:read`. It requires a paragraph execution
profile and returns the immutable BMJ chunk, locator, citation metadata,
content hash and settled `licensed_content_1k_tokens`.

Chunk delivery is independently composable. A customer may have search plus
chunks without answers, or search plus answers without chunks. Fetch a chunk
only after search or answer returns an entitled `evidence_id`; do not expect
the full source text to be embedded in the search/answer response.

Do not infer a right from an API-key scope. The key scope is only a security
ceiling; the active contract entitlement and content grant are authoritative.
Do not expose retrieved full chunks to another party or corpus.

## Request controls

- `query`: required, 2–2,000 characters.
- `max_results`: optional, 1–50; the customer policy may lower it.
- `retrieval`: `hybrid` by default; `vector` and `keyword` are supported.
- `rerank`: true by default.
- `match_threshold`: optional number from 0 to 1; default 0.35.
- `filters`: optional object containing `journal_ids`, `published_from` and
  `published_to`. Filters can narrow but never broaden the contract scope.
- Rolling requests use `window_months` plus optional `checkpoint_from` and
  `checkpoint_to` in `YYYY-MM`. They may narrow `journal_ids`, but must not
  supply date filters: the service derives dates for each checkpoint.
- `X-Request-Id`: 8–128 safe characters. Reuse it only when retrying the exact
  same logical request.

## Error handling

- `400`: fix the payload; do not retry it unchanged.
- `202`: a rolling job was accepted; poll the returned status URL.
- `401`: stop and request a valid customer key.
- `403`: stop; key scope, customer entitlement or content grant is missing.
- `404`: the article, collection or job is absent or outside customer scope.
- `409`: the active execution profile is not capable of canonical chunk reads.
- `429`: respect `Retry-After`. Never rotate keys to evade customer quota.
- `503`: the configured physical generation is unavailable or still becoming
  ready, or synthesis returned `answer_empty`, `answer_truncated` or
  `generator_not_supported`. Retry once with the same request ID, then report
  the exact code. Ask an administrator to compare the execution profile with
  the indexed generation or inspect synthesis telemetry as appropriate.
- Other `5xx`: retry once with the same request ID, then report the failure.

Do not silently fall back to another corpus, generation or public web search.

## Authentication boundaries

Customer API keys authenticate scoped `/v1/*` calls and `/mcp`, then resolve to
a stable customer. Authentication is read-only. Public `/v1/trust/*`
transparency reads are the exception and require no key. The customer owns
reservation and settlement state, so key rotation does not reset allowance.
Reuse the same request ID for an exact retry; failures may refund the
reservation.

Administrators use Cloudflare Access or `ADMIN_TOKEN` for customer, key and
ingestion management. Agents performing evidence search must not use either
administrator method.

The administrator API exposes `POST /api/admin/ingest`,
`POST /api/admin/ingest/check` and `GET /api/admin/jobs/:job_id`. Those routes
are not customer discovery routes. Never send a customer API key to them and
never request an administrator token on behalf of an evidence-search agent.

## Human administrator handoff

Do not open administrator pages with a customer key or ask a user to share an
administrator token. When human intervention is required, direct the
administrator to the narrowest relevant view:

- `/admin` for the control-centre overview;
- `/admin/ingestion` to inspect which generations actually contain completed
  documents, or when a document must be ingested, skipped or re-ingested;
- `/admin/content` to inspect document versions, evidence units, index state
  and source lineage;
- `/admin/collections` to create static or dynamic collections and replace
  ordered static membership;
- `/admin/trust` to publish journal defaults or apply article-specific trust
  assertions;
- `/admin/plans` to inspect named composable offers and their answer/chunk
  add-ons;
- `/admin/customers` for plans, entitlements, execution profiles, generation
  assignments and API-key lifecycle;
- `/admin/usage` to review settled activity by customer, key, document or
  evidence unit;
- `/admin/rolling-answers` to inspect asynchronous monthly checkpoints and
  final trend synthesis;
- `/admin/docs` for service boundaries, entity relationships and execution
  diagrams.

Customer and API-key usage views report settled meter units. Document and
evidence-unit views report distinct settled request exposure so a grounded
answer with multiple meter settlements is not counted more than once.

Authenticated search and grounded-answer text is retained for 30 days by the
separate BMJ request-audit service and is visible to administrators on the
customer and API-key pages. The audit event contains sanitized request options,
outcome, latency and evidence IDs; it never contains the bearer token or
arbitrary HTTP headers. Do not put secrets, credentials or personal data in a
search query.
