Norvya Developers
Get a sandbox
API reference · Employers & fitness

Employers & occupational health

Built — gated21 endpoints · 2 webhook events · OpenAPI tag employer

For employers and the practices that examine their workers: the employer's GLP-1 programme (settings, enrolment code, an outcomes report with small cells suppressed and no person in it), wellness challenges and their suppressed results, the occupational-health worker roster, exam programmes, exams and next-due dates on a fixed released-field view, work injuries reported by the examining practice, and the organisation's own staff wellbeing aggregate. Webhooks: employer.challenge_completed, occupational.exam_due.

Who calls it
Core for Employers & occupational health
Before production
Employer organisations hold the employer and occupational scopes; the examining practice's own client holds occupational.injuries.write.
Sandbox
Build against synthetic members first. What each sandbox holds

Markets that use this family

Scopes

ScopeAllows
employer.challenges.readRead your sponsor recognition challenges, the allow-listed catalog and each challenge's suppressed group counts (joined, completed, completions per week) (employers only).
employer.challenges.writeCreate or update a recognition challenge for your programme's members: a participation or activity item from the allow-listed catalog, a 7 to 92 day window, a badge only (no cash, no health-outcome targets) (employers only).
employer.programs.readRead your GLP-1 programme set-up (enrolment code, the enrolled count) and its aggregate report: persistence, weight and HbA1c change, engagement, your own drug-cost arithmetic and recognition counts, every number suppressed below the minimum group size; never a person (employers only).
employer.programs.writeCreate or update your GLP-1 programme (name, start date, your drug cost per member month, active) and regenerate its enrolment code; employees join only in the app, with their own consent (employers only).
occupational.injuries.writeRecord a work injury report for an employer you examine for under an active service agreement; the employer sees only the classification inputs, never the description (treating practices only).
occupational.readRead your occupational-health roster, the exam programmes, each worker's released exam conclusions (determination, restrictions, follow-up, due dates; never findings or the worker's answers), next-due dates and injury reports for your own log (employers only).
occupational.writeAdd workers to your occupational-health roster and request an exam from a practice you hold a service agreement with (employers only).
staff_wellbeing.readRead your own staff's combined burnout check-in results: participation, bands and scores, only once enough people answered; never who answered or who did not (employers and treating practices).

Endpoints

Your GLP-1 programme set-up

GET/v1/employer/glp1-programemployer.programs.read

The programme's name, enrolment code (employees join in the app with it, after reading exactly what you will and will not see), start date, your drug-cost figure and the enrolled count, suppressed below the minimum group size. null when no programme is set up.

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/employer/glp1-program" \
  -H "Authorization: Bearer $TOKEN"

Create or update your GLP-1 programme

PUT/v1/employer/glp1-programemployer.programs.write

Request body

Content type: application/json (required)

namestring | null
started_onstring | null

YYYY-MM-DD

drug_cost_per_member_monthnumber | null

Your own figure; Norvya supplies no drug prices.

is_activeboolean | null

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X PUT "https://norvya.fly.dev/v1/employer/glp1-program" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string",
    "started_on": "string",
    "drug_cost_per_member_month": 0.0,
    "is_active": false
  }'

Regenerate your programme's enrolment code

POST/v1/employer/glp1-program/enrollment-codeemployer.programs.write

The old code stops working at once; members already enrolled stay enrolled.

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/employer/glp1-program/enrollment-code" \
  -H "Authorization: Bearer $TOKEN"

Your GLP-1 programme's aggregate report (never a person)

GET/v1/employer/glp1-program/reportemployer.programs.read

employer_glp1_report.build_report: enrolment composition, persistence at 12 / 24 / 52 weeks, weight and HbA1c change (mean, median, quartiles; no minimum or maximum), engagement, the value block (YOUR drug cost x the measured programme months — arithmetic on your input, never a savings or ROI claim) and recognition-challenge counts. Every cell is suppressed below min_cell, with complementary suppression; status too_few_enrolled below it. No names, ids, medications, side effects or demographic breakdowns.

Parameters

formatquery · string

json | csv

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/employer/glp1-program/report" \
  -H "Authorization: Bearer $TOKEN"

