Norvya Developers
Get a sandbox
Guide

Pagination, idempotency & time zones

Three conventions that are not uniform across families yet. This page says exactly what each call does today, so you do not have to discover it.

Pagination

There is no single paging scheme. Each list endpoint uses one of three styles; its reference entry names the parameters.

Cursor

Only GET /v1/appointments: pass limit (default 100, at most 500) and, for the next page, the next_cursor from the previous response as cursor. next_cursor is null on the last page. The cursor is opaque; a cursor that was not issued by Norvya answers 422 invalid_cursor.

  • GET/v1/appointmentsYour practice's appointments, booked in Norvya or sent by a practice system

Offset and limit

The response carries total, offset, limit and items; ask for the next page with offset + limit until you reach total. Used by the at-risk roster (default 25, at most 100), the panel worklist (50 / 200), invitations (50 / 200), hospital-at-home episodes (50 / 200) and a roster's enrolment (100 / 1000, nested under members).

Newest N

Some lists take only limit and return the newest items up to a hard cap; you cannot page further back. Examples: a member's messages (100 / 500, with since), webhook deliveries (50 / 200, newest first), rosters, questionnaire batches and the public-health submission and message lists. Other reads are windows rather than pages (days, hours, periods).

Idempotency and safe retries

No Idempotency-Key header. The API does not read one. Retry safety comes from natural keys on the writes that have them; the list below is every one there is.

Safe to retry

  • PUT /v1/appointments/ext/{external_id} is an upsert keyed on your client and your external_id.
  • Observations and workouts carry a per-item external_id, unique per client. The same id with the same content is counted as a duplicate and nothing is written; the same id with different content, or for another member, is refused (external_id_reused, external_id_conflict); a retracted id can never be reused (external_id_retracted). Retractions answer already_retracted when repeated.
  • A member's instrument request is idempotent with its external_id; a questionnaire batch does not send a second request to a member who already has one pending.
  • POST /v1/transitions/adt is idempotent on MSH-10: a control id already received is counted under duplicates. A message without one is refused (missing_control_id).
  • An invitation to an address that already has an open one returns that invitation with deduplicated: true and sends no second email.
  • A syndromic visit is keyed on your visit number: recording it again updates it.

Detectable but not transparent

Some writes refuse a duplicate with 409 instead of returning the original: a post-discharge episode for the same patient and discharge date (episode_exists), sending the same syndromic message (duplicate_message), transmitting a case report twice (already_sent), enrolling a patient already enrolled (already_enrolled), opening a second hospital-at-home episode (episode_already_open). After a timeout, treat that 409 as “it went through” and read the resource.

Not idempotent

Each call writes again: member messages (capped at five per member per day, then 429), webhook registrations, population exports (one running export per client at a time: 409 export_in_progress), panel imports, roster uploads and claims imports (each upload stages a new import). Before retrying one of these after a timeout, list what exists.

Time zones and dates

  • Send instants with an offset. Inputs such as an appointment's start, an observation's effective_at or collected_at, a workout's started_at and a transition contact's contact_at must carry Z or an offset (2026-10-14T09:30:00-04:00). A naive time is refused with a …_timezone_required (or contact_at_needs_offset) code rather than guessed.
  • Instants come back in UTC. Most families write ISO 8601 with a trailing Z. A few older fields still omit the Z (for example in claims, dermatology and some public-health responses): every timestamp without an offset is UTC. Appointments also return the practice-local time with its offset, and their practice_zone.
  • Dates are dates. Fields such as as_of, discharge_date, period bounds and the appointments from / to window are YYYY-MM-DD, on the practice's calendar where the reference says so.
  • Choosing a zone. The care-transitions enrol, worklist and episode reads take tz (an IANA name, default UTC) for day boundaries; appointments take a timezone field that defaults to the practice's zone. Member-facing “today” (life stages, behavioral health, documents) is the member's own local date.
↑ ↓ to moveEnter to open