Public Gateway Soft Launch

MedEvidence Gateway Studio

This sample web app turns the external-only rollout email into something testable. It exercises the public gateway, highlights what the current docs promise, and generates copy-paste sample code for external developers.

Everything here stays on the public gateway contract: one public base URL, one API key header, one session header, and the async request model that returns request_id and article_id.

Public Base URL
https://gw-47-116-7-37.nip.io
Required Headers
X-API-Key and X-MedEvidence-Session-ID
Preferred Flow
POST /ask/async, then GET /request or /poll
Rendering Rule
Use structured_article for final answer rendering

Try the live contract

Run the same flows described in the external rollout email and watch the verification checklist update in real time.

Live response explorer

Use this output to confirm what external clients can actually rely on: auth status, ids, final answer fields, and optional routing headers.

Region: pending
Routing: pending
Request ID: pending
Article ID: pending
Last Gateway Response No request yet
Run one of the checks above to populate this panel.

Rendered answer body (with inline figures)

Walks structured_article and inlines each embedded_components[].asset_id at its owning span, so figures land where they belong in the prose. The gallery below still shows the deduplicated rich_assets registry.

Run a successful /ask or /ask/async request to see the structured article rendered here with figures inlined at their original span positions.

Gateway contract checklist

Each item maps directly back to the public-gateway rollout email and external integration draft.

...

Protected validation returns setup auth state

Validates that callers without a key get 401 missing_api_key from /validate-key, not a fake network success.

...

Authenticated /health/details returns gateway-native summary

Checks for service, regions, and default_recommended_region.

...

/ask/async returns request and article identifiers

Confirms the async contract external apps need for follow-up reads.

...

Session-based routing is accepted by the gateway

Confirms the public contract really does use X-MedEvidence-Session-ID.

...

Answer fields are usable for client rendering

Checks whether structured_article is present, whether span-level embedded_components can appear for rich content such as publication_figure, and whether raw_output remains available as fallback/debug content.

Sample code generator

These snippets now show a complete async usage flow: create a session, call /ask/async, poll to a terminal status, and prefer structured_article for final rendering.

curl

          

External consumer docs

This section is the living technical contract for external consumers. The intent is that future updates happen here, so teams can rely on the sample app instead of waiting for a new rollout email.

Authentication

Protected endpoints require X-API-Key. Calling /validate-key without a key should return 401 missing_api_key. Calling /health returns public liveness.

Session routing

Send X-MedEvidence-Session-ID for one chat thread or conversation. Reuse the same value for MedEvidence and PubMed requests inside that thread.

Async workflow

Use POST /ask/async, then follow with GET /request/<request_id> or GET /poll/<article_id>.

  • /request and /poll are read-only from the client perspective.
  • Backend workers still own upstream polling.
  • POST /ask remains available as a sync compatibility path, but new clients should not start there.

Follow-up context

Send optional parent_article_id on POST /ask/async or POST /ask when the new question should continue an existing article thread.

  • Use the prior successful article_id as parent_article_id.
  • MedEvidence translates that value to provider-native original_article upstream.
  • Omit the field for root turns, or clear it to start a fresh thread.

Answer fields

Use structured_article for final rendering. Span text is normalized so clients should no longer see raw REACTCOMPONENT!:!... marker strings when normalization succeeds. Treat raw_output as preview, debug, audit, or fallback content.

  • Rich spans may include embedded_components.
  • Figure-like content currently appears as publication_figure.
  • Clients that ignore embedded_components still get cleaner text than before, but richer clients can render the extracted component metadata.

Body assembly with figures. Top-level rich_assets is a deduplicated registry keyed by asset_id and does not carry positional order. The position of each figure in the body is carried on the owning span via articlespan_set[].embedded_components[].asset_id. Concatenating only articlespan_set[].text drops figures by design — figure markers are stripped from text during normalization so pure-text clients render cleanly, at the cost of losing figure position. Clients that want figures in the final body must walk the tree and, at each span, render text followed by any embedded_components[] whose display_type == "publication_figure", looking up each asset_id in top-level rich_assets. Spans that contained only a figure carry display_hint: "block" after normalization, signaling a standalone figure block.

