EHR embedding: cards and chart documents
For EMR vendors and practices, with no EHR registration needed: find a patient's member_ref from your own MRN and date of birth, fetch the same CDS Hooks cards Norvya's hook service returns and render them natively, and get the pre-visit brief as a FHIR R4 DocumentReference to file in the chart. Medical practices only; each patient's sharing choices apply.
- Who calls it
- Core for EMR vendors, Specialty care; useful for Practice management & billing
- Before production
- No gate of its own; your market's “Before production” applies.
- Sandbox
- Build against synthetic members first. What each sandbox holds
- Standards
- CDS Hooks card format; FHIR R4 DocumentReference
Markets that use this family
- EMR vendors (core)
- Practice management & billing (useful)
- Specialty care (core)
Scopes
| Scope | Allows |
|---|---|
clinical.brief.read | Read the clinician pre-visit brief for a patient (medical practices only; per-section consent). |
clinical.patient.match | Find the member_ref of one of your practice's patients from your own identifier (MRN) plus date of birth; exactly one match or 404 (medical practices only; tightly rate limited, audited). |
Endpoints
Find a patient's member_ref from your MRN and date of birth
Returns the member_ref of the ONE active patient of your practice whose MRN in Norvya equals identifier.value AND whose date of birth equals birth_date. identifier.system must be one your practice uses for its MRN in Norvya (its SMART or write-back registration) or urn:norvya:practice-mrn. Anything else (no match, several matches, a wrong birth date, an unknown system, someone else's patient) is the same 404 member_not_found. Rate limited more tightly than other calls (429 rate_limited); every call is audited (never with the identifier value or birth date). Medical practices only (409 not_a_medical_practice).
Request body
Content type: application/json (required)
YYYY-MM-DD; must equal the date of birth Norvya holds.
Errors specific to this call
404member_not_found409not_a_medical_practiceAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/patients/match" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"birth_date": "1968-05-01",
"identifier": {
"system": "urn:norvya:practice-mrn",
"value": "MRN-004211"
}
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.post(
BASE + "/v1/patients/match",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"birth_date": "1968-05-01",
"identifier": {
"system": "urn:norvya:practice-mrn",
"value": "MRN-004211",
},
},
timeout=30,
)
resp.raise_for_status()
print(resp.json())// Node.js 18+ (ES module). Keep production credentials on your server.
const BASE = "https://norvya.fly.dev";
const url = new URL(`${BASE}/v1/patients/match`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"birth_date": "1968-05-01",
"identifier": {
"system": "urn:norvya:practice-mrn",
"value": "MRN-004211"
}
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());{
"member_ref": "mr_3f9a0c1d2e4b5a6978c0d1e2"
}The response is the shape from the schema, not live data.
Try it in your sandbox
CDS Hooks-format cards for one patient, to render in your own UI
The cards Norvya's CDS Hooks service returns when this patient's chart is opened at your practice, as CDS Hooks 2.0 card objects, so you can render them natively without registering a CDS Hooks service. A card is a pointer: it names the record areas the patient shares with your practice, never a value. The SMART launch link is included only when your practice has an enabled SMART registration in Norvya. 404 member_not_found for anyone who is not your active patient; 409 not_a_medical_practice.
Parameters
Card language (an app language; else English).
EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens
Errors specific to this call
404member_not_found409not_a_medical_practiceAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/patients/$MEMBER_REF/cards" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
member_ref = os.environ["MEMBER_REF"]
resp = requests.get(
f"{BASE}/v1/patients/{member_ref}/cards",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
resp.raise_for_status()
print(resp.json())// Node.js 18+ (ES module). Keep production credentials on your server.
const BASE = "https://norvya.fly.dev";
const member_ref = process.env.MEMBER_REF;
const url = new URL(`${BASE}/v1/patients/${encodeURIComponent(member_ref)}/cards`);
const res = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());{
"member_ref": "string",
"external_id": "string",
"cards": [
{
"uuid": "string",
"summary": "Norvya Pre-Visit Brief available",
"detail": "string",
"indicator": "info",
"source": {
"label": "Norvya",
"url": "string",
"topic": {}
},
"links": [
{}
]
}
]
}The response is the shape from the schema, not live data.
Try it in your sandbox
The pre-visit brief as a FHIR R4 DocumentReference, ready to file in your chart
The same consent-gated brief as GET .../brief (sections the patient has not shared with your practice are never computed and are listed as not shared), rendered as a plain FHIR R4 DocumentReference: text/plain inline (base64), LOINC 34133-9 Summary of episode note, docStatus preliminary (Norvya output for clinician review), no id and no vendor extensions. 409 not_a_medical_practice; 422 ehr_patient_id_invalid.
Parameters
Your chart's FHIR Patient id, used as the subject reference. Default: the member_ref as an identifier.
EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens
Errors specific to this call
409not_a_medical_practice422ehr_patient_id_invalidAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/patients/$MEMBER_REF/brief/document" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
member_ref = os.environ["MEMBER_REF"]
resp = requests.get(
f"{BASE}/v1/patients/{member_ref}/brief/document",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
resp.raise_for_status()
print(resp.json())// Node.js 18+ (ES module). Keep production credentials on your server.
const BASE = "https://norvya.fly.dev";
const member_ref = process.env.MEMBER_REF;
const url = new URL(`${BASE}/v1/patients/${encodeURIComponent(member_ref)}/brief/document`);
const res = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());{
"resourceType": "string",
"status": "string",
"docStatus": "string",
"type": {
"coding": [
{
"system": "string",
"code": "string",
"display": "string"
}
],
"text": "string"
},
"category": [
{
"coding": [
{}
],
"text": "string"
}
],
"subject": {
"reference": "Patient/123",
"identifier": {
"system": "string",
"value": "string"
}
},
"date": "string",
"description": "string",
"content": [
{
"attachment": {
"contentType": "text/plain; charset=utf-8",
"data": "string",
"title": "string",
"creation": "string"
}
}
],
"context": {
"period": {
"start": "string",
"end": "string"
}
}
}The response is the shape from the schema, not live data.
Try it in your sandbox
Generated from the Partner API router and developers_catalog.py. The router docstring is the source of each description.