Norvya Developers
Get a sandbox
API reference · Employers & fitness

Fitness intelligence

Built — gated8 endpoints · 1 webhook event · OpenAPI tag fitness

Ranked, gym-safe actions for a trainer (what to program, how hard to push, which movements to avoid), an at-risk roster across your members, and a way to ask Norvya to send a member its standard re-engagement nudge. A member's training (workouts, estimated one-rep max per lift, weekly hard sets against volume landmarks, the load ratio, and a readiness band only while wearables are shared), your trainers' notes both ways (attributed to your system, never to a person), and your gym's retention and engagement-versus-results as counts with small cells withheld. Never names a diagnosis, medicine or reading.

Who calls it
Core for Fitness chains; useful for Concierge doctors, Federal (via primes)
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
fitness.analytics.readRead your gym's member retention against your own baseline and whether more-engaged members get better results, as aggregate counts with small cells withheld; members who do not share activity are excluded (gyms, fitness and personal-training organisations only).
fitness.guidance.readRead gym-safe trainer guidance for a member (consent-filtered; no diagnoses, drugs or readings).
fitness.notes.readRead your trainers' notes about a member (your organisation's own notes; the member does not see them) (gyms, fitness and personal-training organisations only).
fitness.notes.writeAdd a trainer note about a member: training notes only, length-capped, kept as your organisation's record (gyms, fitness and personal-training organisations only).
fitness.nudge.writeAsk Norvya to send a member its standard re-engagement push (gyms only; Norvya's own wording, deduplicated; never your text).
fitness.roster.readRead the at-risk roster: your active members ranked for a trainer's attention (churn level, gym-safe flags, the one priority action), consent-filtered; no diagnoses, drugs or readings.
fitness.training.readRead a member's training: completed workouts, estimated one-rep max per lift, weekly hard sets per muscle group against the volume landmarks, and the training-load ratio while the member shares activity with you; the readiness band (never a recovery score or a reading) only while they share wearables (gyms, fitness and personal-training organisations only).

Endpoints

Trainer guidance for one member

GET/v1/members/{member_ref}/guidancefitness.guidance.read

Ranked, gym-safe actions for a trainer: what to program, how hard to push, what to avoid. Filtered by what the member shares with your organisation; never names a diagnosis, medication, lab value or reading. 404 member_not_found for anyone who is not your active member.

Parameters

member_refpath · stringrequired

EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens

Errors specific to this call

404member_not_found

All responses

200Successful Response401Unauthorized403Forbidden404Not Found409Conflict422Validation Error429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/members/$MEMBER_REF/guidance" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "member_ref": "string",
  "external_id": "string",
  "available": false,
  "priority": {},
  "actions": [
    {}
  ],
  "shared": [
    {}
  ],
  "withheld": [
    {}
  ],
  "cannot_assess": [
    {}
  ],
  "consent": {}
}

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

At-risk roster: your members ranked for a trainer's attention

GET/v1/members/at-riskfitness.roster.read

Your active members ranked for attention: churn risk (from club visits when your booking system is linked, else app activity), gym-safe flags and the one priority action from trainer guidance. Each member's sharing switches for your organisation apply: without activity sharing a member has no churn, no last-active date and no activity flags. Never a diagnosis, medication, lab value or reading. One chained audit row per call names the members returned. 404 member_not_found for a member_ref that is not yours.

Parameters

levelquery · string | null

high | medium | low

member_refquery · string | null

One member (mr_... or ext:...).

limitquery · integer
offsetquery · integer

EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens

Errors specific to this call

404member_not_found

All responses

200Successful Response401Unauthorized403Forbidden404Not Found422Unprocessable Entity429Too Many Requests503Service Unavailable

Codes every call can return are in Errors & limits.

curl -s "https://norvya.fly.dev/v1/members/at-risk" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "total": 0,
  "offset": 0,
  "limit": 0,
  "ranking": "string",
  "items": [
    {
      "member_ref": "string",
      "external_id": "string",
      "rank": 0,
      "churn": {
        "level": "string",
        "score": 0,
        "driver": "string"
      },
      "flags": [
        {}
      ],
      "priority_action": {
        "title": "string",
        "severity": "string",
        "based_on": []
      },
      "last_active_date": "string"
    }
  ]
}

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

Ask Norvya to send a member its re-engagement push

POST/v1/members/{member_ref}/nudgefitness.nudge.write

Norvya sends its standard re-engagement push ("We miss you at the gym" / "N days since your last session", opening Gym Mode) — Norvya's wording, never yours: the optional reason is one of a fixed list and is only recorded. At most one nudge per member per 14 days (the same dedup as the in-app trainer button and Norvya's own retention job), and the member's notification limits apply. {sent: false, reason} explains a skip: recently_nudged, no_device_token, not_sharing_activity, suppressed, send_failed, in_progress. Gym, fitness and personal-training organisations only (409 not_a_fitness_organisation).

Parameters