Health behavior

The public gateway /health is gateway-native. It reports gateway availability, region reachability, and the default recommended region for new work.

Current caveats

  • The public gateway currently uses the temporary HTTPS bridge hostname gw-47-116-7-37.nip.io, not the final production domain.
  • Final DNS is still pending.
  • /debug/* is not part of the external consumer contract.
  • Backend-region failover still depends on API key consistency across backend regions.
  • Image delivery now uses an initial T0/T1 policy: normal regions usually redirect, while CN readers receive proxied image responses so figures can still render in answers.

Public endpoint reference

  • GET /health (public liveness)
  • GET /validate-key (API key validation)
  • GET /health/details (protected health details)
  • POST /ask/async (preferred)
  • POST /ask (sync compatibility)
  • GET /request/<request_id>
  • GET /poll/<article_id>
  • GET /asset/<asset_id>?exp=...&sig=...
  • POST /ask/pubmed
  • GET /pubmed/<pmid>

Recommended workflow

  • Use backend-to-backend integration for production.
  • Store the API key on your server side.
  • Generate one session id per chat thread.
  • Call POST /ask/async.
  • Add parent_article_id for follow-up turns that should keep prior context.
  • Poll /request or /poll until completion.

Response fields

  • request_id: follow-up request lookup key
  • article_id: follow-up poll lookup key
  • job_id: upstream execution identifier
  • parent_article_id: optional caller-supplied follow-up context pointer
  • structured_article: formal answer field
  • rich_assets: top-level asset registry for browser-renderable figures and images
  • citations: top-level citation registry for normalized references
  • embedded_components: optional span-level rich content extracted from normalized structured_article spans
  • publication_figure: current rich component type for extracted publication figures
  • raw_output: preview/debug/fallback content
  • last_progress_at: minimum freshness field for clients

Error handling

Clients should expect a stable auth error shape with error and error_code.

  • missing_api_key
  • invalid_api_key
  • revoked_api_key
  • expired_api_key
  • disabled_principal
  • forbidden_request_access
  • parent_article_not_found
  • forbidden_parent_article_access
  • parent_article_not_ready
  • asset_origin_fetch_failed with 502 when MedEvidence cannot fetch the upstream image during proxy delivery

FAQ

  • Do I need to choose a backend region? No. External consumers should use the single public gateway.
  • Can I call from browser JavaScript? You can experiment here, but backend-to-backend integration is the recommended production path.
  • Is 401 missing_api_key on /validate-key an outage? No. It means the validation endpoint is protected and you have not sent a key. /health itself is public liveness.
  • How do I send a follow-up question? Reuse the previous successful article_id as parent_article_id in the next create request.
  • What if structured_article contains figures or other rich blocks? Check for span-level embedded_components. A figure currently appears as publication_figure with metadata such as caption, image URL, and citation details.
  • How should clients render images? Resolve image cards through top-level rich_assets and treat those URLs as the MedEvidence-owned browser contract.
  • How do I keep figures in the correct position when I am assembling the answer body myself? Top-level rich_assets is a deduplicated registry and does not carry positional order. Do not assemble the body by concatenating only articlespan_set[].text — figure markers are stripped from text during normalization, so that approach silently drops figures. Walk articlesection_set[] → articleparagraph_set[] → articlespan_set[] and, at each span, emit the text and then any embedded_components[] whose display_type == "publication_figure", using the asset_id to look up the asset in top-level rich_assets. See docs/external_rich_assets_and_follow_up_guide.md ("Where Images Belong In The Answer Body" and "Recommended Assembly Pattern") for a worked example.
  • What should CN readers expect? MedEvidence proxies image fetches for CN readers, so clients should render the returned rich_assets URLs normally instead of assuming caption-only fallback.