RadVector API clinical inference for radiology

Developers

Built to be integrated, not demoed.

Standard formats in and out, codes on every finding, a sandbox, version pinning and honest failure codes. This page answers the questions an engineering lead asks before the first call.

Does the RadVector API return FHIR?

Yes. Every POST /v1/inference/evaluate answer is available as a FHIR R4 Bundle: send Accept: application/fhir+json or add ?format=fhir. The Bundle holds one preliminary DiagnosticReport, one Observation per finding and one per verdict. A verdict Observation carries the category, the recommendation, derivedFrom pointing at its finding, and the criteria pack, version and validator as an extension and a meta.tag (usable-for-output or advisory). A deferral is an Observation with dataAbsentReason and the reason text, so an undecided finding stays visible instead of disappearing. The default JSON answer is unchanged; FHIR is a second rendering of the same decision.

Can it normalize results from other imaging AI systems?

Yes. POST /v1/results/normalize accepts another AI system's output as FHIR R4 Observations (a Bundle, one Observation or a list) or as a DICOM SR TID 1500 measurement report in the DICOM JSON model, with no DIMSE networking and no pixels. Each measurement group becomes a finding with its source algorithm and version preserved, the findings are graded by the same engine as every other call, and every verdict names both the AI source and the RadVector criteria pack. Patient fields are never read. RadVector grades findings someone else reported, as decision support; it detects nothing.

Are findings coded?

Yes, where a verified code exists. Findings and verdicts carry a codes list drawn only from codes copied from the published Open Imaging Finding Models (RadElement, the ACR and RSNA common data elements), each entry naming the model it came from, and a build-time check re-verifies them against the published set. A finding without a verified code is sent as text. In FHIR the same codes appear as Observation.code.coding. A wrong code is worse than none, so the list is short and grows only by verification.

Is there a pre-read service for prior studies?

Yes. POST /v1/context/prepare takes the current order or exam text, up to a handful of de-identified prior report texts with their dates, and an optional technologist worksheet. It returns the comparable priors ranked same-type first with the interval to this study, the prior measurements bound to their lesions, the reporting standards that apply with each one's required reporting elements, the recommendations earlier reports made that are still open, and missing-information warnings such as a dictation that refers to a worksheet none was supplied for. It is assembly over the same deterministic readers the reporting engine uses; no language model is called.

Is there a sandbox?

Yes, where the deployment has it switched on. A sandbox key is minted from the portal dashboard beside a live key. It returns the same engine answers as a live key, is never billed, never counts against the free monthly allowance, has its own lower rate limit, and cannot call /v1/impression because that route spends hosted model tokens. Every sandbox answer carries X-RadVector-Sandbox: true. A published file of synthetic request bodies, synthetic-reports.json, covers each route with invented values and the answer to expect, so a team can test without any real data.

Can I pin the criteria-pack version?

Yes. Send pack_versions, for example {"fleischner": "fleischner-2017-r6"}, on an evaluate call. When the live pack's version differs, the API answers 409 pack_version_mismatch naming the live version and grades nothing, so a stale integration fails loudly instead of silently changing its recommendations. This is the honest form of pinning: replaying an old pack would also need the evaluator code of that date, and that is not promised.

How does the API say it could not decide?

Every verdict carries a deferral_code from a closed set: inapplicable, absent, excluded, undocumented, contradiction, several_lesions, error, unresolved. An undocumented feature is named, with the range of categories the finding could still reach. A draft pack answers advisory and is marked not usable for output. Your code branches on the code, never on prose.

Can I send many findings at once, and retry safely?

Yes to both. POST /v1/inference/evaluate/batch takes 1 to 25 evaluate requests and answers them in order, each with its own result or error and each metered as if sent alone. An Idempotency-Key header on evaluate, results normalization and impression stores the first answer durably in the API's own database, so a retry that lands on another instance replays that answer with Idempotent-Replayed: true and is neither re-run nor re-metered. The same key with a different body is refused.

How do I know which build is serving?

An authenticated GET /v1/health returns build, the deploy's build id, beside the live criteria packs and their versions. The anonymous health answer never carries it. Every answer also carries an X-Trace-Id, which appears on each verdict's provenance and each usage event, so a question about one call can be answered from the record.

Start with a sandbox key and the fixtures.

Create an account, mint a key, send one fixture.