Direct Mode
Search FHIR sources in your app, then create a Connect Session for the selected source.
Show prompt text
You are an AI coding agent integrating the Medblocks Platform API into the codebase that is currently open. Medblocks is a healthcare data platform; the Platform API is a server-to-server REST API that creates Patients, opens Connect Sessions (patient-mediated EHR/FHIR authorization), searches the FHIR source catalog, and reports connection status. This is a PHI-adjacent integration — do not guess your way through it.
Work in the five phases below. Do not skip ahead. When anything is ambiguous, STOP AND ASK rather than assume.
========================================
PHASE 0 — AUTHORITATIVE SOURCES
========================================
Fetch and read these before writing anything. If you cannot fetch a URL, ask the user to paste it. The OpenAPI spec is the only source of truth for routes, params, headers, request/response shapes, and error codes — it wins over anything in this prompt or in the docs if they ever disagree.
- OpenAPI spec (authoritative): https://app.medblocks.com/openapi/medblocks.json
- Setup + API keys + Version header: https://medblocks.com/docs/setup
- API overview + auth + fetch helper: https://medblocks.com/docs/api/overview
- Patients: https://medblocks.com/docs/api/patients
- Picker Mode (hosted picker): https://medblocks.com/docs/api/picker-mode
- Direct Mode (you pick the source): https://medblocks.com/docs/api/direct-mode
- After connection (return + status): https://medblocks.com/docs/api/after-connection
- FHIR access (roadmap): https://medblocks.com/docs/api/fhir-access
- Error envelope + code list: https://medblocks.com/docs/reference/errors
- Generated reference + playground: https://medblocks.com/docs/reference/api
From the OpenAPI spec, record before moving on:
- info.version (the date string used in the Version header; pin this for production).
- Every path under paths (the public surface today is /health, /patients, /patients/{id}, /patients/{id}/sessions, /sessions, /sessions/{id}, /connections, /connections/{id} — verify against the spec, not this prompt).
- components.securitySchemes (Bearer API key, format mb_sk_live_...).
- Callbacks defined on POST /sessions (pickerModeReturn, directModeSuccess, directModeError) — these describe what the patient browser receives on the return_url.
- The error envelope shape under any non-2xx response.
If a route or field you'd like to use is not in the spec, do not invent it. Say so and stop.
========================================
PHASE 1 — RECONNAISSANCE (analyze before writing)
========================================
Read the project before adding files. Determine, with evidence (cite the file you saw it in):
1. Language + runtime: look at package.json / bun.lock / pnpm-lock.yaml / yarn.lock / deno.json / requirements.txt / pyproject.toml / go.mod / Gemfile / Cargo.toml / pom.xml / build.gradle / *.csproj. Identify the runtime (Node, Bun, Deno, CPython, Ruby, Go, JVM, .NET, etc.) and exact framework versions.
2. HTTP framework: Next.js (App Router or Pages Router?), Remix, SvelteKit, Astro, Nuxt, Express, Hono, Fastify, Koa, NestJS, Bun.serve, FastAPI, Flask, Django (DRF?), Rails, Gin/Chi/Echo, Spring Boot, ASP.NET Core, Phoenix, etc.
3. Frontend layer (if any): React/Vue/Svelte/Solid? SSR or SPA? Build tool (Vite, Webpack, Bun, Turbopack)? Is there a frontend at all, or is this a backend-only service?
4. Existing API-client conventions: is there a services/, lib/, infra/, api/ folder? Are typed clients already generated from OpenAPI specs (openapi-typescript, openapi-fetch, orval, hey-api, swagger-codegen, openapi-python-client, oapi-codegen)? Is there a shared fetch wrapper or error class you should extend instead of duplicating?
5. Secrets + config: where are env vars loaded (.env, .env.local, dotenv, Bun's built-in loader, Pydantic Settings, Viper, dotenv-rails, Doppler, Vercel/Fly/Railway env)? Where is .env.example?
6. Auth + user model: does the app already have a "current user/patient" with a stable internal ID? That ID will become the Medblocks patient_id.
7. Logging + error handling: what logger (pino, winston, structlog, zerolog, slf4j, Python logging)? Is there a request-ID convention you should propagate?
8. Tests: which runner (bun test, vitest, jest, pytest, go test, rspec, junit)? Are HTTP calls mocked (msw, nock, responses, vcr)? Is there a sandbox or test API key already available?
9. Deployment target: Vercel / Cloudflare Workers / AWS Lambda / Fly / Railway / bare metal / on-prem? This affects timeout caps, edge-vs-node runtime, and where the backend route the browser calls actually runs.
Print a short reconnaissance report (5–8 bullets) before doing anything else. If you cannot identify any of items 1–6 with confidence, STOP and ask.
========================================
PHASE 2 — CLARIFYING QUESTIONS
========================================
Ask the user — by name, in a numbered list — every question below whose answer is not already obvious from Phase 1. Wait for answers. Do not stub things out and "fill in later".
A. Connect mode
- Picker Mode (Medblocks hosts the source picker), Direct Mode (your app picks the source and passes connection_id), or both?
- If Direct: where in the existing UI does the source-search experience belong?
B. Patient identity
- What is the stable, never-changing internal ID this app already has for a patient/user? It must not start with the Medblocks-reserved prefixes pat_, sess_, conn_, or fhirsrc_.
- Create Patients explicitly via POST /patients during intake, or upsert implicitly the first time a Session is created? (Upsert is the shorter path for most products.)
C. Return URL UX
- What absolute URL should Medblocks redirect the patient to after the hosted flow? (Usually a route this product owns, e.g. https://<app>/connected.)
- What product-specific label should the "Done" button show? ("Back to <ProductName>" beats "Continue".)
- On the return page, what should the patient see for success, failure, and cancellation?
D. Server boundary
- Confirm: this app has (or can add) a backend that holds MEDBLOCKS_API_KEY. The browser must never see it. Where in the codebase should the server-only Medblocks client live, given existing conventions?
E. Version pinning
- Pin the Version header to the spec's current info.version (today the spec reports 2026-04-25 — verify the value you just fetched) for production? Leave it off only during local exploration.
F. Idempotency + retries
- Is Session creation always user-initiated (one click → one Session) or also driven by a job that can retry? Medblocks does not currently document an Idempotency-Key header — re-check the spec before adding hand-rolled retry-with-jitter on writes.
G. Storage in this app's DB
- Should the app persist Medblocks session_id, connection_id, and last-known status in its own database, or treat Medblocks as the system of record and read on demand?
H. Eventing
- Webhooks are not documented in the current spec. Is the app fine with: (1) reading GET /sessions/{id} on return_url, and (2) reading GET /patients/{id} or GET /patients/{id}/sessions on demand for status?
I. Tests
- Unit only with mocked fetch, contract tests against medblocks.json, or live integration tests against a sandbox key?
J. Compliance + logging
- Any PHI redaction rules, BAA scope, or logging restrictions that affect what we can record (request bodies, patient names, emails)?
K. Anything else surprising in this codebase you want me to know before I touch it.
Where an answer is obvious from Phase 1 evidence, do not ask — state your assumption inline: "Assuming X because <file:line>; tell me if not."
========================================
PHASE 3 — PROPOSE A PLAN
========================================
Before generating code, post a short plan and pause for confirmation. The plan must include:
1. The exact files you will create or modify, with paths that follow THIS codebase's conventions (not generic Next.js paths if this isn't Next.js).
2. New env vars: MEDBLOCKS_API_KEY (required), MEDBLOCKS_API_URL (default https://app.medblocks.com), MEDBLOCKS_API_VERSION (the date from spec info.version). Where they will be documented (.env.example, README, deploy config).
3. Which endpoints you will call, by operationId from the spec. At minimum:
- api.createSession (POST /sessions) — Picker or Direct
- api.getSession (GET /sessions/{id}) — return_url verification
- api.createPatient (POST /patients) — only if explicit creation chosen
- api.getPatient (GET /patients/{id}) — only if patient-status UI needed
- api.listPatientSessions (GET /patients/{id}/sessions) — only if history UI needed
- api.listFhirSources (GET /connections) — only for Direct mode source search
4. Where the new UI hooks in: which existing component holds the "Connect" button, which page owns the return_url, and how status surfaces back to the user.
5. Error-mapping strategy: how Medblocks error.code + error.type values translate into this app's existing exception/HTTP shapes.
6. Test plan: what cases you'll cover and at what level (unit / contract / integration).
Wait for "go ahead" before writing code, unless the user explicitly told you to one-shot the implementation.
========================================
PHASE 4 — IMPLEMENTATION
========================================
Follow the conventions of THIS codebase. Do not impose Next.js patterns on Express, Express on FastAPI, etc. Generic rules:
1. ENV + CONFIG
- Add MEDBLOCKS_API_KEY to .env.example with a one-line comment about what it is and where to mint it. Update the project's env docs.
- NEVER expose the key through NEXT_PUBLIC_*, VITE_*, EXPO_PUBLIC_*, PUBLIC_*, or any other client-bundled prefix. NEVER import it from a module that ships to the browser. NEVER call the Medblocks API from a Server Component that streams to the client without first ensuring the key isn't serialized into the payload.
- Default MEDBLOCKS_API_URL to https://app.medblocks.com; allow override for sandboxes.
2. TYPED CLIENT
- If the project already has an OpenAPI codegen pipeline, extend it: feed medblocks.json through the same tool (openapi-typescript / orval / hey-api / openapi-python-client / oapi-codegen / quicktype / NSwag, whatever is in use).
- Otherwise, drop in one server-only module in the conventional location for this stack:
- Next.js (App Router): lib/medblocks.ts (or src/lib/medblocks.ts)
- Next.js (Pages): lib/medblocks.ts
- Remix / SvelteKit: app/lib/medblocks.server.ts (note the .server.ts suffix to lock it server-side)
- Express/Hono/NestJS: src/services/medblocks.ts
- Bun.serve project: src/medblocks.ts
- FastAPI: app/services/medblocks.py
- Django: <project>/services/medblocks.py
- Rails: app/services/medblocks.rb
- Go: internal/medblocks/client.go
- .NET: Services/MedblocksClient.cs
- …or wherever this repo already keeps third-party API clients.
- Every request must send:
- Authorization: Bearer ${MEDBLOCKS_API_KEY}
- Version: ${MEDBLOCKS_API_VERSION} (the date from spec info.version)
- Content-Type: application/json (on POST/PUT)
- Implement an error type that captures HTTP status + the full envelope: error.type, error.code, error.message, error.param, error.doc_url, error.request_id. Never throw raw Error("Medblocks failed") — keep the envelope.
- Use cursor pagination: list responses include has_more and next_cursor; pass starting_after on the next call.
3. SERVER ROUTES THE BROWSER CAN CALL
- One route per flow, all server-only. Route paths follow the framework's conventions. Each route forwards the upstream HTTP status and the Medblocks error body verbatim on failures, so the frontend can render error.message.
- Start Session (Picker): POST → calls api.createSession without connection_id. Body: { patient_id, return_url, return_button_label, patient_name?, patient_email?, recommended_connection_ids?, metadata? }. Response surfaces { id, url, status }.
- Start Session (Direct): POST → calls api.createSession WITH connection_id. Body: same as above but connection_id replaces recommended_connection_ids (the two are mutually exclusive — verify the spec).
- Read Session: GET ?id=... → calls api.getSession. Used by the return_url page.
- Search sources: GET ?q=... (Direct only) → calls api.listFhirSources with q + limit.
- (Optional) Patient: GET ?id=... → calls api.getPatient. Used for connected-source UI.
- (Optional) History: GET ?patient_id=... → calls api.listPatientSessions.
4. FRONTEND WIRING
- "Connect" button: POSTs to your server's start-session route, then sets window.location.href = body.url. Disable while pending.
- Return page: read session_id from the query string (always present). In Direct mode you also get success, connection_id, error, error_description. Treat the query string as a hint only. The source of truth is GET /sessions/{id}.connections — connections[].status === "active" means the patient is actually connected. session.status === "complete" only means the hosted flow finished.
- Render distinct UI for success / partial failure / outright failure / patient-cancelled. On every failure, log error.request_id so support can correlate.
5. BACKGROUND PULL — SET EXPECTATIONS EXPLICITLY
- A "complete" Session does NOT mean records are pulled. Medblocks schedules background retrieval after authorization. Surface "Connected — pulling records" to the user, then rely on dashboard exports or the upcoming FHIR access surface for record-level signals. Do not block the patient UI on data availability.
6. ERROR HANDLING
- Branch on error.code (stable) first, error.type (broad) only as a fallback. Common branches to handle by code:
- missing_api_key / invalid_api_key / expired_api_key → 500 to user, page on-call.
- unsupported_api_version → fail loudly in CI before deploy.
- bad_request / invalid_data → surface error.param to the caller with a 400.
- resource_not_found → 404 with a retry path.
- external_id_already_exists / resource_conflict → product decision on collision policy.
- throttled / quota_exceeded → exponential backoff with jitter; emit telemetry.
- 5xx api_error → bounded retry on idempotent GETs only; never silent-retry writes unless the spec adds Idempotency-Key.
- 502 ehr_error / oauth_error / token_exchange_failed / token_unavailable → surface to the user with a "reconnect" CTA; do NOT show raw portal text.
- Use the project's existing logger. Required log fields on every Medblocks failure: http_status, error.code, error.type, error.request_id, and the relevant patient_id / session_id.
7. IDEMPOTENCY + CONCURRENCY
- patient_id is the stable upsert key — re-sending POST /patients or upserting via POST /sessions with the same patient_id is safe. Do not invent surrogate IDs.
- Session creation is not idempotent: every call creates a new sess_*. If a background worker creates Sessions, persist the resulting session_id keyed by the originating intent BEFORE redirecting, to avoid duplicates.
8. SECURITY + PII
- No key in client bundles, ever. Re-check after bundling (grep the built output for the key prefix during CI).
- Do not log full patient names / emails unless the product's PII policy allows it. Always log session_id and request_id.
- All Medblocks traffic is over HTTPS. Verify the base URL is https://, not http://.
========================================
PHASE 5 — VERIFY (before reporting done)
========================================
Don't claim success until you've actually run things. Report results in this order:
1. Static checks pass: the project's typechecker (tsc --noEmit / mypy / pyright / go vet / cargo check / dotnet build), linter, and formatter.
2. Tests pass: existing suite + any new tests you wrote.
3. A real smoke test using a sandbox or dev API key:
a. Create or upsert a Patient.
b. Create a Picker (and/or Direct) Session — confirm the response has id (sess_*) and url.
c. Hit url in a browser, complete the hosted step in a sandbox EHR.
d. Land on your return_url. Confirm the page calls GET /sessions/{id} and renders connections[].status correctly.
4. Final report to the user:
- Files changed (paths).
- Env vars to set, where, and the spec's current info.version.
- Exact manual steps to repeat the smoke test.
- What you deliberately did NOT implement and why.
- Any assumption from Phase 2 that still needs human confirmation.
========================================
NON-NEGOTIABLES
========================================
- Server-only. API key never reaches a browser.
- Only call routes that exist in medblocks.json. Don't fabricate endpoints, fields, headers, or error codes.
- After every return_url, re-read GET /sessions/{id} from the server. The query string is a hint, not a source of truth.
- Version header must match the spec's info.version for production deploys.
- Log error.request_id on every failure.
- No retry-with-jitter on write endpoints without explicit spec support for idempotency.
- When uncertain, STOP and ask. In a healthcare integration, a wrong assumption baked in is worse than a slow integration.
========================================
DECISION HINTS
========================================
- Product already has a facility/EHR picker UX → Direct Mode.
- Product just wants a "Connect your records" button → Picker Mode.
- Both modes can coexist on the same API key.
- Stable patient_id ≠ email. Use an internal user ID, member ID, or chart ID — anything that won't change for that human.
- "Session complete" = patient finished the hosted flow. "Connection active" = Medblocks holds a usable token. Use connections[].status === "active" as the success signal, not session.status.Use Direct Mode when the EHR choice belongs inside your product flow. A trial matching app, referral intake, care navigation tool, or patient onboarding screen can search sources itself and pass the chosen connection_id into a Session.

Search Sources
Use /connections to search the Medblocks FHIR source catalog. Render the name, type, and logo_url in your UI.
async function searchFacilities(query: string) {
if (query.length < 2) return [];
const response = await fetch(
`/api/ehr-search?q=${encodeURIComponent(query)}`,
);
const { data } = await response.json();
return data as Array<{
id: string;
name: string;
type: string;
logo_url: string | null;
}>;
}qstringSearch text for the source catalog.
limitintegerMaximum number of items to return.
starting_afterstringPagination cursor from next_cursor.
const MEDBLOCKS_BASE = process.env.MEDBLOCKS_API_URL ?? "https://app.medblocks.com";
const MEDBLOCKS_VERSION = "2026-04-25";
type FhirSource = {
id: string;
name: string;
type: string;
fhir_base_url: string;
logo_url: string | null;
portal_url: string | null;
};
export async function searchFhirSources(query: string) {
const params = new URLSearchParams({ q: query, limit: "20" });
const response = await fetch(`${MEDBLOCKS_BASE}/connections?${params}`, {
headers: {
Authorization: `Bearer ${process.env.MEDBLOCKS_API_KEY}`,
Version: MEDBLOCKS_VERSION,
},
});
if (!response.ok) throw new Error(`Medblocks API error ${response.status}`);
const body = await response.json() as { data: FhirSource[] };
return body.data;
}qstringSearch text for the source catalog.
limitintegerMaximum number of items to return.
starting_afterstringPagination cursor from next_cursor.
AuthorizationBearer <token>requiredMedblocks API key for server-side requests.
VersionstringDate-pinned API version. If omitted, Medblocks uses the version pinned on your API key.
Response
{
"resource_type": "list",
"data": [
{
"id": "fhirsrc_01J9YR9N3X4VZ6P2K5RH7M3LMP",
"resource_type": "fhir_source",
"name": "Epic MyChart",
"type": "epic",
"fhir_base_url": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4",
"logo_url": "https://cdn.medblocks.com/ehr-logos/epic.svg",
"portal_url": "https://mychart.epic.com"
}
],
"has_more": false,
"next_cursor": null
}Create A Direct Session
When the patient picks a source, create a Session with connection_id. The patient goes straight to that portal’s login.
async function connectFacility(facility: { id: string; name: string }) {
// Store the selected facility for the return screen
localStorage.setItem(
"pending_facility",
JSON.stringify({ id: facility.id, name: facility.name }),
);
const response = await fetch("/api/connect-ehr", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
patientId: currentPatientId,
patientName: currentPatientName,
connectionId: facility.id,
}),
});
const { url } = await response.json();
// Redirect the patient to the EHR portal login
window.location.href = url;
}patient_idstringrequiredStable patient ID from your system. Creates the Patient if needed.
connection_idstringSource ID from /connections. Used for Direct Mode. Mutually exclusive with recommended_connection_ids.
patient_emailstringPatient email to store or update.
patient_namestringPatient display name to store or update.
return_urlstringrequiredURL to redirect after the Connect flow.
return_button_labelstringText shown on the patient-facing completion button.
export async function createDirectSession(input: {
patientId: string;
patientName?: string;
connectionId: string;
returnUrl: string;
}) {
const response = await fetch(`${MEDBLOCKS_BASE}/sessions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MEDBLOCKS_API_KEY}`,
"Content-Type": "application/json",
Version: MEDBLOCKS_VERSION,
},
body: JSON.stringify({
patient_id: input.patientId,
patient_name: input.patientName,
connection_id: input.connectionId,
return_url: input.returnUrl,
return_button_label: "Back to your app",
}),
});
if (!response.ok) throw new Error(`Medblocks API error ${response.status}`);
return response.json() as Promise<{ id: string; url: string }>;
}patient_idstringrequiredStable patient ID from your system. Creates the Patient if needed.
connection_idstringSource ID from /connections. Used for Direct Mode. Mutually exclusive with recommended_connection_ids.
patient_emailstringPatient email to store or update.
patient_namestringPatient display name to store or update.
return_urlstringrequiredURL to redirect after the Connect flow.
return_button_labelstringText shown on the patient-facing completion button.
AuthorizationBearer <token>requiredMedblocks API key for server-side requests.
VersionstringDate-pinned API version. If omitted, Medblocks uses the version pinned on your API key.
connection_id accepts either the fhirsrc_* public ID or the raw fhir_base_url, but the public ID is easier to store.
Handle Success
On success, Medblocks redirects to your return URL with a Session ID, success flag, and connection ID.
https://your-app.example.com/connect?session_id=sess_...&success=true&connection_id=fhirsrc_...async function handleReturn() {
const params = new URLSearchParams(window.location.search);
const sessionId = params.get("session_id");
const success = params.get("success");
const error = params.get("error_description") ?? params.get("error");
if (success === "true") {
showSuccess("Facility connected");
}
if (success === "false") {
showError(error ?? "Connection failed. Please try again.");
}
// Verify with backend regardless of query params
if (sessionId) {
const response = await fetch(`/api/connect/session?id=${sessionId}`);
const session = await response.json();
await refreshConnectedFacilities(session);
}
}idstringrequiredSession ID from the return URL.
export async function readConnectSession(sessionId: string) {
const response = await fetch(
`${MEDBLOCKS_BASE}/sessions/${encodeURIComponent(sessionId)}`,
{
headers: {
Authorization: `Bearer ${process.env.MEDBLOCKS_API_KEY}`,
Version: MEDBLOCKS_VERSION,
},
},
);
if (!response.ok) throw new Error(`Medblocks API error ${response.status}`);
return response.json() as Promise<{
id: string;
status: string;
connections: Array<{ connection_id: string; status: string }>;
}>;
}idstringrequiredSession ID from the return URL.
AuthorizationBearer <token>requiredMedblocks API key for server-side requests.
VersionstringDate-pinned API version. If omitted, Medblocks uses the version pinned on your API key.
Update your UI optimistically, then call GET /sessions/{session_id} from your backend for the source of truth.

