Practice operations & reports
The rest of a practice's day from your own system: the member annual review a physician hands the patient (data or PDF, for the findings you select; urgent and practice-internal findings refused), one clinician's morning brief, rule-based outreach drafts for open worklist findings (never sent by the call), the practice's quarterly value report and programme outcomes report as aggregates with small cells withheld (JSON or PDF; observational, no dollar figure), clinicians' licence and NPI fields with their verification state, a telehealth video room for an existing appointment, the benefits-coordination worklist, and invitations for an imported patient to claim their own account (Norvya sends the invitation; the code goes to the patient only). Treating practices only.
- Who calls it
- Core for Concierge doctors, Independent practices; useful for EMR vendors
- Before production
- Claim invitations need a BAA on file (a sandbox simulates).
- Sandbox
- Build against synthetic members first. What each sandbox holds
Markets that use this family
- Concierge doctors (core)
- Independent practices (core)
- EMR vendors (useful)
Scopes
| Scope | Allows |
|---|---|
clinical.annual_review.read | Read a patient's member annual review content (their year in numbers, what is going well, screenings and vaccines, the open findings a physician may select); urgent and practice-internal findings are never selectable (treating practices only). |
clinical.credentials.read | Read your practice's clinicians with the licence and NPI fields Norvya holds for them and whether Norvya verified them (treating practices only). |
clinical.morning_brief.read | Read one clinician's morning brief for a day: the members on their schedule, what changed since each member's last visit, open referrals and suggested labs, on what the practice may use for each member (treating practices only). |
clinical.outreach.read | Read rule-based message drafts for your patients' open worklist findings, in the patient's language; never sent by this call (send with messages.write) and never for urgent or practice-internal findings (treating practices only). |
clinical.reports.read | Read your practice's quarterly value report and programme outcomes report as JSON: aggregates only, counts below the small-cell minimum withheld, with the reports' honest framing (observational, this practice only); no dollar figure (treating practices only). |
clinical.video.write | Open a telehealth video room for an existing appointment of your practice (Daily, under Norvya's BAA; recording and transcription off); participants join in the Norvya app (treating practices only). |
members.claim_invite | Invite a patient your practice imported (no Norvya login yet) to claim their own account: Norvya emails its own invitation in the patient's language with a one-time code that also needs their date of birth; follow the invitation to claimed (treating practices with a BAA on file). |
programs.benefits.read | Read your practice's benefits-coordination worklist (transport and home-modification requests on open community referrals); Norvya never determines eligibility or books a ride (treating practices only). |
Endpoints
A patient's member annual review content (JSON)
The one-page review a physician hands the patient at their annual visit, as data: their year in numbers (a critical value is withheld), what is going well, the findings YOU select (urgent, practice-internal and unverified-source findings are refused: 409 member_review_urgent_finding / member_review_finding_not_for_patients), screenings and vaccines (only when the patient shares their medical history with you), the medicine count and the review sentence. Rule-based wording (no LLM). The doctor line is left out: there is no Norvya user behind a partner call.
Parameters
finding_key values from selectable_findings to put under "What we'll focus on" (at most 4).
Any of Norvya's languages; English when the review has no wording for it.
EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens
All 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/annual-review" \
-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}/annual-review",
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)}/annual-review`);
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": "mr_3f9c1a0b7d2e4c6a8b1d0e2f",
"external_id": "string",
"engine": "member_review_v1",
"lang": "en",
"selectable_findings": [
{}
],
"review": {},
"pdf_available": false
}The response is the shape from the schema, not live data.
Try it in your sandbox
A patient's member annual review as a PDF
The same one-page review the practice prints from Norvya, for the selection you pass (the same refusals as the JSON call), rendered by the same builder. A disclosure: audited as a PHI export before it is returned (503 when the audit log is down). No doctor line (no Norvya user behind a partner call).
Parameters
As for the JSON call (at most 4).
Any of Norvya's languages; English when the script's font is missing.
EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens
Errors specific to this call
503whenAll responses
200The PDF.401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/patients/$MEMBER_REF/annual-review/content" \
-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}/annual-review/content",
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)}/annual-review/content`);
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());Try it in your sandbox
Your practice's quarterly value report (aggregates, JSON)
The proof-of-value report Norvya's practice owners see: blood pressure, HbA1c and LDL control, follow-up, findings raised and closed, care gaps and engagement between two stored worklist snapshots. Aggregates only; every count from 1 to min_cell - 1 is withheld by the engine. Printed framing: observational, this practice only, not a controlled comparison. No dollar figure. 422 value_report_invalid_period.
Parameters
YYYY-Qn | since_joining; default the last full quarter.
Errors specific to this call
422value_report_invalid_periodAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/value-report" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/value-report",
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 url = new URL(`${BASE}/v1/practice/value-report`);
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());Try it in your sandbox
Your practice's quarterly value report as a PDF
The same report as the JSON call, rendered by the builder behind the practice owner's own PDF download. Aggregates only (small cells withheld by the engine); audited as an export before it is returned.
Parameters
YYYY-Qn | since_joining; default the last full quarter.
All responses
200The PDF.401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/value-report/content" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/value-report/content",
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 url = new URL(`${BASE}/v1/practice/value-report/content`);
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());Try it in your sandbox
Your programme outcomes report (aggregates, JSON)
The programme outcomes report (engagement, retention, paired measures, questionnaire change, adherence, transitions) over a window: cohort counts, proportions and medians with intervals and completeness, each below the small-cell minimum withheld (status too_small when the whole cohort is). Observational framing on every report. 422 outcomes_invalid_compare / outcomes_invalid_date.
Parameters
YYYY-MM-DD
YYYY-MM-DD; default today
Comma list; default every applicable metric (see available_metrics).
first_vs_last_30d (default) | pre_post_enrolment
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/outcomes-report" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/outcomes-report",
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 url = new URL(`${BASE}/v1/practice/outcomes-report`);
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());Try it in your sandbox
Your programme outcomes report as a PDF
The same report as the JSON call, rendered by the builder behind the practice's own PDF download; small cells withheld, observational framing printed. Audited as an export before it is returned.
Parameters
YYYY-MM-DD
YYYY-MM-DD; default today
Comma list (see the JSON call).
first_vs_last_30d (default) | pre_post_enrolment
All responses
200The PDF.401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/outcomes-report/content" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/outcomes-report/content",
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 url = new URL(`${BASE}/v1/practice/outcomes-report/content`);
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());Try it in your sandbox
One clinician's morning brief for a day
The members on the clinician's schedule that day (Norvya appointments, else the practice's planned visits from a panel import, which are the practice's day, not the clinician's), each with what changed since their last visit, their open referrals and the suggested labs to confirm (nothing is ordered). Read on what the practice may use for each member. At most 40 members. 404 clinician_not_found.
Parameters
staff_ref of a clinician of your practice (GET /v1/practice/clinicians/credentials or /v1/scheduling/settings).
YYYY-MM-DD; default today.
Errors specific to this call
404clinician_not_foundAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/morning-brief?clinician=$CLINICIAN" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/morning-brief",
headers={"Authorization": f"Bearer {TOKEN}"},
params={"clinician": os.environ["CLINICIAN"]},
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/practice/morning-brief`);
url.searchParams.set("clinician", process.env.CLINICIAN);
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());Try it in your sandbox
Rule-based message drafts for open worklist findings
One draft per OPEN finding in your latest worklist run (the wording library the annual review prints, in the patient's language, framed with a greeting and "reply to this message"; never the clinician reason text or a number). Urgent and practice-internal findings, and patients who turned messages off, are skipped and counted. Nothing is sent: send the body with POST /v1/members/{member_ref}/messages (messages.write) and mark the finding with POST /v1/panel/worklist/items/{item_ref}/triage.
Parameters
Only this patient.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/outreach-drafts" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/outreach-drafts",
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 url = new URL(`${BASE}/v1/practice/outreach-drafts`);
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());Try it in your sandbox
Invite an imported patient to claim their own Norvya account
For a patient your practice imported (a practice-of-record account with no login): Norvya emails its own invitation, in the patient's language and your branding, with a one-time code; claiming also needs the date of birth you recorded (five wrong tries lock it for an hour). The contact you send is held sealed for the invitation and never put on the account. A fresh code replaces any earlier one. outcome says what happened; the code is never returned. Never looks up an account by address. 409 claim_invite_baa_required; 422 claim_email_invalid / claim_phone_invalid / claim_channel_invalid. A sandbox simulates (nothing written or sent).
Parameters
Request body
Content type: application/json (required)
email | sms (sms only where Norvya's text sender is configured).
Held sealed for the invitation only; never put on the account, never returned.
The patient's language (17 supported); the invitation email is written in it.
Errors specific to this call
422claim_email_invalidAll 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/$MEMBER_REF/claim-invite" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "email",
"email": "string",
"phone": "string",
"language": "es"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
member_ref = os.environ["MEMBER_REF"]
resp = requests.post(
f"{BASE}/v1/patients/{member_ref}/claim-invite",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"channel": "email",
"email": "string",
"phone": "string",
"language": "es",
},
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)}/claim-invite`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"channel": "email",
"email": "string",
"phone": "string",
"language": "es"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());{
"member_ref": "string",
"outcome": "string",
"status": "sent",
"channel": "string",
"has_email": false,
"has_phone": false,
"sent_at": "string",
"expires_at": "string",
"claimed_at": "string",
"simulated": false,
"code_returned": false
}The response is the shape from the schema, not live data.
Try it in your sandbox
Where a patient's claim invitation stands
no_contact | not_sent | sent | expired | claimed | revoked, or has_login for a patient who already signs in themselves; whether a contact is on file (never the contact) and the dates. The resource a member.account_claimed webhook points to.
Parameters
All 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/claim-invite" \
-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}/claim-invite",
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)}/claim-invite`);
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",
"outcome": "string",
"status": "sent",
"channel": "string",
"has_email": false,
"has_phone": false,
"sent_at": "string",
"expires_at": "string",
"claimed_at": "string",
"simulated": false,
"code_returned": false
}The response is the shape from the schema, not live data.
Try it in your sandbox
Open a video room for an existing appointment
Through the app's own video path (telehealth_video.create_visit): the appointment's clinician hosts, the patient joins from the app, the room opens 15 minutes before and closes 60 minutes after the booked end. Idempotent: an open room for the appointment is returned (created false). Recording and transcription are off and cannot be turned on. 404 appointment_not_found; 422 partner_appointment_required / host_required / invalid_context; 503 video_not_activated (no BAA-covered vendor configured; a sandbox uses Norvya's simulated vendor).
Parameters
Request body
Content type: application/json
general | concierge (therapy and medical travel visits are opened in the app).
Errors specific to this call
422partner_appointment_required503video_not_activatedAll 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/appointments/$APPOINTMENT_REF/video-link" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"context": "concierge",
"interpreter_language": "es"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
appointment_ref = os.environ["APPOINTMENT_REF"]
resp = requests.post(
f"{BASE}/v1/appointments/{appointment_ref}/video-link",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"context": "concierge",
"interpreter_language": "es",
},
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 appointment_ref = process.env.APPOINTMENT_REF;
const url = new URL(`${BASE}/v1/appointments/${encodeURIComponent(appointment_ref)}/video-link`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"context": "concierge",
"interpreter_language": "es"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());{
"appointment_ref": "string",
"video_visit_ref": "string",
"created": false,
"status": "string",
"scheduled_start": "string",
"duration_minutes": 0,
"opens_at": "string",
"closes_at": "string",
"joinable_now": false,
"interpreter_language": "string",
"sandbox": false,
"recording": false,
"transcription": false,
"join_in_app": false
}The response is the shape from the schema, not live data.
Try it in your sandbox
Your clinicians with their licence and NPI fields
The active clinical staff (admin, provider, pharmacist, therapist) as staff_ref values with the licence fields Norvya holds on their account: licence number, US state or issuing country and authority (DHA, DoH, MOHAP ... outside the US, where there is no NPI and no state), NPI, and whether Norvya verified the professional (verified_professional, from the clinician registration review). Norvya does not track licence expiry on the account and does not re-verify against a board here. Staff details are not patient data.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/clinicians/credentials" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/clinicians/credentials",
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 url = new URL(`${BASE}/v1/practice/clinicians/credentials`);
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());Try it in your sandbox
Your benefits-coordination worklist (transport, home modification)
The clinic benefits worklist (benefits.clinic_worklist): transport requests and home-modification (HCBS) needs on OPEN community referrals, each with its member_ref and the sdoh_referral_ref of the referral it rides on, for patients who share their medical history with you (the social-needs family's gate). Norvya books no ride, authorises no service and determines no eligibility; every row says so.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/practice/benefits/worklist" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/practice/benefits/worklist",
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 url = new URL(`${BASE}/v1/practice/benefits/worklist`);
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());Try it in your sandbox
Webhook events
Payloads carry no health data, only a reference to fetch with your token. Each event needs its scope as well as webhooks.manage. Signing and retries
| Event | Scope | When |
|---|---|---|
member.account_claimed | members.claim_invite | A patient the practice imported claimed their own account with the practice's invitation code. |
Generated from the Partner API router and developers_catalog.py. The router docstring is the source of each description.