Medical travel
For international patient desks and medical-travel coordinators: the inquiry desk before a journey (create an inquiry from your CRM by its own id, quote it as an immutable Good Faith Estimate version, move it through the desk's statuses and convert an accepted one to a journey), open and run care journeys (forward-only phases, the patient's acceptance first), the journey's in-app thread with the patient and the caregiver they approved (your posts shown as your system, never a person), flights and transfers with live status where a provider is configured, the care team and its hours, approval progress, the partner-hospital directory (accreditations as recorded; Norvya verifies none), home nursing under the letter of guarantee (the LOG is the gate: no shift beyond it), visit records, satisfaction surveys, a sponsor's referral for a patient without the app, the HL7 International Patient Summary, the repatriation packet and the bilingual summary for the clinician at home, weekly nursing reports and monthly invoices in the sponsor's language, and machine translation of a journey's free text. Visit notes, approvals and consents stay with the named people who attest to them in Norvya.
- Who calls it
- Core for Medical travel desks; useful for Hospitals and health systems
- Before production
- A BAA on file (or a sandbox); any treating-practice organisation can act as a desk.
- Sandbox
- Build against synthetic members first. What each sandbox holds
- Standards
- HL7 International Patient Summary
Markets that use this family
- Hospitals and health systems (useful)
- Medical travel desks (core)
Scopes
| Scope | Allows |
|---|---|
travel.billing.read | Read the monthly home-nursing invoices per PRN (lines, hold flags, totals in integer minor units) and download them as PDF or HTML. |
travel.documents.read | Download a journey's documents: the HL7 International Patient Summary, the FHIR repatriation packet, the bilingual readable summary and the weekly nursing reports (each an audited export, inside the patient's sharing choices). |
travel.inquiries.read | Read your desk's travel inquiries (contact, origin, language, sponsor, the coordinator's summary, status history) with their quote versions (each an immutable Good Faith Estimate). Medical-travel desks with a BAA on file. |
travel.inquiries.write | Create an inquiry from your own system by its id, edit it, link a patient on your roster, issue or revise its quote, and move it through the desk's own statuses. Converting an accepted inquiry to a journey also needs travel.journeys.write. |
travel.journeys.read | Read your desk's care journeys (phase, dates, continuity after repatriation, timeline), their flights and transfers with live flight status where configured, the care team and staff hours, approval progress, and your partner-hospital directory (accreditations as recorded; Norvya verifies none). Medical-travel desks with a BAA on file. |
travel.journeys.write | Open a care journey for a patient on your roster (it waits for the patient's acceptance), move it forward a phase, add, edit and cancel flights and transfers, and keep your partner-hospital directory. |
travel.messages.read | Read the in-app message thread on a journey: the desk, the patient and the caregiver the patient approved (who see the same thread). |
travel.messages.write | Post in a journey's thread as your desk's system (shown as "Partner system", never as a person); the patient and caregiver get Norvya's generic push. Refused until the patient has accepted the journey. |
travel.nursing.read | Read home-nursing cases: the sponsor's PRN, LOMN and LOG versions, shifts, the hours check, visit records (notes, vitals, medication administration, incidents), satisfaction survey results and sponsor referrals. |
travel.nursing.write | Record a new LOG version, plan, confirm and cancel nursing shifts (never outside the newest LOG's dates or in a pause: the server refuses), move a nursing case forward, and send a sponsor referral for a patient without the app. |
travel.translations.write | Ask Norvya to machine-translate a free-text item on a journey (the reason for travel, a visit note, a medication line, an incident, a LOMN instruction) into a supported language; the original stays the record. |
Endpoints
Medical travel vocabularies: phases, leg modes, nursing statuses, invoice flags, languages
No patient data. `not_exposed` names what stays in Norvya because a person attests to it (a nurse's visit record, an approval decision, an invoice issue).
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/travel/reference" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/travel/reference",
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/travel/reference`);
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 desk's care journeys (open by default), with continuity and nursing flags
Patients actively on your roster only (a sandbox: synthetic members). Newest first, at most 500.
Parameters
Part of a PRN.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/travel/journeys" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/travel/journeys",
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/travel/journeys`);
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());{
"items": [
{}
],
"count": 0
}The response is the shape from the schema, not live data.
Try it in your sandbox
Open a care journey for a patient on your roster (waits for the patient's acceptance)
travel_coordination.create_for_patient: the patient receives Norvya's generic "request to review" push and the journey stays consent_status `pending` until they accept in the app; until then every write answers 409 care_journey_consent_pending. One open journey per patient per desk (409 care_journey_already_open).
Request body
Content type: application/json (required)
A patient on your desk's active roster.
ISO 3166-1 alpha-2, where the patient lives.
The language the documents must be in.
A partner hospital from GET /v1/travel/facilities, linked once the patient accepts.
self | insurer | employer | government | embassy | charity
What the patient travels for, plain words.
Errors specific to this call
409care_journey_consent_pending409care_journey_already_openAll responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/journeys" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"member_ref": "mr_3f9c1a0b7d2e4c6a8b1d0e2f",
"origin_country": "AE",
"destination_country": "US",
"primary_language": "ar",
"interpreter_required": false,
"treating_facility": "string",
"facility_ref": "string",
"sponsor_type": "government",
"sponsor_name": "string",
"reason": "string",
"planned_arrival": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.post(
BASE + "/v1/travel/journeys",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"member_ref": "mr_3f9c1a0b7d2e4c6a8b1d0e2f",
"origin_country": "AE",
"destination_country": "US",
"primary_language": "ar",
"interpreter_required": False,
"treating_facility": "string",
"facility_ref": "string",
"sponsor_type": "government",
"sponsor_name": "string",
"reason": "string",
"planned_arrival": "string",
},
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/travel/journeys`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"member_ref": "mr_3f9c1a0b7d2e4c6a8b1d0e2f",
"origin_country": "AE",
"destination_country": "US",
"primary_language": "ar",
"interpreter_required": false,
"treating_facility": "string",
"facility_ref": "string",
"sponsor_type": "government",
"sponsor_name": "string",
"reason": "string",
"planned_arrival": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
One care journey: phase, dates, timeline, continuity after repatriation
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/travel/journeys/$JOURNEY_REF" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}`);
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
Move a journey forward a phase
care_journey.advance: forward only (409 phase_not_forward), the dates set by the move (arrival, discharge, departure), a timeline line and a chained audit row. 409 care_journey_consent_pending until the patient accepted; 409 approvals_required when your desk requires approvals before travel.
Parameters
Request body
Content type: application/json (required)
planned | arrived | in_treatment | discharged | repatriated | home_follow_up | closed (forward only)
The day it happened (default today).
Errors specific to this call
409phase_not_forward409care_journey_consent_pending409approvals_requiredAll 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/travel/journeys/$JOURNEY_REF/phase" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to_phase": "arrived",
"on_date": "string",
"note": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/phase",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"to_phase": "arrived",
"on_date": "string",
"note": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/phase`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"to_phase": "arrived",
"on_date": "string",
"note": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Link (or unlink) the journey's partner hospital
Parameters
Request body
Content type: application/json (required)
null unlinks.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X PUT "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/facility" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"facility_ref": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.put(
f"{BASE}/v1/travel/journeys/{journey_ref}/facility",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"facility_ref": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/facility`);
const res = await fetch(url, {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"facility_ref": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Flights and transfers, each end in its own time zone, with live flight status
`live` is null until the leg is checked (a flight-data provider must be configured; GET /v1/travel/reference). Marking an outbound leg flown never moves the journey's phase.
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/travel/journeys/$JOURNEY_REF/legs" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/legs",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/legs`);
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());{
"items": [
{}
],
"count": 0
}The response is the shape from the schema, not live data.
Try it in your sandbox
Add a flight or transfer leg
journey_ops.create_leg (direction, mode and both places required; local times with each end's IANA zone; oxygen 0.5-15 L/min; an escort must be on the care team on the day of departure).
Parameters
Request body
Content type: application/json (required)
outbound | return | transfer (required to create)
commercial | commercial_stretcher | air_ambulance | ground_ambulance | private_car | other (required to create)
planned | booked | flown (cancel has its own call)
An IATA code or a place.
Local wall clock at departure, YYYY-MM-DDTHH:MM.
IANA zone of the departure end.
none | WCHR | WCHS | WCHC (IATA)
A staff_ref on the journey's care team on the day of departure (see GET .../team); "" clears it.
All responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/legs" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"direction": "string",
"mode": "string",
"status": "string",
"leg_order": 0,
"carrier": "string",
"flight_number": "string",
"booking_reference": "string",
"depart_place": "AUH",
"arrive_place": "IAH",
"depart_at": "2026-10-20T02:10",
"depart_tz": "Asia/Dubai",
"arrive_at": "string",
"arrive_tz": "string",
"stretcher": false,
"oxygen": false,
"oxygen_lpm": 0.0,
"wheelchair": "string",
"medical_escort_required": false,
"escort_staff_ref": "string",
"other_needs": "string",
"note": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/legs",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"direction": "string",
"mode": "string",
"status": "string",
"leg_order": 0,
"carrier": "string",
"flight_number": "string",
"booking_reference": "string",
"depart_place": "AUH",
"arrive_place": "IAH",
"depart_at": "2026-10-20T02:10",
"depart_tz": "Asia/Dubai",
"arrive_at": "string",
"arrive_tz": "string",
"stretcher": False,
"oxygen": False,
"oxygen_lpm": 0.0,
"wheelchair": "string",
"medical_escort_required": False,
"escort_staff_ref": "string",
"other_needs": "string",
"note": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/legs`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"direction": "string",
"mode": "string",
"status": "string",
"leg_order": 0,
"carrier": "string",
"flight_number": "string",
"booking_reference": "string",
"depart_place": "AUH",
"arrive_place": "IAH",
"depart_at": "2026-10-20T02:10",
"depart_tz": "Asia/Dubai",
"arrive_at": "string",
"arrive_tz": "string",
"stretcher": false,
"oxygen": false,
"oxygen_lpm": 0.0,
"wheelchair": "string",
"medical_escort_required": false,
"escort_staff_ref": "string",
"other_needs": "string",
"note": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Edit a leg (any subset; "" clears a text field)
Parameters
Request body
Content type: application/json (required)
outbound | return | transfer (required to create)
commercial | commercial_stretcher | air_ambulance | ground_ambulance | private_car | other (required to create)
planned | booked | flown (cancel has its own call)
An IATA code or a place.
Local wall clock at departure, YYYY-MM-DDTHH:MM.
IANA zone of the departure end.
none | WCHR | WCHS | WCHC (IATA)
A staff_ref on the journey's care team on the day of departure (see GET .../team); "" clears it.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X PATCH "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/legs/$LEG_REF" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"direction": "string",
"mode": "string",
"status": "string",
"leg_order": 0,
"carrier": "string",
"flight_number": "string",
"booking_reference": "string",
"depart_place": "AUH",
"arrive_place": "IAH",
"depart_at": "2026-10-20T02:10",
"depart_tz": "Asia/Dubai",
"arrive_at": "string",
"arrive_tz": "string",
"stretcher": false,
"oxygen": false,
"oxygen_lpm": 0.0,
"wheelchair": "string",
"medical_escort_required": false,
"escort_staff_ref": "string",
"other_needs": "string",
"note": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
leg_ref = os.environ["LEG_REF"]
resp = requests.patch(
f"{BASE}/v1/travel/journeys/{journey_ref}/legs/{leg_ref}",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"direction": "string",
"mode": "string",
"status": "string",
"leg_order": 0,
"carrier": "string",
"flight_number": "string",
"booking_reference": "string",
"depart_place": "AUH",
"arrive_place": "IAH",
"depart_at": "2026-10-20T02:10",
"depart_tz": "Asia/Dubai",
"arrive_at": "string",
"arrive_tz": "string",
"stretcher": False,
"oxygen": False,
"oxygen_lpm": 0.0,
"wheelchair": "string",
"medical_escort_required": False,
"escort_staff_ref": "string",
"other_needs": "string",
"note": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const leg_ref = process.env.LEG_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/legs/${encodeURIComponent(leg_ref)}`);
const res = await fetch(url, {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"direction": "string",
"mode": "string",
"status": "string",
"leg_order": 0,
"carrier": "string",
"flight_number": "string",
"booking_reference": "string",
"depart_place": "AUH",
"arrive_place": "IAH",
"depart_at": "2026-10-20T02:10",
"depart_tz": "Asia/Dubai",
"arrive_at": "string",
"arrive_tz": "string",
"stretcher": false,
"oxygen": false,
"oxygen_lpm": 0.0,
"wheelchair": "string",
"medical_escort_required": false,
"escort_staff_ref": "string",
"other_needs": "string",
"note": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Cancel a leg (kept, never deleted)
Parameters
Request body
Content type: application/json
All 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/travel/journeys/$JOURNEY_REF/legs/$LEG_REF/cancel" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
leg_ref = os.environ["LEG_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/legs/{leg_ref}/cancel",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"reason": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const leg_ref = process.env.LEG_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/legs/${encodeURIComponent(leg_ref)}/cancel`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"reason": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Check the leg's live flight status now
flight_status.refresh_leg (cached a few minutes per flight and day). Only the live fields change; the leg status and the phase stay manual. 503 flight_status_not_configured; 409 flight_status_leg_not_active; 422 flight_status_needs_flight_number / flight_status_needs_departure.
Parameters
Errors specific to this call
503flight_status_not_configured409flight_status_leg_not_activeAll 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/travel/journeys/$JOURNEY_REF/legs/$LEG_REF/refresh-status" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
leg_ref = os.environ["LEG_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/legs/{leg_ref}/refresh-status",
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 journey_ref = process.env.JOURNEY_REF;
const leg_ref = process.env.LEG_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/legs/${encodeURIComponent(leg_ref)}/refresh-status`);
const res = await fetch(url, {
method: "POST",
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
The journey's care team and the staff hours logged on it
Time tracking, not payroll (journey_ops): minutes by staff member, activity and day; superseded, voided and open entries are not counted. Adding members and clocking time stay in Norvya (a named staff member).
Parameters
IANA zone for the per-day totals (default UTC).
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/team" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/team",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/team`);
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
Approval progress on the journey (read only)
Each run of the desk's approval template: steps, the current step and the decisions with who decided in what role. A step is decided in Norvya by a staff member whose role the step requires.
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/travel/journeys/$JOURNEY_REF/approvals" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/approvals",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/approvals`);
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 partner-hospital directory (accreditations as recorded; Norvya verifies none)
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/travel/facilities" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/travel/facilities",
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/travel/facilities`);
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());{
"items": [
{}
],
"count": 0
}The response is the shape from the schema, not live data.
Try it in your sandbox
Add a partner hospital to your directory
Request body
Content type: application/json (required)
[{body, status, expires_on}] AS RECORDED by your desk; Norvya verifies none.
All responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/facilities" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"facility_type": "string",
"city": "string",
"country": "string",
"address": "string",
"phone": "string",
"email": "string",
"website": "string",
"contacts": [
{}
],
"specialties": [
"string"
],
"accreditations": [
{}
],
"agreement_status": "string",
"agreement_expires_on": "string",
"languages_spoken": [
"string"
],
"notes": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.post(
BASE + "/v1/travel/facilities",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"name": "string",
"facility_type": "string",
"city": "string",
"country": "string",
"address": "string",
"phone": "string",
"email": "string",
"website": "string",
"contacts": [
{},
],
"specialties": [
"string",
],
"accreditations": [
{},
],
"agreement_status": "string",
"agreement_expires_on": "string",
"languages_spoken": [
"string",
],
"notes": "string",
},
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/travel/facilities`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "string",
"facility_type": "string",
"city": "string",
"country": "string",
"address": "string",
"phone": "string",
"email": "string",
"website": "string",
"contacts": [
{}
],
"specialties": [
"string"
],
"accreditations": [
{}
],
"agreement_status": "string",
"agreement_expires_on": "string",
"languages_spoken": [
"string"
],
"notes": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
One partner hospital
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/travel/facilities/$FACILITY_REF" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
facility_ref = os.environ["FACILITY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/facilities/{facility_ref}",
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 facility_ref = process.env.FACILITY_REF;
const url = new URL(`${BASE}/v1/travel/facilities/${encodeURIComponent(facility_ref)}`);
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
Update a partner hospital (any subset)
Parameters
Request body
Content type: application/json (required)
[{body, status, expires_on}] AS RECORDED by your desk; Norvya verifies none.
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X PATCH "https://norvya.fly.dev/v1/travel/facilities/$FACILITY_REF" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"facility_type": "string",
"city": "string",
"country": "string",
"address": "string",
"phone": "string",
"email": "string",
"website": "string",
"contacts": [
{}
],
"specialties": [
"string"
],
"accreditations": [
{}
],
"agreement_status": "string",
"agreement_expires_on": "string",
"languages_spoken": [
"string"
],
"notes": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
facility_ref = os.environ["FACILITY_REF"]
resp = requests.patch(
f"{BASE}/v1/travel/facilities/{facility_ref}",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"name": "string",
"facility_type": "string",
"city": "string",
"country": "string",
"address": "string",
"phone": "string",
"email": "string",
"website": "string",
"contacts": [
{},
],
"specialties": [
"string",
],
"accreditations": [
{},
],
"agreement_status": "string",
"agreement_expires_on": "string",
"languages_spoken": [
"string",
],
"notes": "string",
},
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 facility_ref = process.env.FACILITY_REF;
const url = new URL(`${BASE}/v1/travel/facilities/${encodeURIComponent(facility_ref)}`);
const res = await fetch(url, {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "string",
"facility_type": "string",
"city": "string",
"country": "string",
"address": "string",
"phone": "string",
"email": "string",
"website": "string",
"contacts": [
{}
],
"specialties": [
"string"
],
"accreditations": [
{}
],
"agreement_status": "string",
"agreement_expires_on": "string",
"languages_spoken": [
"string"
],
"notes": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
A home-nursing case: PRN, LOMN, LOG versions, exception starts, pauses, flags, hours check
nursing_authorization.case_dict. Flags: extension_required, log_expired, lomn_pending, paused, no_log. Documents are described (name, type, size, SHA-256), never returned.
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/travel/journeys/$JOURNEY_REF/nursing" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing`);
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
Move a nursing case forward (the LOG gates nursing_active)
nursing_authorization.set_status: forward only (409 nursing_status_not_forward); paused and resume stay in Norvya (409 nursing_status_use_pause); nursing_active needs a LOG or exception start covering today (409 nursing_log_not_in_force).
Parameters
Request body
Content type: application/json (required)
A nursing status after the current one (paused / resume stay in Norvya). nursing_active needs a LOG (or an exception start) covering today.
Errors specific to this call
409nursing_log_not_in_forceAll 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/travel/journeys/$JOURNEY_REF/nursing/status" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "nursing_active",
"note": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/status",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"status": "nursing_active",
"note": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/status`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"status": "nursing_active",
"note": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Record a new version of the sponsor's Letter of Guarantee (multipart, with the letter)
nursing_authorization.record_log: a NEW version (never edited); the newest version gates scheduling. 409 log_prn_mismatch when the PRN is not the case's; 413 nursing_document_too_large; 422 nursing_document_type.
Parameters
Request body
Content type: multipart/form-data (required)
Must be the case's PRN.
Hours, e.g. 12 or 7.5.
per_day | per_week | total
The letter: PDF, PNG or JPEG, at most 10 MB.
Errors specific to this call
409log_prn_mismatch413nursing_document_too_large422nursing_document_typeAll responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/nursing/logs" \
-H "Authorization: Bearer $TOKEN" \
-F sponsor_reference="$SPONSOR_REFERENCE" \
-F start_date="$START_DATE" \
-F end_date="$END_DATE" \
-F approved_hours="$APPROVED_HOURS" \
-F hours_unit="$HOURS_UNIT" \
-F issued_on="$ISSUED_ON" \
-F document=@your_fileimport os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
with open("your_file", "rb") as fh:
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/logs",
headers={"Authorization": f"Bearer {TOKEN}"},
files={"document": fh},
data={"sponsor_reference": os.environ["SPONSOR_REFERENCE"], "start_date": os.environ["START_DATE"], "end_date": os.environ["END_DATE"], "approved_hours": os.environ["APPROVED_HOURS"], "hours_unit": os.environ["HOURS_UNIT"], "issued_on": os.environ["ISSUED_ON"]},
timeout=30,
)
resp.raise_for_status()
print(resp.json())// Node.js 18+ (ES module). Keep production credentials on your server.
import { readFile } from "node:fs/promises";
const BASE = "https://norvya.fly.dev";
const journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/logs`);
const form = new FormData();
form.append("sponsor_reference", process.env.SPONSOR_REFERENCE);
form.append("start_date", process.env.START_DATE);
form.append("end_date", process.env.END_DATE);
form.append("approved_hours", process.env.APPROVED_HOURS);
form.append("hours_unit", process.env.HOURS_UNIT);
form.append("issued_on", process.env.ISSUED_ON);
form.append("document", new Blob([await readFile("your_file")]), "your_file");
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
},
body: form,
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Planned nursing shifts and the dates the paperwork allows
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/travel/journeys/$JOURNEY_REF/nursing/shifts" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/shifts",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/shifts`);
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
Plan a nursing shift (refused outside the newest LOG's dates or in a pause)
nursing_authorization.create_shift. The server's gate: 409 nursing_no_log (no LOG at all), 409 shift_outside_log (outside the newest LOG's dates and any exception start; a lapsed LOG escalates to the desk at once), 409 nursing_paused, 409 shift_overlap (the same nurse elsewhere), 422 shift_nurse_not_on_team.
Parameters
Request body
Content type: application/json (required)
A nurse or escort nurse on the care team (GET .../team).
Wall clock where the nursing happens.
IANA zone (default: the case's).
Errors specific to this call
409shift_outside_log409nursing_paused409shift_overlap422shift_nurse_not_on_teamAll responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/nursing/shifts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nurse_staff_ref": "string",
"start_local": "2026-10-12T08:00",
"end_local": "2026-10-12T20:00",
"tz": "string",
"note": "string",
"confirmed": false
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/shifts",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"nurse_staff_ref": "string",
"start_local": "2026-10-12T08:00",
"end_local": "2026-10-12T20:00",
"tz": "string",
"note": "string",
"confirmed": False,
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/shifts`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"nurse_staff_ref": "string",
"start_local": "2026-10-12T08:00",
"end_local": "2026-10-12T20:00",
"tz": "string",
"note": "string",
"confirmed": false
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Confirm or cancel a shift (cancel needs a reason)
Parameters
Request body
Content type: application/json (required)
confirmed | cancelled
Required to cancel.
All 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/travel/journeys/$JOURNEY_REF/nursing/shifts/$SHIFT_REF/status" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "cancelled",
"reason": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
shift_ref = os.environ["SHIFT_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/shifts/{shift_ref}/status",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"status": "cancelled",
"reason": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const shift_ref = process.env.SHIFT_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/shifts/${encodeURIComponent(shift_ref)}/status`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"status": "cancelled",
"reason": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Scheduled and delivered minutes against the LOMN and the LOG, by day, week and span
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/travel/journeys/$JOURNEY_REF/nursing/hours-check" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/hours-check",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/hours-check`);
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
Visit records: nurses' notes with vitals, medication administration, incidents (read only)
home_nursing_records.care_record_summary over case-local days (default the last 14). A scheduled dose with nothing recorded is `not_recorded`, never "missed". Written in Norvya by the nurse on the care team; a partner system does not write them. Items carry an item_ref for translation.
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/travel/journeys/$JOURNEY_REF/nursing/visits" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/visits",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/visits`);
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
Patient and family satisfaction survey results for the case
Every response with plain averages (the desk's own quality questions, not a validated instrument). A comment the crisis path flagged is withheld (comment_withheld: crisis_path).
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/travel/journeys/$JOURNEY_REF/nursing/surveys" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/surveys",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/surveys`);
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
A sponsor referral's status: consent basis, documented consent, claim of the account
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/travel/journeys/$JOURNEY_REF/referral" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/referral",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/referral`);
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
Send a sponsor's home-nursing referral for a patient without the app
nursing_referral.create_ipc_referral: a MANAGED patient account (nobody can sign in; nothing is sent to it), linked to your desk, and a nursing case keyed to the PRN, status referral_received. The case is consent `pending` on the documented basis: NOTHING moves on it until desk staff record the patient's signed consent form in Norvya (or the patient claims the account and accepts in the app). 409 prn_taken. In a sandbox the account is labelled synthetic and no contact is stored.
Request body
Content type: application/json (required)
The sponsor's PRN (unique per desk).
US | ES | GB
US state (required for US).
female | male | other | unknown
Held sealed for the patient's later claim of the account; nothing is sent. Ignored in a sandbox.
As email.
Errors specific to this call
409prn_takenAll responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/referrals" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sponsor_reference": "IPC-26-1005",
"full_name": "string",
"date_of_birth": "string",
"care_country": "GB",
"care_state": "FL",
"sex": "string",
"email": "string",
"phone": "string",
"diagnosis_summary": "string",
"language": "string",
"sponsor_name": "string",
"origin_country": "AE",
"received_on": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.post(
BASE + "/v1/travel/referrals",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"sponsor_reference": "IPC-26-1005",
"full_name": "string",
"date_of_birth": "string",
"care_country": "GB",
"care_state": "FL",
"sex": "string",
"email": "string",
"phone": "string",
"diagnosis_summary": "string",
"language": "string",
"sponsor_name": "string",
"origin_country": "AE",
"received_on": "string",
},
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/travel/referrals`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"sponsor_reference": "IPC-26-1005",
"full_name": "string",
"date_of_birth": "string",
"care_country": "GB",
"care_state": "FL",
"sex": "string",
"email": "string",
"phone": "string",
"diagnosis_summary": "string",
"language": "string",
"sponsor_name": "string",
"origin_country": "AE",
"received_on": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
The journey's HL7 International Patient Summary (FHIR document Bundle)
care_journey.ips_from_packet over the coordinator's consent-scoped packet (fhir_export.patient_bundle -> fhir_export.ips_document; never a second builder). Withheld sections are marked, not empty. 403 care_journey_handoff_not_shared; 503 care_journey_bundle_unavailable.
Parameters
Errors specific to this call
503care_journey_bundle_unavailableAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/ips" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/ips",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/ips`);
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
The repatriation packet: the FHIR collection bundle plus the travel context
What the patient carries home, as the desk may see it: consent-scoped per resource type (medications need share_medications, labs share_labs, vitals share_biometrics...); what was withheld is named in consent_excluded.
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/travel/journeys/$JOURNEY_REF/repatriation-packet" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/repatriation-packet",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/repatriation-packet`);
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
The bilingual readable summary for the clinician at home (pdf or accessible html)
Parameters
The second language (default: the journey's primary language; en = English only).
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/summary/$FMT" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
fmt = os.environ["FMT"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/summary/{fmt}",
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 journey_ref = process.env.JOURNEY_REF;
const fmt = process.env.FMT;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/summary/${encodeURIComponent(fmt)}`);
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
The weekly nursing reports for the case (drafts, sent, every version)
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/travel/journeys/$JOURNEY_REF/nursing/reports" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/reports",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/reports`);
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());{
"items": [
{}
],
"count": 0
}The response is the shape from the schema, not live data.
Try it in your sandbox
Download a weekly nursing report (pdf or accessible html, in a chosen language)
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/travel/journeys/$JOURNEY_REF/nursing/reports/$REPORT_REF/$FMT" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
report_ref = os.environ["REPORT_REF"]
fmt = os.environ["FMT"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/reports/{report_ref}/{fmt}",
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 journey_ref = process.env.JOURNEY_REF;
const report_ref = process.env.REPORT_REF;
const fmt = process.env.FMT;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/reports/${encodeURIComponent(report_ref)}/${encodeURIComponent(fmt)}`);
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 desk's monthly home-nursing invoices across cases
One invoice per PRN per calendar month, built from approved timesheets against the LOG (nursing_reports). Newest first, at most 500. Norvya does not send or collect them.
Parameters
draft | issued | void
All responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s "https://norvya.fly.dev/v1/travel/invoices" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/travel/invoices",
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/travel/invoices`);
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());{
"items": [
{}
],
"count": 0
}The response is the shape from the schema, not live data.
Try it in your sandbox
The case's monthly invoices
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/travel/journeys/$JOURNEY_REF/nursing/invoices" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/invoices",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/invoices`);
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());{
"items": [
{}
],
"count": 0
}The response is the shape from the schema, not live data.
Try it in your sandbox
One invoice with its lines (care, overage, exception, outside, pause) and hold flags
Money is integer minor units with an ISO 4217 code (exponent given); hours are minutes. Hours over the LOG are their own held `overage` line.
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/travel/journeys/$JOURNEY_REF/nursing/invoices/$INVOICE_REF" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
invoice_ref = os.environ["INVOICE_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/invoices/{invoice_ref}",
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 journey_ref = process.env.JOURNEY_REF;
const invoice_ref = process.env.INVOICE_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/invoices/${encodeURIComponent(invoice_ref)}`);
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
Download an invoice (pdf or accessible html, in a chosen language)
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/travel/journeys/$JOURNEY_REF/nursing/invoices/$INVOICE_REF/$FMT" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
invoice_ref = os.environ["INVOICE_REF"]
fmt = os.environ["FMT"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/nursing/invoices/{invoice_ref}/{fmt}",
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 journey_ref = process.env.JOURNEY_REF;
const invoice_ref = process.env.INVOICE_REF;
const fmt = process.env.FMT;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/nursing/invoices/${encodeURIComponent(invoice_ref)}/${encodeURIComponent(fmt)}`);
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
Machine-translate one free-text item on the journey
translation_items: the saved translation is served again for the same source text (free, identical); a new one writes a chained AI_INFERENCE audit row. Needs the scope that reads the item as well (travel.nursing.read for nursing items, travel.billing.read for an invoice's reasons). A translation that could not be validated is status `failed` with a reason, not an error; legal_reference_only items (a LOMN) are a reading aid, never the document. 503 translation_disabled.
Parameters
Request body
Content type: application/json (required)
care_journey | shift_note | mar_entry | incident | nursing_lomn | nursing_pause | nursing_shift | nursing_invoice
The item's item_ref (a journey_ref for care_journey).
Default: the item type's first free-text field.
Errors specific to this call
503translation_disabledAll 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/travel/journeys/$JOURNEY_REF/translations" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"item_type": "shift_note",
"item_ref": "string",
"field": "string",
"target_language": "ar"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/translations",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"item_type": "shift_note",
"item_ref": "string",
"field": "string",
"target_language": "ar",
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/translations`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"item_type": "shift_note",
"item_ref": "string",
"field": "string",
"target_language": "ar"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Your desk's travel inquiries (open emergencies first, then newest)
travel_coordination.list_inquiries (the desk screen's list), at most 500; `counts` by status across the desk.
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/travel/inquiries" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.get(
BASE + "/v1/travel/inquiries",
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/travel/inquiries`);
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
Create an inquiry from your CRM (by your own id)
travel_coordination.create_inquiry: status `new`, attributed to your system (never a person). A member_ref links a patient on your roster.
Request body
Content type: application/json (required)
Your CRM's id for the inquiry (unique per desk; 409 inquiry_external_id_taken).
Kept for the desk; Norvya sends nothing to it.
Your coordinator's words; never coded or interpreted.
A patient on your active roster (needed for an in-app quote and to convert).
All responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/inquiries" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"external_id": "CRM-2026-00417",
"contact_name": "string",
"origin_country": "AE",
"contact_detail": "string",
"primary_language": "ar",
"interpreter_required": false,
"condition_summary": "string",
"sponsor_type": "government",
"sponsor_name": "string",
"member_ref": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
resp = requests.post(
BASE + "/v1/travel/inquiries",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"external_id": "CRM-2026-00417",
"contact_name": "string",
"origin_country": "AE",
"contact_detail": "string",
"primary_language": "ar",
"interpreter_required": False,
"condition_summary": "string",
"sponsor_type": "government",
"sponsor_name": "string",
"member_ref": "string",
},
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/travel/inquiries`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"external_id": "CRM-2026-00417",
"contact_name": "string",
"origin_country": "AE",
"contact_detail": "string",
"primary_language": "ar",
"interpreter_required": false,
"condition_summary": "string",
"sponsor_type": "government",
"sponsor_name": "string",
"member_ref": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
One inquiry with its status history and quote versions
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/travel/inquiries/$INQUIRY_REF" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
inquiry_ref = os.environ["INQUIRY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/inquiries/{inquiry_ref}",
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 inquiry_ref = process.env.INQUIRY_REF;
const url = new URL(`${BASE}/v1/travel/inquiries/${encodeURIComponent(inquiry_ref)}`);
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
Edit an open inquiry, or link / unlink the patient
travel_coordination.update_inquiry + link_patient (409 inquiry_closed once declined or converted).
Parameters
Request body
Content type: application/json (required)
Link a patient on your roster; an empty string unlinks. Refused once a quote exists (409 inquiry_already_quoted).
Errors specific to this call
409inquiry_closedAll responses
200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X PATCH "https://norvya.fly.dev/v1/travel/inquiries/$INQUIRY_REF" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"contact_name": "string",
"contact_detail": "string",
"origin_country": "string",
"primary_language": "string",
"interpreter_required": false,
"condition_summary": "string",
"sponsor_type": "string",
"sponsor_name": "string",
"member_ref": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
inquiry_ref = os.environ["INQUIRY_REF"]
resp = requests.patch(
f"{BASE}/v1/travel/inquiries/{inquiry_ref}",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"contact_name": "string",
"contact_detail": "string",
"origin_country": "string",
"primary_language": "string",
"interpreter_required": False,
"condition_summary": "string",
"sponsor_type": "string",
"sponsor_name": "string",
"member_ref": "string",
},
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 inquiry_ref = process.env.INQUIRY_REF;
const url = new URL(`${BASE}/v1/travel/inquiries/${encodeURIComponent(inquiry_ref)}`);
const res = await fetch(url, {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"contact_name": "string",
"contact_detail": "string",
"origin_country": "string",
"primary_language": "string",
"interpreter_required": false,
"condition_summary": "string",
"sponsor_type": "string",
"sponsor_name": "string",
"member_ref": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Move an inquiry (the desk's own moves: reviewing, accepted, declined)
travel_coordination.move: only the moves the desk's screen allows from the current status (409 inquiry_move_not_allowed).
Parameters
Request body
Content type: application/json (required)
A status in next_statuses (quoted comes from issuing a quote, converted from convert).
Kept when declining.
Errors specific to this call
409inquiry_move_not_allowedAll 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/travel/inquiries/$INQUIRY_REF/status" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to_status": "reviewing",
"reason": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
inquiry_ref = os.environ["INQUIRY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/inquiries/{inquiry_ref}/status",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"to_status": "reviewing",
"reason": "string",
},
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 inquiry_ref = process.env.INQUIRY_REF;
const url = new URL(`${BASE}/v1/travel/inquiries/${encodeURIComponent(inquiry_ref)}/status`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"to_status": "reviewing",
"reason": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Issue the first quote or a revision (an immutable Good Faith Estimate version)
travel_coordination.issue_quote -> good_faith_estimate.issue: hashed, kept six years; a revision supersedes, never overwrites. In the app when the inquiry was linked to a patient before the first quote, otherwise recorded as e-mail delivery (your desk sends it; Norvya does not). Norvya never chooses a code or a price and takes no payment.
Parameters
Request body
Content type: application/json (required)
The Good Faith Estimate contents, as the desk's quote form: convening_npi, convening_tin, convening_location, convening_state, diagnosis_codes, lines[{description, service_code, code_type, quantity, expected_charge}], note. 422 required_contents_missing lists what is missing.
Needed for the first quote.
Default: the linked patient's date of birth.
All 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/travel/inquiries/$INQUIRY_REF/quote" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payload": {},
"primary_service": "string",
"recipient_dob": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
inquiry_ref = os.environ["INQUIRY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/inquiries/{inquiry_ref}/quote",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"payload": {},
"primary_service": "string",
"recipient_dob": "string",
},
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 inquiry_ref = process.env.INQUIRY_REF;
const url = new URL(`${BASE}/v1/travel/inquiries/${encodeURIComponent(inquiry_ref)}/quote`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"payload": {},
"primary_service": "string",
"recipient_dob": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
Convert an accepted inquiry to a care journey (waits for the patient's acceptance)
travel_coordination.convert_inquiry — the function the desk's Convert button calls: 409 inquiry_not_accepted / inquiry_patient_not_linked; the journey is coordinator-opened, so the patient is asked in the app (generic push) and every journey write answers 409 care_journey_consent_pending until they accept. Needs travel.journeys.write as well.
Parameters
Request body
Content type: application/json
Errors specific to this call
409inquiry_not_accepted409care_journey_consent_pendingAll 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/travel/inquiries/$INQUIRY_REF/convert" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination_country": "string",
"treating_facility": "string",
"planned_arrival": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
inquiry_ref = os.environ["INQUIRY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/inquiries/{inquiry_ref}/convert",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"destination_country": "string",
"treating_facility": "string",
"planned_arrival": "string",
},
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 inquiry_ref = process.env.INQUIRY_REF;
const url = new URL(`${BASE}/v1/travel/inquiries/${encodeURIComponent(inquiry_ref)}/convert`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"destination_country": "string",
"treating_facility": "string",
"planned_arrival": "string"
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());Try it in your sandbox
The journey's in-app thread (desk, patient, approved caregiver)
The thread the desk's staff read in Norvya (travel_coordination.thread_rows), oldest first, the last 200. The patient and caregiver are sender_role only, never named; `mine` marks your own client's messages. A message sent with "write in mine, send in theirs" carries the translation the sender previewed.
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/travel/journeys/$JOURNEY_REF/messages" \
-H "Authorization: Bearer $TOKEN"import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.get(
f"{BASE}/v1/travel/journeys/{journey_ref}/messages",
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/messages`);
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
Post in the journey's thread as your desk's system
travel_coordination.post: shown in Norvya as "Partner system: <your client's label>", never as a person. The patient, the linked caregiver and desk staff who wrote on the thread get Norvya's generic "new message" push (no content on the lock screen). 409 care_journey_consent_pending until the patient accepts; 409 journey_closed once closed.
Parameters
Request body
Content type: application/json (required)
Plain text, sent as typed (no machine translation through the API).
Errors specific to this call
409care_journey_consent_pending409journey_closedAll responses
201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service UnavailableCodes every call can return are in Errors & limits.
curl -s -X POST "https://norvya.fly.dev/v1/travel/journeys/$JOURNEY_REF/messages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"body": "string"
}'import os
import requests
BASE = "https://norvya.fly.dev"
TOKEN = os.environ["TOKEN"]
journey_ref = os.environ["JOURNEY_REF"]
resp = requests.post(
f"{BASE}/v1/travel/journeys/{journey_ref}/messages",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"body": "string",
},
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 journey_ref = process.env.JOURNEY_REF;
const url = new URL(`${BASE}/v1/travel/journeys/${encodeURIComponent(journey_ref)}/messages`);
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"body": "string"
}),
});
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 |
|---|---|---|
travel.inquiry_updated | travel.inquiries.read | An inquiry of the desk was created (in Norvya, through the API or from a hospital's request link) or changed status, including a new quote version and conversion. |
travel.invoice_issued | travel.billing.read | A monthly nursing invoice was issued. |
travel.journey_message_posted | travel.messages.read | Someone wrote in a journey's thread: the patient, their caregiver, desk staff in Norvya or another of your clients. |
travel.journey_updated | travel.journeys.read | A journey of the desk was opened, accepted or declined, moved a phase, or its nursing status changed. |
travel.leg_status_changed | travel.journeys.read | A flight or transfer was added, changed status or was cancelled. |
travel.nursing_visit_recorded | travel.nursing.read | A nurse recorded a visit note. |
Generated from the Partner API router and developers_catalog.py. The router docstring is the source of each description.