session_idstringrequiredSession ID to read from your backend.
success"true"requiredDirect mode success flag from the browser flow.
connection_idstringrequiredConnected source ID.
Handle Failure
On failure, your return URL receives success=false and may include an error code and description.
https://your-app.example.com/connect?session_id=sess_...&success=false&error=user_denied&error_description=Patient+canceled+the+connectionShow a friendly retry path. Log the Session ID for support.

session_idstringrequiredSession ID to read from your backend.
success"false"requiredDirect mode success flag from the browser flow.
errorstringrequiredStable failure code such as user_denied or oauth_callback_failed.
error_descriptionstringHuman-readable failure text.
List Connected Facilities
After return, rebuild the connected source list from patient Sessions. This pattern works when your app does not need raw records, only connection status.
async function refreshConnectedFacilities() {
const response = await fetch(`/api/connected-facilities?patientId=${patientId}`);
const facilities = await response.json();
// Render the connected facility list in your UI
renderFacilityList(facilities);
}idstringrequiredYour patient_id from Patient creation or Session upsert.
limitintegerMaximum number of items to return.
starting_afterstringPagination cursor from next_cursor.
export async function listConnectedSourceIds(patientId: string) {
const params = new URLSearchParams({ limit: "100" });
const response = await fetch(
`${MEDBLOCKS_BASE}/patients/${encodeURIComponent(patientId)}/sessions?${params}`,
{
headers: {
Authorization: `Bearer ${process.env.MEDBLOCKS_API_KEY}`,
Version: MEDBLOCKS_VERSION,
},
},
);
if (!response.ok) throw new Error(`Medblocks API error ${response.status}`);
const { data: sessions } = await response.json() as {
data: Array<{
connections: Array<{ connection_id: string; status: string }>;
}>;
};
const activeIds = new Set<string>();
for (const session of sessions) {
for (const conn of session.connections) {
if (conn.status === "active") activeIds.add(conn.connection_id);
}
}
return [...activeIds];
}idstringrequiredYour patient_id from Patient creation or Session upsert.
limitintegerMaximum number of items to return.
starting_afterstringPagination cursor from next_cursor.
AuthorizationBearer <token>requiredMedblocks API key for server-side requests.
VersionstringDate-pinned API version. If omitted, Medblocks uses the version pinned on your API key.
Example: TrialMatched Flow
TrialMatched is a good Direct mode shape because the product already has a patient-facing search experience. The patient searches for a facility, chooses the right Epic Sandbox or health system, and TrialMatched passes that selected connection_id into Session creation.
The important boundary is server-only API access. The browser searches your backend. Your backend searches Medblocks.
async function onFacilitySelected(facility: { id: string; name: string }) {
const response = await fetch("/api/connect-ehr", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
patientId: trialmatchedPatientId,
patientName: patientDisplayName,
connectionId: facility.id,
}),
});
const body = await response.json();
if (!response.ok) {
showConnectionError(body.error?.message ?? "Could not start connection");
return;
}
window.location.href = body.url;
}qstringSearch text for the source catalog.
limitintegerMaximum number of items to return.
export async function searchConnectionCatalog(query: string) {
if (query.trim().length < 2) {
return { data: [] };
}
const params = new URLSearchParams({ q: query, limit: "20" });
const response = await fetch(`${MEDBLOCKS_BASE}/connections?${params}`, {
headers: {
Authorization: `Bearer ${process.env.MEDBLOCKS_API_KEY}`,
Version: MEDBLOCKS_VERSION,
},
});
const body = await response.json();
if (!response.ok) throw new Error(body.error?.message ?? "Could not search sources");
return body as {
data: Array<{ id: string; name: string; type: string }>;
};
}qstringSearch text for the source catalog.
limitintegerMaximum number of items to return.
AuthorizationBearer <token>requiredMedblocks API key for server-side requests.
VersionstringDate-pinned API version. If omitted, Medblocks uses the version pinned on your API key.
On the return page, TrialMatched reads success, session_id, connection_id, error, and error_description from the URL for quick UI copy, then calls GET /sessions/{id} from the backend before saving anything as connected.
Common Errors
| Code | Meaning |
|---|---|
resource_not_found | The selected connection_id does not exist or is not available. |
bad_request | connection_id is missing or conflicts with Picker mode fields. |
oauth_error | The portal authorization failed upstream. |
token_exchange_failed | Medblocks could not exchange the OAuth code for tokens. |
Read the full envelope and code list in API Errors.
Related
- Use hosted source selection with Picker Mode.
- Read the concept page: Session.
- Continue with After Connection.
- Inspect the schema in Create a Connect Session.
