Norvya Developers
Get a sandbox
API reference · Foundation

Events (webhooks)

Built — gated5 endpoints · OpenAPI tag events

Signed notifications: a member newly at high risk of leaving (gyms), a worklist run finishing, a patient newly needing attention, an import completing, a population export ready, a registry acknowledgement or a Reportability Response arriving, a patient's care-management time reaching a program's first published mark, an appointment booked or cancelled in Norvya (practices), a member joining, a member's message, a questionnaire completed, a worklist item triaged, a claims import completed, a social-needs screen completed or a community referral updated, an imported patient claiming their account, a caseload questionnaire batch dispatched, a hospital-at-home escalation, a pharmacy refill due or discharge follow-up opened, a care-site change of condition, medication incident or MAR exception, and a medical-travel inquiry, journey, journey message, flight leg, nursing visit or invoice update; and, from the specialty and programme families, a roster's enrolment numbers changing, a raised falls tier, a challenge ending, an occupational exam coming due, a med-spa adverse event, a rehab goal met, a dietitian caseload flag, a Collaborative Care review due, a lesion recheck due, a patch-test read and a hospice IDG review due. Payloads carry no health data, only a reference to fetch with your token.

Who calls it
Every partner: common to all markets.
Before production
No gate of its own; your market's “Before production” applies.
Sandbox
Build against synthetic members first. What each sandbox holds

Markets that use this family

Scopes

ScopeAllows
webhooks.manageRegister HTTPS webhook endpoints (payloads carry no PHI). Each event also needs its own scope: worklist / attention events clinical.panel.read, import events panel.import.write, population.export_completed population.export (medical practices only), member.at_risk fitness.roster.read, public_health.iis_ack_received public_health.immunizations.read, public_health.case_report_response public_health.case_reports.read. claims.import_completed needs claims.import.write (medical practices only). member.account_claimed needs members.claim_invite (medical practices only). hospital_at_home.escalation needs hospital_at_home.read (hospitals only). hospice.idg_review_due needs hospice.read (hospice agencies only). pharmacy.refill_due and pharmacy.discharge_episode_opened need pharmacy.patients.read; care_site.change_of_condition, care_site.incident_recorded and care_site.mar_exception need care_sites.read (pharmacies and care-facility operators, their own sites). travel.journey_updated and travel.leg_status_changed need travel.journeys.read, travel.nursing_visit_recorded travel.nursing.read, travel.invoice_issued travel.billing.read (medical-travel desks only). travel.inquiry_updated needs travel.inquiries.read, travel.journey_message_posted travel.messages.read (medical-travel desks only). instrument.batch_dispatched needs instruments.request (medical practices only). roster.enrolment_summary needs members.roster.read (medical practices only). older_adults.falls_tier_raised needs older_adults.read (medical practices only). employer.challenge_completed needs employer.challenges.read and occupational.exam_due occupational.read (employers only); medspa.adverse_event_reported needs medspa.aftercare.read (treating practices only). rehab.goal_met needs rehab.read; nutrition.caseload_flagged needs nutrition.read (medical practices only). cocm.review_due needs behavioral.read (medical practices only). derm.lesion_recheck_due and derm.patch_test_read_recorded need derm.read (medical practices only).

Endpoints

Your webhook endpoints

GET/v1/webhookswebhooks.manage

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/webhooks" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "webhooks": [
    {
      "webhook_id": "string",
      "url": "string",
      "events": [
        "string"
      ],
      "status": "string",
      "disabled_reason": "string",
      "consecutive_failures": 0,
      "created_at": "string",
      "last_success_at": "string",
      "last_failure_at": "string",
      "disabled_at": "string"
    }
  ]
}

The response is the shape from the schema, not live data.

Register a webhook endpoint (secret shown once)

POST/v1/webhookswebhooks.manage

HTTPS only; the host must resolve to public addresses only (loopback, private, link-local / metadata, CGNAT and ULA ranges are refused, at registration and again at every send; redirects are never followed). Your client must also hold the scope behind each event (worklist.* / patient.attention_needed: clinical.panel.read; import.*: panel.import.write; population.export_completed: population.export — these are practice events, for medical practices only; patient.admitted / patient.discharged / patient.ed_visit: transitions.notices.read, treating practices and hospitals; transition.episode_enrolled / transition.red_flag: transitions.read, hospitals only; member.at_risk: fitness.roster.read, any organisation, so a gym can subscribe to it) — 422 webhook_event_not_allowed otherwise.

patient.admitted / .discharged / .ed_visit fire when an ADT notice is matched to one of your patients and visible to you (after a discharge, TCM asks for contact within 2 business days: GET the resource_url for the clock). transition.episode_enrolled fires when a post-discharge episode opens at your hospital; transition.red_flag when a patient's check-in trips a new red flag. All five carry member_ref.

population.export_completed fires when an export YOUR client started finishes; it is sent to your client's endpoints only.

member.at_risk fires once when a member newly enters churn level "high" (daily scan); not again until the member leaves "high" and re-enters.

