Phase 1 defined what a night of sleep looks like as data: sessions, 30-second stage epochs, respiratory events, arousals and the scoring metadata that explains how they were derived. Phase 2 defines the HTTP surface through which a sleep monitoring host exposes those families. The host is typically a vendor cloud, a sleep-lab platform or a hospital remote patient monitoring system. The surface is deliberately small. Five resource families are reachable under a common /sm/ prefix, and one well-known document tells a client where they live and what the host is willing to do.
Transport rules are strict because every payload is health information. Hosts speak HTTPS only, with TLS 1.3 (RFC 8446) or later. HTTP/2 (RFC 9113) is recommended, and HTTP/1.1 must remain available as a fallback for older bedside gateways. Strict-Transport-Security follows RFC 6797, and responses may be compressed with gzip or br. That last point matters for stage and event batches, which are repetitive and compress well.
| Resource family | Clinical meaning | Route prefix |
|---|---|---|
| Session | One recording: a polysomnography night, a home sleep apnoea test, a wearable night or a nap | /sm/session |
| Stage | The hypnogram, one record per scored epoch | /sm/stage |
| Event | Respiratory events used to compute the apnoea-hypopnoea index | /sm/event |
| Arousal | Brief cortical arousals that fragment sleep | /sm/arousal |
| Scoring | The algorithm, version and lineage behind a set of results | /sm/scoring |
A client never hard-codes the paths in the table above. It first fetches /.well-known/wia-sleep-monitoring from the host. The reply is a small JSON document, shown here with the elements that matter most:
GET https://<host>/.well-known/wia-sleep-monitoring
{
"wia_sleep_monitoring_version": "1.0.0",
"host_id": "did:wia:sleep-host:hospital-sleep-lab",
"endpoints": {
"session": "https://sm.example/sm/session",
"stage": "https://sm.example/sm/stage",
"event": "https://sm.example/sm/event",
"arousal": "https://sm.example/sm/arousal",
"scoring": "https://sm.example/sm/scoring"
},
"supported_signatures": ["Ed25519"],
"supported_session_kinds": ["polysomnography","home_sleep_apnoea_test","consumer_wearable","nap"],
"supported_scoring_algorithms": ["AASM-2024-default","AASM-2018-default","Vendor-A-v3"],
"rate_limits": {
"anonymous": { "rps": 1, "burst": 5 },
"authenticated": { "rps": 30, "burst": 100 }
}
}
Two lists in this document carry sleep-medicine meaning rather than plumbing. supported_session_kinds tells a client whether the host handles attended laboratory polysomnography, unattended home testing, consumer wearables or naps. A hospital lab and a wearable vendor will advertise very different sets. supported_scoring_algorithms names the scoring rules the host can apply or has applied. It can list two editions of the AASM default side by side, such as AASM-2024-default and AASM-2018-default, next to a proprietary entry like Vendor-A-v3. A research client can see up front whether results from the host can be compared with its own.
The rate_limits block separates anonymous callers (1 request per second, burst of 5) from authenticated ones (30 per second, burst of 100). Discovery responses may be cached for 300 seconds, so a client that polls frequently should not re-fetch the document on every call.
The session is the root object. Everything else, whether epochs, events, arousals or scoring, hangs off a session_id.
POST /sm/session (signed)
GET /sm/session/{session_id}
GET /sm/session?patient_id={id}&kind={k}&from={ts}&to={ts}
PATCH /sm/session/{session_id} (signed; ended_at update + scoring_metadata link)
DELETE /sm/session/{session_id} (signed; tombstone, audit retention 25 years)
The query form of GET reflects how sleep data is actually retrieved. A clinician wants the sessions of one patient over a time window. A researcher wants every polysomnography session in a date range. Filters on patient, kind and time cover both.
Creating a session does not upload a recording. A full-night polysomnogram is stored as an EDF file, the European Data Format widely used for multichannel physiological signals. One night typically occupies hundreds of megabytes, so the session envelope carries only a pointer and a fingerprint:
POST /sm/session HTTP/1.1
Host: sm.example
Content-Type: application/json
Idempotency-Key: 7e3a9b6c-...
{ "wia_sleep_monitoring_version":"1.0.0", "type":"session",
"patient_id":"did:wia:patient:01HXY",
"session_kind":"polysomnography",
"started_at":"2026-04-26T22:30:00Z",
"ended_at":"2026-04-27T07:00:00Z",
"device_ids":["did:wia:device:psg-vendor-A"],
"clinical_grade":true,
"edf_url":"https://recordings.example/sessions/ses_01HXY.edf",
"edf_sha256":"7d8f2c…",
"signature":{ "alg":"Ed25519", "value":"…" } }
HTTP/1.1 201 Created
Location: /sm/session/ses_01HXY
The edf_sha256 value lets any later reader confirm that the file it downloaded is the one the session describes. That matters when the same EDF may be re-scored years afterwards. The clinical_grade flag is guarded. A host must refuse clinical_grade: true unless regulatory clearance proof accompanies it, so a consumer device cannot present its output as diagnostic merely by setting a boolean.
A session moves through a lifecycle:
DRAFT → IN_PROGRESS → SCORING → SCORED → ARCHIVED
↓
→ CORRECTED (new scoring_metadata appended)
Each transition is a signed PATCH made by a delegate holding the right scope. technician_scoring covers the scoring stage, physician_review covers clinical sign-off, and archive_manager covers archiving. The scopes mirror the real division of labour in a sleep lab. A technologist scores, a physician reviews, and records staff archive. CORRECTED is not an overwrite. It records that new scoring metadata was appended.
DELETE does not erase history. It leaves a tombstone, and the audit trail is retained for 25 years, in line with the long record-retention periods that clinics face under local regulation.
POST /sm/stage (signed; bulk submit per session)
GET /sm/stage/{stage_id}
GET /sm/stage?session_id={id}
Sleep is scored in fixed 30-second epochs, so an eight-hour night yields 960 of them. Posting each epoch separately would mean 960 round trips and 960 signatures. Phase 2 therefore treats bulk submission as the normal pattern. One stage_batch envelope carries the whole hypnogram for a session:
{ "wia_sleep_monitoring_version":"1.0.0",
"type":"stage_batch",
"session_id":"ses_01HXY",
"stages":[
{ "epoch_index":0, "epoch_start":"2026-04-26T22:30:00Z",
"epoch_duration_seconds":30, "stage":"wake", "confidence":0.99 },
{ "epoch_index":1, "epoch_start":"2026-04-26T22:30:30Z",
"epoch_duration_seconds":30, "stage":"n1", "confidence":0.85 }
/* ... remaining epochs ... */
],
"signature":{ "alg":"Ed25519", "value":"…" } }
HTTP/1.1 201 Created
Each epoch has an index, a start time, a duration, a stage label (the example shows wake and n1) and a confidence value. The confidence field is especially useful for wearable and automated scoring, where a stage call can be tentative. A reviewing clinician can concentrate on low-confidence stretches instead of re-reading the whole night.
Hosts must honour the Idempotency-Key header. Large overnight uploads often come from home gateways on unreliable connections, and a client that times out cannot know whether the batch was stored. Repeating the request with the same key lets it retry safely, without risking a hypnogram with duplicated or half-written epochs. Hosts must also persist a write durably before acknowledging it.
POST /sm/event (signed; respiratory event submission)
GET /sm/event/{event_id}
GET /sm/event?session_id={id}&kind={k}
Respiratory events are the substance of sleep apnoea diagnosis. The headline number is the apnoea-hypopnoea index (AHI), the count of apnoeas and hypopnoeas divided by hours of sleep. AHI is the main quantity that places a patient in a severity band. The event routes exist so that this number can be recomputed rather than merely stored.
Two rules support that goal. The first is that submissions can be bulk, using an events: [...] array, which is the natural shape for a night with many events. The second is that hosts must preserve event order by started_at, because downstream AHI recomputation depends on it. The kind filter on reads lets a client pull one category of event without scanning the full list.
Events and stages are linked through the denominator. The event count is only half of the AHI. The sleep time comes from the stage records, since wake epochs do not count toward it. A host that stores both under one session_id can therefore recompute the index when either side changes, for instance when a technologist corrects a stretch of epochs from sleep to wake.
POST /sm/arousal (signed; bulk submit)
GET /sm/arousal/{arousal_id}
GET /sm/arousal?session_id={id}
An arousal is a brief shift toward wakefulness in the EEG that interrupts sleep without necessarily waking the patient. Counting them gives a view of sleep fragmentation that stage labels alone do not show. The AASM scoring manual sets a minimum duration of three seconds. Phase 2 turns that clinical rule into a gate: a host must refuse any arousal whose duration_seconds is below 3.
The refusal is a structured error, not a bare status code. Errors follow RFC 9457 problem details, and the reserved error types live under https://wiastandards.com/sleep-monitoring/errors/:
{
"type": "https://wiastandards.com/sleep-monitoring/errors/arousal-too-short",
"title": "Arousal duration below AASM minimum",
"status": 422,
"detail": "arousal.duration_seconds=2 violates AASM 3-second minimum",
"instance": "/sm/arousal/ar_01HXY-23"
}
The detail text names the offending value and the rule it violates. A technologist's scoring software can show the message directly instead of leaving the user to guess. It is also a deliberate design choice that the server enforces the rule. An uploader with a scoring bug cannot silently inflate an arousal index with sub-threshold events.
POST /sm/scoring (signed; new scoring_metadata)
GET /sm/scoring/{scoring_metadata_id}
POST /sm/scoring/{scoring_metadata_id}/recompute (re-run with new algorithm)
Scoring rules are not static. When the AASM publishes a manual update, or a vendor ships a new algorithm version, previously scored studies may need to be scored again. The recompute route is the sanctioned path for this. It re-runs a session under a new algorithm and produces a new scoring_metadata record that points to the earlier one through supersedes. The original is kept. This gives a reader two guarantees at once. The current result is easy to identify, and the history behind it can be reconstructed. A clinician comparing a 2018-rule result with a 2024-rule result is looking at two preserved records, not one overwritten number.
The algorithm name carries real weight, particularly for children. AASM rules for paediatric patients differ from those for adults, so the standard expects the algorithm field to distinguish them, for example with AASM-2024-paediatric. This prevents an adult-rule comparison from being made on paediatric data without anyone noticing.
Operationally, re-scoring is cheap in transfer terms. Because the supersedes chain is metadata, a re-score does not force the large EDF recording to move again. Sleep studies are typically scored one to three days after recording, so latency is rarely the concern. The concern is that every scoring envelope must be preserved, since a gap in that chain is a gap in the evidence trail.
The access model separates writing, anonymous reading and reading protected health information:
| Operation | Requirement |
|---|---|
| Any write | HTTP Message Signatures per RFC 9421, plus Idempotency-Key |
| Read without PHI | May be anonymous, subject to the discovery rate limit |
| Read carrying PHI | Must be authenticated |
| Read across vendors | Uses Phase 3 federation receipts |
The payload also carries its own signature (the examples use Ed25519), so an envelope remains attributable after it leaves the HTTP exchange. That matters for the audit trail. Cross-host reads are logged with the peer's receipt, the consent envelope referenced, the scope returned and a timestamp. The log is append-only, and accreditation reviewers treat it as primary evidence.
Taken together, a Phase 2 host must publish the discovery document with every endpoint. It must implement the five route families, honour Idempotency-Key on writes, refuse sub-3-second arousals, refuse unproven clinical_grade claims, and answer 4xx/5xx failures with problem-detail JSON. A published black-box suite exercises these behaviours, and within the 1.x line existing endpoints must not be removed or repurposed. Removal requires a major version and a 12-month deprecation window.
stage_batch, event, arousal and session envelopes here are the Phase 1 shapes, now carried over HTTP. Field meanings are defined in the data-format chapter, not repeated here.supersedes chain from 5.7 is also detailed there.Several operational points sit around the endpoints and are easy to underestimate. Storage cost is dominated by raw EDF files, since clinics retain them for 7 to 25 years depending on local regulation. Hosts are advised to offer a separate read replica for query-heavy research workloads, and a /sm/health liveness probe that sits outside public rate-limit accounting. Problem-detail responses should be retained for identified peers for 30 days to help debug across hosts.
Failover has its own rules. A standby host must reload its seen-nonce cache before processing envelopes, re-issue handshakes to peers whose receipts it lacks, replay scoring envelopes it missed from the primary's append-only log, and notify peers with a notice envelope that includes an estimated restore_at. Because studies are scored over days, not minutes, speed is not the main risk. The audit trail is.
Versioning and extension follow a few simple rules. The prefixes /sm/audit/, /sm/integrity/, /sm/regulator/, /sm/research/ and /sm/family/ are reserved and must not serve unrelated content. A receiver must refuse a major version it does not implement. It should treat unknown minor-version fields as non-fatal, and treat unknown enum tokens as unrecognised while still processing the rest of the envelope.
For patients, the interface choices translate into practical protections. Reads that carry health data require authentication, and cross-vendor sharing runs through receipts that reference consent. A deletion request leaves a tombstone and a 25-year audit trail, so the patient should be told that the record of access and removal outlives the data itself. A clinical_grade label can only appear with clearance proof. A figure from a consumer wearable will not look diagnostic unless it has been cleared.
For clinicians, the important behaviours are the ones that protect interpretation. Minimum arousal duration is enforced on the server. AHI inputs keep their started_at order. A re-scored study never replaces its predecessor, because the earlier result stays reachable through supersedes. Paediatric studies must be identified by algorithm so that adult rules are not applied to them by accident. The state changes in the lifecycle also show who acted, because each one requires a delegate scope such as technician_scoring or physician_review.
To summarise the chapter: Phase 2 turns the Phase 1 data families into five signed, idempotent route sets. Discovery describes what a host supports. Bulk stage and event submission handle the size of a night's data. Server-side checks protect AASM rules. Scoring supersession makes re-analysis safe. The next chapter moves from a single host to the relationships between hosts, covering how recording hosts, scoring services, clinical systems and research aggregators establish trust, exchange federation receipts and share sleep data under consent.