---
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://dev.bmj-content-hub.com
# Always the workers.dev hostname: the branded hostname challenges non-browser clients.
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 agreement
   collections. Its `api_key` object reports the selected `operation_ids`,
   `transports` and `mcp_tools`. The response also includes an `access` array — one row per mode, each with
   `{ capability, scope, grant, plan, key, grant_ok, effective }` — computed
   from the same table the server enforces. Check `effective` on the row for
   a mode before attempting it, instead of discovering a 403 mid-task.
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.

## Choose the right mode

Every mode is gated by the agreement's package operation and connection
method, the key's narrower operation/method selection, the release's content
grant and the agreement collections. The key's derived scope is checked again
as defence in depth. `GET /v1/customer/context` reports the result. Pick the
mode by need, not by habit:

- **Exact words, names, drug/trial names or a quoted phrase** → `POST /v1/find`
  (or the `find_articles`/`find_passages` MCP tools). Lexical BM25 over titles
  and abstracts (`level: "article"`) or full-text paragraphs
  (`level: "paragraph"`). No embedding step; fast (100–300 ms). Requires
  `search:lexical`.
- **A question or a meaning-based query where you want ranked evidence to
  inspect yourself** → `POST /v1/search`. Requires `retrieve:preview`.
- **A question where you want a synthesised, cited answer** → `POST /v1/answers`.
  Requires `answer:generate`; an independent add-on to search, not implied by
  it. Set `"stream": true` for server-sent events instead of waiting for the
  complete body.
- **A trend across a publication timeline** → `POST /v1/rolling-answers`.
  Requires both `answer:generate` and `answer:rolling`.
- **The exact licensed source text behind an `evidence_id`** →
  `GET /v1/chunks/:evidence_id`. Requires `source:chunk:read` and a `chunk`
  content grant; independently composable with search or answers.

Do not fall back from one mode to another to route around a denial — if
`access[]` reports a mode as not `effective`, stop and report which layer
(package, key or content grant) is missing rather than trying an adjacent mode
that happens to be allowed.

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` only when both the agreement and key report MCP
as an effective connection method. 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`;
- `find_articles` and `find_passages` for exact words, names and quoted
  phrases (BM25 over titles/abstracts or full-text paragraphs), complementing
  the meaning-based search endpoints.

It also exposes `bmj://collections` when Browse is selected, and always exposes
`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 frozen collection retains exact article membership. Growing and rolling
collections retain validated journal/date rules and are re-evaluated when
read. All results are intersected with the authenticated customer's agreement
collections and content release. 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 different collection boundary.

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 a low-level API-key scope. Effective access requires
the agreement and key to permit the operation and connection method, plus a
sufficient content grant and collection boundary.
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; the package/key operation, connection method or content grant is missing.
- `404`: the article, collection or job is absent or outside customer scope,
  or (`answers`, code `no_evidence`) no evidence in the customer's content
  scope matched the question — a valid empty result, not a failure.
- `409`: the active execution profile is not capable of canonical chunk reads.
- `429`: `rate_limited` is the per-key burst limiter (`Retry-After: 10`);
  `quota_denied` is the customer's per-minute/day quota (`Retry-After: 60` or
  `86400`). Respect `Retry-After` either way. Never rotate keys to evade it.
- `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.