Payloads carry no PHI: {event, id, created_at, resource_url, member_ref (attention, member.at_risk, patient.* and transition.* only)} — GET the resource_url with your token. Verify every delivery: `Norvya-Signature: t=<unix>, v1=<hex>` where v1 = HMAC-SHA256(secret, "<t>." + raw body); compare in constant time, reject a t more than 300 s from your clock (replay window) and de-duplicate on the Norvya-Delivery header (delivery is at-least-once). Failed deliveries retry with exponential backoff (1, 2, 4 ... min; 8 attempts); 20 consecutive failures disable the endpoint (POST /v1/webhooks/{id}/enable).

Request body

Content type: application/json (required)

urlstringrequired

https:// only; must resolve to public addresses.

eventsarray of stringrequired

Subset of: worklist.run_completed, patient.attention_needed, import.completed, import.failed, population.export_completed (medical practices), patient.admitted, patient.discharged, patient.ed_visit (treating practices and hospitals holding transitions.notices.read), transition.episode_enrolled, transition.red_flag (hospitals holding transitions.read), member.at_risk (any organisation holding fitness.roster.read)

Errors specific to this call

422webhook_event_not_allowed

All responses

201Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X POST "https://norvya.fly.dev/v1/webhooks" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "string",
    "events": [
      "string"
    ]
  }'
Response 201
{
  "webhook_id": "string",
  "url": "string",
  "events": [
    "string"
  ],
  "status": "string",
  "disabled_reason": "string",
  "consecutive_failures": 0,
  "created_at": "string",
  "last_success_at": "string",
  "last_failure_at": "string",
  "disabled_at": "string",
  "secret": "string",
  "signature_header": "string"
}

The response is the shape from the schema, not live data.

Delete a webhook endpoint

DELETE/v1/webhooks/{webhook_id}webhooks.manage

Pending deliveries are cancelled.

Parameters

webhook_idpath · stringrequired

All responses

204Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X DELETE "https://norvya.fly.dev/v1/webhooks/$WEBHOOK_ID" \
  -H "Authorization: Bearer $TOKEN"
Response 204
No response body.

The response is the shape from the schema, not live data.

Re-enable a disabled webhook endpoint

POST/v1/webhooks/{webhook_id}/enablewebhooks.manage

The URL is re-checked; the failure count resets. Deliveries cancelled while it was disabled are not re-sent.

Parameters

webhook_idpath · stringrequired

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X POST "https://norvya.fly.dev/v1/webhooks/$WEBHOOK_ID/enable" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "webhook_id": "string",
  "url": "string",
  "events": [
    "string"
  ],
  "status": "string",
  "disabled_reason": "string",
  "consecutive_failures": 0,
  "created_at": "string",
  "last_success_at": "string",
  "last_failure_at": "string",
  "disabled_at": "string"
}

The response is the shape from the schema, not live data.

Recent deliveries of one webhook endpoint

GET/v1/webhooks/{webhook_id}/deliverieswebhooks.manage

Newest first: status, attempts, next retry, last HTTP status / error class, and the (PHI-free) payload. Response bodies are never stored.

Parameters

webhook_idpath · stringrequired
limitquery · integer
statusquery · string | null

pending | delivered | failed | cancelled

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/webhooks/$WEBHOOK_ID/deliveries" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "webhook_id": "string",
  "deliveries": [
    {
      "delivery_id": "string",
      "event": "string",
      "event_id": "string",
      "status": "string",
      "attempts": 0,
      "next_attempt_at": "string",
      "last_attempt_at": "string",
      "last_status_code": 0,
      "last_error": "string",
      "delivered_at": "string",
      "created_at": "string",
      "payload": {}
    }
  ]
}

The response is the shape from the schema, not live data.

Event catalogue

Every event Norvya sends, with the scope your client must also hold and the family whose resource it points to.