Your recognition challenges and their group counts

GET/v1/employer/challengesemployer.challenges.read

The allow-listed catalog (participation and activity items only) and, per challenge, joined / completed / completions per ISO week among members with an active programme enrolment — each suppressed below min_cell. No leaderboard, no names, nothing finer than a week.

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/employer/challenges" \
  -H "Authorization: Bearer $TOKEN"

Create a recognition challenge

POST/v1/employer/challengesemployer.challenges.write

sponsor_challenges.create_challenge: an allow-listed catalog item, a 7 to 92 day window not in the past, recognition 'badge' only, at most 10 open per programme. No push is sent to members (they find it in the app).

Request body

Content type: application/json (required)

catalog_keystringrequired

One of GET /v1/employer/challenges catalog[].catalog_key.

start_onstringrequired

YYYY-MM-DD

end_onstringrequired

YYYY-MM-DD; the window is 7 to 92 days.

recognitionstring

badge (the only recognition: never cash or prizes)

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/employer/challenges" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "catalog_key": "string",
    "start_on": "string",
    "end_on": "string",
    "recognition": "string"
  }'

One challenge's group counts

GET/v1/employer/challenges/{challenge_ref}/resultsemployer.challenges.read

Parameters

challenge_refpath · stringrequired

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/employer/challenges/$CHALLENGE_REF/results" \
  -H "Authorization: Bearer $TOKEN"

Change a challenge's window or switch it off

PATCH/v1/employer/challenges/{challenge_ref}employer.challenges.write

Parameters

challenge_refpath · stringrequired

Request body

Content type: application/json (required)

start_onstring | null
end_onstring | null
is_activeboolean | null

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s -X PATCH "https://norvya.fly.dev/v1/employer/challenges/$CHALLENGE_REF" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "start_on": "string",
    "end_on": "string",
    "is_active": false
  }'

Occupational exam programmes

GET/v1/occupational/programsoccupational.read

occupational_health.vocabulary: each programme with the CFR citation it rests on and whether its interval was read from the regulation (interval_asserted false = no due date is invented), hazards, determinations, statuses, injury outcomes and the keys an employer can ever be shown.

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/occupational/programs" \
  -H "Authorization: Bearer $TOKEN"

Practices you hold an active occupational-health service agreement with

GET/v1/occupational/providersoccupational.read

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/occupational/providers" \
  -H "Authorization: Bearer $TOKEN"

Your occupational-health roster

GET/v1/occupational/workersoccupational.read

Your own roster rows (employee number, name, job, department, hazards). has_account says whether the worker claimed the row in the app (which lets them read their full record and the disclosure log); nothing else about them is shown.

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/occupational/workers" \
  -H "Authorization: Bearer $TOKEN"

Add a worker to your roster

POST/v1/occupational/workersoccupational.write

Request body

Content type: application/json (required)

employee_refstringrequired

Your own employee number.

full_namestring | null
job_titlestring | null
departmentstring | null
hazardsarray of string | null

GET /v1/occupational/programs hazards[]

first_exposure_onstring | null

YYYY-MM-DD (hearing conservation baseline)

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/occupational/workers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "employee_ref": "string",
    "full_name": "string",
    "job_title": "string",
    "department": "string",
    "hazards": [
      "string"
    ],
    "first_exposure_on": "string"
  }'

Exam determinations and due dates (released conclusions only)

GET/v1/occupational/rosteroccupational.read

occupational_health.roster: one row per exam of an active worker, built from the employer allow-list (EMPLOYER_VISIBLE_KEYS). A conclusion the examining clinician has not released reads determination 'pending' with no restrictions (released false).

Parameters

statusquery · string

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/occupational/roster" \
  -H "Authorization: Bearer $TOKEN"

What has been released to you about one worker

GET/v1/occupational/workers/{worker_ref}/examsoccupational.read

Every exam of the worker as the employer may hold it: programme, status, determination, restrictions, valid until, follow-up, due dates, released on. Never the worker's declaration, the findings or the notes (29 CFR 1910.134(e)(6); 1630.14(b)(1)).