member_refpath · stringrequired

EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens

Request body

Content type: application/json

Errors specific to this call

409not_a_fitness_organisation

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/members/$MEMBER_REF/nudge" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "string"
  }'
Response 200
{
  "member_ref": "string",
  "external_id": "string",
  "sent": false,
  "reason": "string",
  "requested_reason": "string",
  "simulated": false
}

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

A member's training: workouts, estimated 1RM per lift, weekly sets, load

GET/v1/members/{member_ref}/trainingfitness.training.read

What the member's own Progress card shows, for their trainer: completed workouts, estimated one-rep max per lift with its trend and series, weekly hard sets per muscle group against the volume landmarks, and the training-load ratio, while the member shares activity with you. The readiness band (ready / caution / rest) only while they share wearables; the recovery score behind it, and any reading, diagnosis or medicine, never. withheld lists what the member keeps from you.

Parameters

member_refpath · stringrequired

EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens

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/members/$MEMBER_REF/training" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "member_ref": "string",
  "external_id": "string",
  "shares_activity": false,
  "workouts": {},
  "window_weeks": 0,
  "prs_this_week": 0,
  "lifts": [
    {}
  ],
  "weekly_sets": {},
  "load": {},
  "readiness": {},
  "withheld": [
    "string"
  ],
  "note": "string"
}

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

Member retention against your own baseline (aggregate, small cells withheld)

GET/v1/gym/retentionfitness.analytics.read

Active in 7 / 30 days, a weekly active trend (8 weeks), and the share of members still active 4, 8 and 12 weeks after joining, against the pre-Norvya retention baseline your organisation entered in Norvya. Members who do not share activity are counted only as not_sharing. Counts from 1 to min_cell - 1 are withheld (complementary cells too). The windows are the engine's fixed ones; there is no period parameter.

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/gym/retention" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "min_cell": 0,
  "enrolled": {
    "count": 0,
    "suppressed": false
  },
  "cohort": {
    "count": 0,
    "suppressed": false
  },
  "not_sharing": {
    "count": 0,
    "suppressed": false
  },
  "active_7d": {
    "count": 0,
    "suppressed": false
  },
  "active_30d": {
    "count": 0,
    "suppressed": false
  },
  "engagement_rate_30d": 0.0,
  "avg_streak_days": 0.0,
  "weekly_active_trend": [
    {}
  ],
  "retention_curve": {},
  "baseline_retention_rate": 0.0,
  "vs_baseline": {},
  "low_confidence": false,
  "definition": "string",
  "suppression_rule": "string"
}

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

Do your more-engaged members get better results? (aggregate)

GET/v1/gym/engagement-vs-resultsfitness.analytics.read

Members who share activity and body composition, split at the median number of active days over 90 days, with each tier's average fat-loss quality score and weight lost. Locked (available false, reason building) until 10 members share; a tier with fewer than 5 members with the outcome is not compared. An association, not proof.

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/gym/engagement-vs-results" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "min_cell": 0,
  "available": false,
  "reason": "string",
  "cohort": {
    "count": 0,
    "suppressed": false
  },
  "unlock_at": 0,
  "engagement_metric": "string",
  "median_active_days": 0,
  "comparisons": {},
  "low_confidence": false,
  "note": "string"
}

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

Your trainers' notes about a member

GET/v1/members/{member_ref}/trainer-notesfitness.notes.read

Notes written by your trainers in Norvya's gym screen or through this API, newest first (at most 200). The member does not see trainer notes. Authors' names are not returned.

Parameters

member_refpath · stringrequired

EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens

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/members/$MEMBER_REF/trainer-notes" \
  -H "Authorization: Bearer $TOKEN"
Response 200
{
  "member_ref": "string",
  "external_id": "string",
  "count": 0,
  "notes": [
    {
      "note_ref": "string",
      "text": "string",
      "author": "string",
      "created_at": "string"
    }
  ]
}

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

Add a trainer note about a member

POST/v1/members/{member_ref}/trainer-notesfitness.notes.write

A plain-text training note (at most 2000 characters), kept as your organisation's record and shown to your trainers in Norvya's gym screen; the member does not see it. Training notes only: do not record medical information. Attributed in Norvya to this client (shown to trainers as "Partner system: <client name>"), never to a person.

Parameters

member_refpath · stringrequired

EMR vendors also pass vendor (the practice) with a vendor-issued token. EMR vendor tokens

Request body

Content type: application/json (required)

textstringrequired

A training note (plain text). The member does not see trainer notes. Do not put medical information here.

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/members/$MEMBER_REF/trainer-notes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Form check on squat: depth improving. Add 5 lb next session."
  }'
Response 201
{
  "member_ref": "string",
  "note": {
    "note_ref": "string",
    "text": "string",
    "author": "string",
    "created_at": "string"
  }
}

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

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
member.at_riskfitness.roster.readA member newly entered churn level high in the daily scan.

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

↑ ↓ to moveEnter to open