EventScopeFamilyWhen
appointment.bookedscheduling.readBilling & schedulingAn appointment was booked in Norvya (your own writes are never echoed).
appointment.cancelledscheduling.readBilling & schedulingAn appointment was cancelled in Norvya (your own writes are never echoed).
billing.threshold_metbilling.readBilling & schedulingA patient's documented care-management time (or remote-monitoring days) for one program reached the program's first published reference mark this month.
care_site.change_of_conditioncare_sites.readCare sitesA change-of-condition report at one of your sites (a pharmacy's or a care facility's own).
care_site.incident_recordedcare_sites.readCare sitesAn incident report or a medication incident at one of your sites.
care_site.mar_exceptioncare_sites.readCare sitesA scheduled dose recorded held or refused at one of your sites.
claims.import_completedclaims.import.writeClaims inA claims import you sent finished committing (sent to that client only).
cocm.review_duebehavioral.readBehavioral healthThe practice's weekly Collaborative Care caseload review is due (about the practice, not a patient: no member_ref).
derm.lesion_recheck_duederm.readDermatologyA tracked lesion of a member who shares skin scans with the practice reached its recheck date (once per lesion per due date).
derm.patch_test_read_recordedderm.readDermatologyA read was recorded on a patch test of the practice (once per read set; never an allergen or a grade).
employer.challenge_completedemployer.challenges.readEmployers & occupational healthA wellness challenge's window ended (once per challenge; the results are the suppressed group counts).
hospice.idg_review_duehospice.readHospice agenciesAt least one patient on service at the agency has an IDG plan-of-care review due within 3 days or overdue (about the agency, not a patient: no member_ref; once per UTC day).
hospital_at_home.escalationhospital_at_home.readHospital at home, days at home & woundsAn observation round on a hospital-at-home episode scored at or above NEWS2's urgent-response threshold.
import.completedpanel.import.writeData inA panel import made through the API committed.
import.failedpanel.import.writeData inA panel import made through the API failed.
instrument.batch_dispatchedinstruments.requestClinical intelligenceA caseload questionnaire batch you started finished sending its requests.
instrument.completedinstruments.readClinical intelligenceA patient completed a questionnaire this practice requested (after the crisis gate has run).
medspa.adverse_event_reportedmedspa.aftercare.readMed spasAn aesthetic adverse event was recorded at the practice (by staff, through the Partner API, or as a draft from a patient's warning-sign check-in).
member.account_claimedmembers.claim_invitePractice operations & reportsA patient the practice imported claimed their own account with the practice's invitation code.
member.at_riskfitness.roster.readFitness intelligenceA member newly entered churn level high in the daily scan.
member.joinedmembers.readEnrolment & messagingA person linked to your organisation from the app's join screen.
message.receivedmessages.readEnrolment & messagingA member wrote to your organisation in its in-app conversation.
nutrition.caseload_flaggednutrition.readNutrition (dietitians)A member of the dietitian caseload carries a caseload flag: no food logged for 7 days, or 14-day calories outside 10 % of the target (Norvya conventions, not clinical guidelines).
occupational.exam_dueoccupational.readEmployers & occupational healthAn exam of an active worker, not completed or cancelled, is due soon or past due (once per exam and due date).
older_adults.falls_tier_raisedolder_adults.readOlder adultsA patient's newest CDC STEADI self-check set a higher falls tier than their previous one, and the new tier is moderate or high.
patient.admittedtransitions.notices.readCare transitionsAn admission matched to one of the practice's patients and visible to it.
patient.attention_neededclinical.panel.readClinical intelligenceA patient newly entered the top priority band in a completed current-data run.
patient.dischargedtransitions.notices.readCare transitionsA discharge matched to one of the practice's patients and visible to it.
patient.ed_visittransitions.notices.readCare transitionsAn ED visit matched to one of the practice's patients and visible to it.
pharmacy.discharge_episode_openedpharmacy.patients.readPharmacyA discharge follow-up episode opened.
pharmacy.refill_duepharmacy.patients.readPharmacyA pack cycle of the pharmacy became due soon or overdue.
population.export_completedpopulation.exportPopulationA FHIR Bulk export you started finished (sent to that client only).
public_health.case_report_responsepublic_health.case_reports.readPublic-health reportingA Reportability Response was filed for one of the practice's eICRs.
public_health.iis_ack_receivedpublic_health.immunizations.readPublic-health reportingA state immunization registry acknowledged a dose report of the practice.
rehab.goal_metrehab.readRehab (PT / OT / speech)A functional goal the practice set was marked met (once per goal; only while the patient shares their medical history).
roster.enrolment_summarymembers.roster.readEnrolment rosterA roster you uploaded and committed has new enrolment numbers (at most once a UTC day per roster, only when its counts changed).
social_needs.referral_updatedsocial_needs.readSocial needs & programmesA closed-loop community referral of the practice was created or changed status.
social_needs.screening_completedsocial_needs.readSocial needs & programmesA patient submitted an AHC-HRSN screen.
transition.episode_enrolledtransitions.readCare transitionsA post-discharge episode opened at the hospital (staff desk, Partner API or an ADT A03).
transition.red_flagtransitions.readCare transitionsA patient's check-in tripped a new red flag.
travel.inquiry_updatedtravel.inquiries.readMedical travelAn 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_issuedtravel.billing.readMedical travelA monthly nursing invoice was issued.
travel.journey_message_postedtravel.messages.readMedical travelSomeone wrote in a journey's thread: the patient, their caregiver, desk staff in Norvya or another of your clients.
travel.journey_updatedtravel.journeys.readMedical travelA journey of the desk was opened, accepted or declined, moved a phase, or its nursing status changed.
travel.leg_status_changedtravel.journeys.readMedical travelA flight or transfer was added, changed status or was cancelled.
travel.nursing_visit_recordedtravel.nursing.readMedical travelA nurse recorded a visit note.
worklist.item_triagedclinical.panel.readClinical intelligenceA worklist finding was triaged by staff in the app or by another of the practice's clients (never echoed to the client that made the change).
worklist.run_completedclinical.panel.readClinical intelligenceA worklist run of the practice completed.

Generated from the Partner API router and developers_catalog.py. The router docstring is the source of each description.

↑ ↓ to moveEnter to open