Norvya Developers
Get a sandbox
API reference · Specialty practices

Med spas

Built — gated12 endpoints · 1 webhook event · OpenAPI tag medspa

For medical spas: pre-treatment clearances, treatments recorded against the member, adverse events reported and followed, re-treatment due dates and the aftercare photos the member chose to share. Webhook: medspa.adverse_event_reported. The medical director's GLP-1 oversight roster: patients on a GLP-1 who share their medication data, each finding quoting the FDA label with its citation (pending clinician sign-off; never a dose suggestion).

Who calls it
Core for Med spas
Before production
Medical-spa organisations (a treating practice) with a BAA on file.
Sandbox
Build against synthetic members first. What each sandbox holds

Markets that use this family

Scopes

ScopeAllows
medspa.aftercare.readRead aesthetic treatments with their aftercare check-in schedule and alerts, adverse events, and the retreatment-due list on your practice's own intervals (treating practices, BAA on file).
medspa.aftercare.writeRecord an aesthetic treatment (its aftercare check-ins are scheduled) and an adverse event for a patient of your practice (treating practices, BAA on file).
medspa.clearance.readRead your practice's treatment-clearance intakes: the screen's flags with their reason and source, and the supervising provider's decision and expiry (treating practices that offer clearance, BAA on file).
medspa.glp1_oversight.readRead your medical director's GLP-1 oversight roster: patients on a GLP-1 / GIP medicine whose medication data they share with you, the therapy as recorded, last injection logged, weight trend, latest labs, diabetes monitoring status and each finding with its verbatim FDA label citation (pending clinician sign-off); never a dose suggestion or a prescribing recommendation (treating practices, BAA on file).
medspa.photos.readRead a patient's before / after photo timeline as metadata (view, phase, area, date, consent flags); image files are not served through the API (treating practices).

Endpoints

Treatment-clearance intakes

GET/v1/medspa/clearancesmedspa.clearance.read

med_spa_clearance: each intake's category, status (awaiting_review | reviewed | expired), the screen's contraindication / precaution counts and the supervising provider's decision with its expiry (a practice setting). The screen is never a clearance: only a provider's decision makes one.

Parameters

statusquery · string
member_refquery · string | null

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

One clearance intake: flags, reasons, sources and the decision

GET/v1/medspa/clearances/{clearance_ref}medspa.clearance.read

Every flag the screen raised (item, severity, reason, the primary source it rests on with its verbatim quote, and any disagreeing source), the decision, its note and the deciding clinician as a staff_ref. The member's raw answers and medication details stay in the app.

Parameters

clearance_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/medspa/clearances/$CLEARANCE_REF" \
  -H "Authorization: Bearer $TOKEN"

A patient's aesthetic treatments and aftercare plans

GET/v1/medspa/members/{member_ref}/treatmentsmedspa.aftercare.read

Each treatment (category, areas, date, product, quantity, lot, treating clinician as staff_ref, consent on file) with its aftercare plan: the scheduled check-ins (the practice's own days for the category, else Norvya's default 1 / 3 / 14 — a convention, not a clinical standard), the warning signs the patient reported and any alert (never closed by time).

Parameters

member_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/medspa/members/$MEMBER_REF/treatments" \
  -H "Authorization: Bearer $TOKEN"

Record an aesthetic treatment (schedules its aftercare check-ins)

POST/v1/medspa/members/{member_ref}/treatmentsmedspa.aftercare.write

med_spa_aftercare.create_treatment: its validation (category, areas, date within the last 365 days and not in the future, units), then the check-ins are scheduled and the patient is reminded by Norvya's generic push when one is due.

Parameters

member_refpath · stringrequired

Request body

Content type: application/json (required)

categorystringrequired

neurotoxin | dermal_filler | laser_energy | chemical_peel | microneedling | iv_therapy | other

areasarray of string | null
treated_onstring | null

YYYY-MM-DD, within the last 365 days (default today).

product_namestring | null
quantitynumber | null
quantity_unitstring | null

units | ml | syringes | vials | passes | sessions | other

lot_numberstring | null
providerstring | null

staff_ref of the treating clinician (GET /v1/panel/clinicians)

notesstring | null

Errors specific to this call

365days

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/medspa/members/$MEMBER_REF/treatments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "string",
    "areas": [
      "string"
    ],
    "treated_on": "string",
    "product_name": "string",
    "quantity": 0.0,
    "quantity_unit": "string",
    "lot_number": "string",
    "provider": "string",
    "notes": "string"
  }'

One treatment and its aftercare plan

GET/v1/medspa/treatments/{treatment_ref}medspa.aftercare.read