Parameters

worker_refpath · stringrequired

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/occupational/workers/$WORKER_REF/exams" \
  -H "Authorization: Bearer $TOKEN"

When a worker's next exam in a programme is due

GET/v1/occupational/workers/{worker_ref}/next-dueoccupational.read

occupational_health.next_due: only hearing conservation has an interval read from the regulation (annual after a baseline within six months of first exposure); every other programme answers asserted false with no date, rather than a guessed one.

Parameters

worker_refpath · stringrequired
programquery · stringrequired

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/occupational/workers/$WORKER_REF/next-due?program=$PROGRAM" \
  -H "Authorization: Bearer $TOKEN"

One exam, as released to you

GET/v1/occupational/exams/{exam_ref}occupational.read

Parameters

exam_refpath · stringrequired

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/occupational/exams/$EXAM_REF" \
  -H "Authorization: Bearer $TOKEN"

Request an exam from a practice you hold an agreement with

POST/v1/occupational/examsoccupational.write

occupational_health.request_exam: the programme must be known, the agreement active, and a respirator request carries the supplemental information 1910.134(e)(5) requires (else 422 supplemental_information_required, never sent incomplete).

Request body

Content type: application/json (required)

worker_refstringrequired
provider_refstringrequired

GET /v1/occupational/providers

programstringrequired

GET /v1/occupational/programs programs[].key

due_onstring | null
reasonstring | null
supplementalobject | null

Respirator: the 1910.134(e)(5) information the examiner needs (required keys per program).

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/occupational/exams" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "worker_ref": "string",
    "provider_ref": "string",
    "program": "string",
    "due_on": "string",
    "reason": "string",
    "supplemental": {}
  }'

Injury reports for your own log, with return-to-work status

GET/v1/occupational/injuriesoccupational.read

occupational_health.employer_injury_view: occurred on, work related, outcome, restricted work, recorded on — input for YOUR OSHA 300 log, not the log (recordability is yours to decide under 29 CFR 1904). Never the clinical description. return_to_work is the latest RELEASED return_to_work or fitness_for_duty exam conclusion performed on or after the injury (determination, restrictions, valid until), else null; Norvya records no return-to-work date.

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/occupational/injuries" \
  -H "Authorization: Bearer $TOKEN"

Employers you examine for under an active agreement

GET/v1/occupational/provider/employersoccupational.injuries.write

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/occupational/provider/employers" \
  -H "Authorization: Bearer $TOKEN"

Record a work injury report for an employer you examine for

POST/v1/occupational/provider/injuriesoccupational.injuries.write

occupational_health.record_injury under an ACTIVE service agreement. The description is clinical and stays with you; the employer is shown only occurred on, work related, outcome and restricted work.

Request body

Content type: application/json (required)

employer_refstringrequired

GET /v1/occupational/provider/employers

employee_refstringrequired

The employer's own employee number for the worker.

occurred_onstringrequired
descriptionstring | null

Clinical; never shown to the employer.

work_relatedboolean | null

null = not known

outcomestring

treatment_only | restricted_work | days_away | referred | unknown

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/occupational/provider/injuries" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "employer_ref": "string",
    "employee_ref": "string",
    "occurred_on": "string",
    "description": "string",
    "work_related": false,
    "outcome": "string"
  }'

Your staff's combined burnout check-in results

GET/v1/staff-wellbeingstaff_wellbeing.read

staff_wellbeing.clinic_aggregate (Copenhagen Burnout Inventory, work scale): eligible staff, how many answered, the band distribution and mean / median, only once at least min_cell people answered in the 30-day window (below it even the response count is withheld). Never a name, a score or who has not answered; a crisis answer never reaches the organisation.

Parameters

periodsquery · integer

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/staff-wellbeing" \
  -H "Authorization: Bearer $TOKEN"

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

EventScopeWhen
employer.challenge_completedemployer.challenges.readA wellness challenge's window ended (once per challenge; the results are the suppressed group counts).
occupational.exam_dueoccupational.readAn exam of an active worker, not completed or cancelled, is due soon or past due (once per exam and due date).

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

↑ ↓ to moveEnter to open