Parameters

treatment_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/medspa/treatments/$TREATMENT_REF" \
  -H "Authorization: Bearer $TOKEN"

Adverse events after aesthetic treatments

GET/v1/medspa/adverse-eventsmedspa.aftercare.read

Confirmed events (recorded by staff or through the API), drafts a patient's warning-sign check-in created (a clinician confirms or dismisses them in the app) and dismissed drafts with the reason. from / to filter on the date the event was recorded (default the last 90 days).

Parameters

fromquery · string | null
toquery · string | null
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/medspa/adverse-events" \
  -H "Authorization: Bearer $TOKEN"

One adverse event

GET/v1/medspa/adverse-events/{event_ref}medspa.aftercare.read

Parameters

event_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/medspa/adverse-events/$EVENT_REF" \
  -H "Authorization: Bearer $TOKEN"

Report an adverse event after an aesthetic treatment

POST/v1/medspa/members/{member_ref}/adverse-eventsmedspa.aftercare.write

med_spa_aftercare.create_event: recorded as confirmed (the practice reporting it), its vocabulary validated by the engine, optionally tied to one of the patient's treatments here. Fires medspa.adverse_event_reported to your other subscribed endpoints.

Parameters

member_refpath · stringrequired

Request body

Content type: application/json (required)

event_typestringrequired

vascular_occlusion | visual_disturbance | distant_toxin_spread | eyelid_or_brow_droop | allergic_reaction | infection | burn_or_blister | nodule_or_lump | pigment_change | scarring | bruising_or_swelling | cold_sore_outbreak | other

severitystringrequired

mild | moderate | severe

descriptionstring | null
onset_onstring | null
actions_takenstring | null
outcomestring | null

resolved | resolving | not_resolved | resolved_with_sequelae | unknown

follow_up_onstring | null
treatment_refstring | null

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/medspa/members/$MEMBER_REF/adverse-events" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "string",
    "severity": "string",
    "description": "string",
    "onset_on": "string",
    "actions_taken": "string",
    "outcome": "string",
    "follow_up_on": "string",
    "treatment_ref": "string"
  }'

Patients whose retreatment may be due, on your practice's own intervals

GET/v1/medspa/retreatment-duemedspa.aftercare.read

med_spa_retreatment.compute_items: the latest active treatment per patient, category and area, due on the interval YOUR practice saved (a practice setting, not clinical guidance; a category without one is never due). reminders_opted_out = the patient asked this practice for no retreatment reminders.

Parameters

statusquery · string | null

comma list of not_due | due_soon | due | overdue (default due_soon,due,overdue)

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/medspa/retreatment-due" \
  -H "Authorization: Bearer $TOKEN"

A patient's photo timeline (metadata only)

GET/v1/medspa/members/{member_ref}/photosmedspa.photos.read

med_spa_photos: each active photo's view, phase, area, date, the treatment it belongs to, and its consent flags (member_visible, marketing_consent: only the patient grants marketing). Image bytes are not served (image: null) — Norvya has no signed short-lived URL for aesthetic photos, and they are never sent to a model.

Parameters

member_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/medspa/members/$MEMBER_REF/photos" \
  -H "Authorization: Bearer $TOKEN"

GLP-1 oversight roster for your medical director

GET/v1/medspa/glp1-oversightmedspa.glp1_oversight.read

glp1_oversight.roster, the staff oversight screen's own engine: each patient on a GLP-1 / GIP medicine whose medication data they share with your practice (the share_medications switch; others are not listed), the therapy and dose as recorded, start date, last injection logged, weight trend, latest HbA1c / eGFR / creatinine, diabetes monitoring status and the live findings, ranked as on the staff screen. Each finding quotes the FDA prescribing information verbatim with its URL (citations[]; a plateau is a Norvya convention and says so). Every rule is pending clinician sign-off. Never a dose suggestion or a prescribing recommendation.

Parameters

severityquery · string | null

urgent | high | medium | low: only members with a finding at it

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

One patient's GLP-1 oversight row

GET/v1/medspa/glp1-oversight/{member_ref}medspa.glp1_oversight.read

The same row as on the roster. 404 member_not_on_glp1_roster when the patient is not on it — not on a GLP-1, or not sharing medication data with your practice: the two read the same, on purpose.

Parameters

member_refpath · stringrequired

Errors specific to this call

404member_not_on_glp1_roster

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/medspa/glp1-oversight/$MEMBER_REF" \
  -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
medspa.adverse_event_reportedmedspa.aftercare.readAn 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).

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

↑ ↓ to moveEnter to open