# WAI Extension: Access Services > Status: Draft. Registers the record `wai.access.services`: the access services of a room > session, and figures about them that any verifier recomputes from signed records. Captions > are [timed-text](timed-text.md)'s, unchanged. This extension adds a **quality figure** for a > caption track: its **latency**, the room-clock instant a cue was first shown less the > capture instant of the speech it carries; its **stability**, the revisions after first > display and the words they changed; and its **accuracy**, the word errors of the first > revision shown against a reviewed final revision or a signed reference transcript. Each is > defined in integer arithmetic over the signed revisions, so two verifiers reach the same > numbers, and a figure a vendor states is checked by recomputing it. **Audio description** is > a signed description track, a script of timed descriptions with their mixing instructions > (the programme audio ducked under each) and their voicing, a separate audio track or a voice > the sink synthesizes, each labelled human or machine; it maps onto a document of the Dubbing > and Audio description Profiles of TTML2. **Sign language** is a signing track, a > `wai.avatar.pose` stream or the video of a person signing, whose objects are bound by digest > to the caption revisions they translate, with a reviewer's optional sign-off. A > **completeness claim** states, for a room session, that each access track it declares was > delivered complete and in sync, measured from signed times against the tolerances the texts > below state; a verifier checks it against the records. No new transport. The reference > implementation is the `access_services` module of `wai-rs` (feature `access_services`); the > corpus is [`live-conformance/access/`](../live-conformance/access/), and its second > implementation is `live-conformance/access_services_verify.py`. > Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. > > Texts read for this revision, each pinned to the version named: Directive (EU) 2019/882, > Annex I, Section IV, point (b)(ii), and Article 31(2), from a reproduction of the text as > adopted (the Official Journal could not be fetched for this revision); EN 301 549 V4.1.1 > (2026-09), clauses 4.2.4, 6.5.2, 6.5.3, 7.1.2, 7.1.5, 7.2.1 to 7.2.3, 7.3, 12.4 and 12.5; > Web Content Accessibility Guidelines (WCAG) 2.2, Success Criteria 1.2.2 to 1.2.7; 47 CFR > 79.1(j)(2), (j)(3) and (k)(2), and 47 CFR 79.3(a)(3), as in force on 2026-10-01; Dubbing and > Audio description Profiles of TTML2 (DAPT), W3C Candidate Recommendation Draft of > 2026-06-26 (`CRD-dapt-20260626`), §4.1 to §4.10, §5.2, §5.3, §5.5 to §5.7 and Appendix > H.2.2; EBU Tech 3370, EBU-TT Part 3, version 1.0 (May 2017), §2.3.2 and §2.3.3; and two > statements of the United Kingdom's communications regulator, *Measuring the quality of live > subtitling* (16 October 2013), §1.3, §3.65 to §3.67 and footnote 21, and *Ensuring the > quality of TV and on-demand services* (15 April 2024), §3.3, §5.17, §5.23, §5.25, §5.29, > §6.2 and §6.14. A section number of one of those texts in this extension is that version's, > and is written with the text's name; a bare section number is this extension's. DAPT is a > Candidate Recommendation Draft, whose features may change before it is a Recommendation, > and one that §5.5 uses is at risk there. ## 1. Scope In scope: the records and their encoding (§2); words, and the alignment of two lists of words (§3); the quality figure of a caption track: the sources that place its words on the room clock, a reference transcript, latency, stability, accuracy, and the figure record (§4); audio description: the description record, its script, mixing and voicing, its synchronisation, its DAPT document, and what a player owes its listeners (§5); sign language: the signing record, its binding to caption revisions, and a review (§6); the completeness claim (§7); verification (§8); and what each figure measures in the texts above (§9). Out of scope: a transport; recognising speech, writing descriptions, translating into a sign language and synthesizing speech, which produce the content; grading the severity of an error, which needs a person (§9); decoding a voicing track or a signing track, whose objects this extension names by digest; spoken subtitles and extended audio description, which pause or lengthen the programme; how a sink renders a description or a signer; and what any law requires. Relationship to the core spec: `wai.access.services` is a **record** ([SPEC](../SPEC.md) §5): a signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable. Relationship to [timed-text](timed-text.md): captions are its live captions, verified as its §4.3 verifies them, and this extension changes nothing in them. The audio description that timed-text §1 leaves out of its scope is this extension's §5. Relationship to [avatars](avatars.md) and [capture-binding](capture-binding.md): a signing track in pose form is a `wai.avatar.pose` world, whose op batches are its objects (avatars §5); a source on a take is placed on the room clock by the take's clock mapping (capture-binding §9.3). Relationship to [synthesis](synthesis.md): the origins this extension's records state are a caption's, which synthesis §1 maps onto its classes. A description voiced or written by a model, and a signing a model generates, is synthesized media in its sense: class `synthesized`, step `generate`, with the operation `speech-synthesis` for a voice, and oversight `fully_autonomous` for origin `machine` or `human_validated` for origin `machine-reviewed` (synthesis §3.1, §3.2). The publisher of such a track signs its origin claim there; this extension's label is the same statement, carried where a description or a signing is read. A review of §6.4 reviews a translation against caption revisions; a review of one exact reconstruction of a generated track, which every exact sink then shows, is synthesis §9.1's. Duties this extension supports: the access services of Directive (EU) 2019/882, Annex I, Section IV(b)(ii) ("subtitles for the deaf and hard of hearing, audio description, spoken subtitles and sign language interpretation … fully transmitted with adequate quality for accurate display, and synchronised with sound and video, while allowing for user control of their display and use"); EN 301 549 V4.1.1 clauses 7.2.1 to 7.2.3, 7.3 and 12.4, beside the caption clauses timed-text §11 answers; WCAG 2.2 Success Criteria 1.2.3, 1.2.5 and 1.2.6, beside the caption criteria 1.2.2 and 1.2.4; and the caption quality standards of 47 CFR 79.1(j). WAI states what its records carry and what a verifier checks. It makes no statement about any regime, and a deployment confirms what a regime asks of it against that regime's text. ## 2. Records Every record of this extension is a JSON object (RFC 8259), read as [jwp-receipts](jwp-receipts.md) §3.1 step 1 reads a claim: RFC 8259's grammar, no byte-order mark, no member named twice in one object, no `\u` escape that leaves a surrogate unpaired, and at most 32 levels of nesting. Its values use the encodings of jwp-receipts §2.4.3: byte strings in lowercase hexadecimal; integers unsigned, at most 2^53 − 1, in plain decimal; no other number and no boolean. The types in this extension's tables are: - `hex32`, `hex64`: 64 or 128 lowercase hexadecimal digits; - `int`: such an integer; - `sint`: a **signed integer**, written as a JSON string: `0`, or an optional `-` then a digit 1 to 9 then any digits, whose value lies in −2^63 to 2^63 − 1; - `string`: a JSON string; `tag`: a string that live-events §4.10 reads as a language tag (checked by the rules of the record's section); - a list of quoted values: a string equal to one of them; - `object`, `array`: as the table under it says; `null`, written where a member may be null. A record has exactly the members its table lists, at every level, none missing and none added, each of its type, and a `kind` that names it. One that is not JSON, or does not have that shape, is `record_malformed`. Every record carries `signer_pubkey` (`hex32`, the Ed25519 key of RFC 8032 that signs it) and `sig` (`hex64`): ``` sig = Ed25519(signer_key, "wai:access\x01" || JCS(record without "sig")) record id = SHA-256(JCS(record)) ``` JCS is RFC 8785, which on these values reduces to what jwp-receipts §2.4.3 states. The signature is verified strictly ([SPEC](../SPEC.md) §7 *Signatures*): a key that fails step 1 or 2 is `bad_key`, and a signature that fails a later step `bad_signature`. A record is named by its record id wherever another names it. A member `room` names the room by its id (live-events §2); a record whose `room` is not the verifier's room is `wrong_room`. Which signers a verifier trusts for each kind of record is its policy. This extension checks what each record states against the events and the other records; a figure's weight comes from its recomputation (§4.7), not from its signer. ## 3. Words and their alignment ### 3.1 Words The **words** of a text are found in two steps: 1. The text is split at every U+0020 and U+000A, and empty pieces are dropped. 2. In each piece, every code point from U+0041 to U+005A is replaced by the one 0x20 above it (ASCII letters to lower case), and code points of the set *P* are removed from its start and its end, repeatedly, until it neither starts nor ends with one. A piece left empty is dropped; the rest are the words, in order. *P* is U+0021 `!`, U+0022 `"`, U+0027 `'`, U+0028 `(`, U+0029 `)`, U+002C `,`, U+002D `-`, U+002E `.`, U+003A `:`, U+003B `;`, U+003F `?`, U+005B `[`, U+005D `]`, U+00A1 `¡`, U+00AB `«`, U+00BB `»`, U+00BF `¿`, U+2013 `–`, U+2014 `—`, U+2018 `‘`, U+2019 `’`, U+201C `“`, U+201D `”` and U+2026 `…`. No other normalisation is made: letters outside ASCII keep their case, and two words are equal when their code points are. The words so found ignore the case of ASCII letters and the punctuation at a word's edges, so an error of capitalisation or of such punctuation is not a word error here (§9). ### 3.2 Alignment The alignment of a **hypothesis** *H* (words *h*₁ … *h*ₘ) to a **reference** *R* (words *r*₁ … *r*ₙ) is the table *D* of integers, for 0 ≤ *i* ≤ *m* and 0 ≤ *j* ≤ *n*: - *D*[0][*j*] = *j*, and *D*[*i*][0] = *i*; - *D*[*i*][*j*] = the least of *D*[*i* − 1][*j* − 1] + *c*, *D*[*i* − 1][*j*] + 1 and *D*[*i*][*j* − 1] + 1, where *c* is 0 when *h*ᵢ = *r*ⱼ and 1 otherwise. Its **end** is a column *j*\*: - a **full** alignment ends at *j*\* = *n*; - a **prefix** alignment ends at the greatest *j* for which *D*[*m*][*j*] is the least value in row *m*: the reference is read as far as the hypothesis reaches, and no further. Its **errors** are *E* = *D*[*m*][*j*\*] and its **reference words** *N* = *j*\*. They are split into substitutions, deletions and insertions by walking back from (*m*, *j*\*) to (0, 0), taking at each step the first of these that holds: 1. *i* > 0, *j* > 0 and *D*[*i*][*j*] = *D*[*i* − 1][*j* − 1] + *c*: to (*i* − 1, *j* − 1), a **substitution** when *c* is 1; 2. *i* > 0 and *D*[*i*][*j*] = *D*[*i* − 1][*j*] + 1: to (*i* − 1, *j*), an **insertion** (a hypothesis word the reference does not have); 3. otherwise: to (*i*, *j* − 1), a **deletion** (a reference word the hypothesis does not have). The substitutions, deletions and insertions add up to *E*. ## 4. Caption quality ### 4.1 The caption track and its cues A **caption track** is the caption events of one principal *P* in a room's stream, verified as [timed-text](timed-text.md) §4.3 verifies them. Its **cues** are the chains of its revisions (timed-text §2): each from its first revision *r*₀ to its last *r*ₖ. A cue is named by the digest of *r*₀'s `caption` event (live-events §3.2). *P*'s captions are **whole** when *P* has at least one cue, the verifier holds every `seq` of *P* from 0 to the last it holds (no delivery gap of *P*, live-events §6.2), and, for every cue, *r*ₖ is final and its annotation is not unknown (timed-text §4.3). They are **resolved** when they are whole and every *r*ₖ has a `duration_ms` above 0, so that each cue resolves (timed-text §4.5). A figure is computed only over whole captions. The **events digest** of *P* is SHA-256 over the digests of *P*'s `caption` events the verifier holds, 32 bytes each, in `seq` order. ### 4.2 Sources A **sources** record places the words of each cue on the room clock: where the speech the cue carries was captured. | member | type | meaning | |---|---|---| | `kind` | `"wai-caption-sources"` | | | `room` | `hex32` | the room | | `principal` | `hex32` | *P*, whose cues it places | | `sources` | array | one entry per cue it places, each an object below | | `signer_pubkey`, `sig` | | §2 | | member of an entry | type | meaning | |---|---|---| | `cue` | `hex32` | the cue, by the digest of its first revision | | `basis` | `"take"`, `"room"` | what `start` and `end` are measured on | | `take` | `hex32` or `null` | for `take`, the take id (capture-binding §3.2); `null` for `room` | | `start`, `end` | `int` | the capture of the cue's first word and of its last: device-clock microseconds of the take, or room-clock microseconds | Its signer is whoever aligned the words to the media: the captioner, or anyone holding the media afterwards. The alignment of a word to the media is the signer's statement; the media's own time is not. On the basis `take`, the instants are positions in the master of a take, and the take's clock mapping places them on the room clock: the offset *o* of the take's opening commitment (capture-binding §9.3), which the capturing device signed while it captured. A **source instant** *d* maps to the room-clock instant *d* + *o*. On the basis `room`, the instants are on the room clock as stated. Rules, in order: 1. `room` is the verifier's: otherwise `wrong_room`. 2. No sources record of *P* comes before it in the order given: otherwise `record_conflict`. 3. Each entry: `take` is a `hex32` exactly when `basis` is `take`, and `start` ≤ `end`; no cue is named twice: otherwise `record_malformed`. 4. *P*'s captions are whole (§4.1): otherwise `stream_incomplete`. 5. Each entry's `cue` names a cue of *P*'s captions: otherwise `source_unknown`. 6. Each entry on the basis `take`: among the events the verifier accepted there is a `take_commit` event whose payload capture-binding §5.2 reads, whose `take` is the entry's take, and which is an opening commitment (`n` 0, `phase` 1, capture-binding §5.3); the first such in stream order is the take's opening commitment, its author *A* and its offset *o*. The take is **derived**: an accepted `control` event whose command is `record_start` and whose `target` is *A* has a digest *s* for which SHA-256 of `wai:capture-take`, 0x01, the room id, *A* and *s* is the take (capture-binding §3.2). And `start` + *o* is not below 0. Otherwise `source_take_unknown`. *P*'s **sources record** is the first sources record of *P* in the order given, and its **reference record** the first reference record (§4.3); a later one of either is `record_conflict`. Who may start a take, and which device captures for which principal, are live-rooms' and capture-binding's checks; this extension uses the take's mapping as capture-binding §9.3 defines it, and its bound, *J*, holds where a take verifier gives the take `single-take` and `aligned` (capture-binding §9.4, §10). ### 4.3 The reference transcript A **reference** record gives, for each cue of *P*, the text a reviewer holds correct. | member | type | meaning | |---|---|---| | `kind` | `"wai-caption-reference"` | | | `room` | `hex32` | the room | | `principal` | `hex32` | *P* | | `entries` | array | one per cue, each `{"cue": hex32, "text": string}` | | `signer_pubkey`, `sig` | | §2 | Rules, in order: `room` is the verifier's (`wrong_room`); no reference record of *P* comes before it in the order given (`record_conflict`); no cue is named twice (`record_malformed`); each text meets timed-text §3.2 rules 1 to 3 (`text_not_in_subset`); *P*'s captions are whole (`stream_incomplete`); each entry names a cue of *P* (`reference_unknown`); and every cue of *P* has an entry (`reference_incomplete`). ### 4.4 Latency The latency of a cue that a sources entry places is *L* = `t`(*r*₀) − *S*, in microseconds, where `t`(*r*₀) is the room-clock instant its first revision was shown from (timed-text §4.1) and *S* is its entry's `start` on the room clock (§4.2). *L* is negative when the caption was shown before the speech began. Over the cues of *P* that the sources record places, *n* of them, with their latencies in ascending order *L*₁ ≤ … ≤ *L*ₙ: - `min` = *L*₁, `max` = *L*ₙ; - `mean` = ⌊(*L*₁ + … + *L*ₙ) / *n*⌋, rounded toward negative infinity; - `p50` and `p95`: the latency at rank ⌈50 × *n* / 100⌉ and ⌈95 × *n* / 100⌉, counting from 1. Each is `null` when *n* is 0. The **class** of the latency is `none` when *n* is 0; otherwise `capture` when every entry is on the basis `take`, and `stated` when any is on the basis `room`. ### 4.5 Stability For one cue, its **revisions** are *k*, the revisions after the first, and its **words changed** the sum, over each revision *r*ᵢ and the one that supersedes it *r*ᵢ₊₁, of the substitutions and insertions of the full alignment of the words of *r*ᵢ (the hypothesis) to the words of *r*ᵢ₊₁ (the reference): the words a viewer saw that the next revision did not keep. A word added at the end, as a partial grows, is a deletion there and is not counted. The track's figures are the sums over its cues of revisions and of words changed, and `cues_corrected`, the number of cues whose words changed is above 0. ### 4.6 Accuracy The reference of a cue is its entry's text in *P*'s reference record, when *P* has one; otherwise the text of *r*ₖ, the cue's final revision. The **class** of the accuracy is: - `transcript` when *P* has a reference record; - `reviewed` when it has none and every cue's *r*ₖ has origin 1 (human) or 3 (machine-reviewed): a person wrote or reviewed the reference; - `self` otherwise: the reference is the machine's own final text, and the figure measures how far its first display was from its own last. Two figures, each the sums over the cues of the counts of §3.2: - `first`: the prefix alignment of the words of *r*₀ to the words of the reference. It counts the errors in what the viewer first saw, against as much of the reference as that revision reaches, so the words a partial has not reached yet are not errors. - `final`: the full alignment of the words of *r*ₖ to the words of the reference, when the class is `transcript`; `null` otherwise. Each is `{"words", "errors", "substitutions", "deletions", "insertions"}`: *N*, *E* and its three parts. ### 4.7 The figure A **quality** record states the figures of *P*'s track. | member | type | meaning | |---|---|---| | `kind` | `"wai-caption-quality"` | | | `room` | `hex32` | the room | | `principal` | `hex32` | *P* | | `events` | `hex32` | *P*'s events digest (§4.1) | | `sources` | `hex32` or `null` | the record id of *P*'s sources record, or `null` when there is none | | `reference` | `hex32` or `null` | likewise, of its reference record | | `cues` | `int` | the number of *P*'s cues | | `latency` | object | `{"class", "n", "min", "max", "mean", "p50", "p95"}`: a string of §4.4, an `int`, and five `sint` or `null` | | `stability` | object | `{"revisions", "words_changed", "cues_corrected"}`, each an `int` | | `accuracy` | object | `{"class", "first", "final"}`: a string of §4.6, an object of §4.6, and one or `null` | | `signer_pubkey`, `sig` | | §2 | Rules, in order: 1. `room` is the verifier's: otherwise `wrong_room`. 2. *P*'s captions are whole (§4.1): otherwise `stream_incomplete`. 3. `events`, `sources` and `reference` are as the table says: otherwise `figure_inputs_mismatch`. 4. Every other member but `kind`, `signer_pubkey` and `sig` equals the figure the verifier computes from §4.4 to §4.6: otherwise `figure_mismatch`. A figure that passes is the figure of exactly those revisions, placed by exactly those sources and measured against exactly that reference. A verifier needs no record of its own to compute a figure: the **figure** of a whole track is the object a quality record would carry, without `kind`, `signer_pubkey` and `sig`. ## 5. Audio description ### 5.1 The description record A **description** record is an audio description track: a script of timed descriptions of what is seen, the mixing of each into the programme audio, and its voicing. Times are media time in milliseconds, on the timeline of the media the descriptions accompany, as timed-text §2 defines it. | member | type | meaning | |---|---|---| | `kind` | `"wai-description"` | | | `lang` | `tag` | the language of the descriptions | | `script` | object | `{"origin", "model"}`: who wrote the descriptions | | `voicing` | object | `{"form", "origin", "model", "track"}`: who or what speaks them | | `events` | array | the descriptions, each an object below | | `signer_pubkey`, `sig` | | §2 | `origin` is `"human"`, `"machine"` or `"machine-reviewed"`, the names timed-text §10.5 gives the origins of a caption, and `model` is a `hex32` naming the model that wrote or spoke the text, or `null`. `form` is `"track"`, the voicing is a separate audio track named by `track` (a `hex32`, the track's digest, live-rooms §6.2), or `"sink"`, the sink synthesizes the voice from the text with the model `model`, and `track` is `null`. | member of an event | type | meaning | |---|---|---| | `id` | `string` | its identifier | | `begin`, `end` | `int` | the interval it describes, the description's time slot | | `represents` | `"visual"`, `"visual.nonText"`, `"visual.text"`, `"visual.text.title"`, `"visual.text.credit"`, `"visual.text.location"`, `"visual.dialogue"` | what of the image it describes: DAPT's content descriptors of the visual image (DAPT §4.7 and Appendix H.2.2) | | `text` | `string` | the description | | `duck` | object | `{"gain", "ramp"}`, each an `int`: the programme audio's gain under the description in thousandths, and the milliseconds it takes to fall to it and to rise back | | `voice` | object or `null` | `{"begin", "end"}`, each an `int`: where the voicing track speaks it; `null` when the form is `sink`, or when the track has not voiced it | ### 5.2 Rules In order; the first that fails names the refusal: 1. `lang` reads as live-events §4.10 reads a tag: otherwise `description_malformed`. 2. `script.model` is a `hex32` exactly when `script.origin` is `machine` or `machine-reviewed`; likewise `voicing.model` for `voicing.origin`; `voicing.track` is a `hex32` exactly when `form` is `track`; and when `form` is `sink`, `voicing.origin` is not `human`: otherwise `description_malformed`. 3. There is at least one event; each `id` is one of `A`–`Z`, `a`–`z` or `_`, then up to 63 of those, `0`–`9`, `.` and `-`; no `id` twice; each `begin` < `end` ≤ 359 999 999; each event's `begin` is at or after the `end` of the event before it (one voice describes one thing at a time): otherwise `description_malformed`. 4. Each `text` meets timed-text §3.2 rules 1 to 3: otherwise `text_not_in_subset`. 5. Each `duck`: `gain` ≤ 1000; `ramp` is 0 when `gain` is 1000 and above 0 otherwise; and 2 × `ramp` ≤ `end` − `begin`. Each `voice`: `null` when the form is `sink`, and otherwise `null` or within its event, the event's `begin` ≤ `voice.begin` < `voice.end` ≤ the event's `end`, since a Script Event's audio is truncated at its end (DAPT §4.9.1): otherwise `description_malformed`. 6. The synchronisation of §5.3: otherwise `description_out_of_sync`. ### 5.3 Synchronisation The programme audio falls to the event's gain from `begin` to `begin` + `ramp`, and rises back from `end` − `ramp` to `end`. The description is **spoken** over the interval between, from *a* = `begin` + `ramp` to *z* = `end` − `ramp`. Of a voiced event (its `voice` not `null`): - `voice.begin` ≥ *a* − 35 and `voice.begin` ≤ *a* + 45; - `voice.end` ≤ *z* + 45. These are DAPT §5.5's bounds on a presentation processor: audio presentation changes no more than 35 ms before the time a document specifies and no more than 45 ms after it, which DAPT derives from EBU R 37's bounds on audio relative to image. A voice that starts before the programme audio has fallen, or runs on after it has risen, is out of those bounds. EN 301 549 V4.1.1 clause 7.2.2 requires the synchronisation to be preserved and states no figure. The **offset** of a voiced event is `voice.begin` − *a*, a signed number of milliseconds; an unvoiced event has none. ### 5.4 Description over dialogue A description is spoken in the pauses of the dialogue (47 CFR 79.3(a)(3): "into natural pauses between the program's dialogue"), though the regulator's 2024 statement accepts describing over dialogue where what is seen matters more (§6.14 there). So this is a figure, not a refusal. Against the dialogue of one or more caption tracks, whose resolved cues (timed-text §4.5) cover the union *U* of their intervals, each from its `start` up to, not including, its `end`, an event's **overlap** is the milliseconds of its spoken interval that lie in *U*: from `voice.begin` up to `voice.end` when it is voiced, and from *a* up to *z* otherwise. The figure is the sum of the overlaps and the number of events whose overlap is above 0. ### 5.5 The DAPT document A description record maps onto one DAPT document, given, for the form `track`, the voicing track's URI `U` and media type `T`, each one to 2048 of the ASCII characters `!` to `~` other than `"`, `&`, `<` and `>` (otherwise `parameter_malformed`). It is written as UTF-8 with U+000A line ends: ``` EVENTS ``` - `LANG` is `lang`. `REPS` is each `represents` value of the events once, in the order each first appears, joined by U+0020: every event's value is then one of them (DAPT §4.1.1). `TYPE` is `asRecorded` for the form `track` and `preRecording` for the form `sink`. - `SO` and `VO` are the script's and the voicing's origins. After `wai:script="SO"` comes ` wai:script-model="M"` when the script has a model, and after `wai:voice="VO"`, ` wai:voice-model="M"` when the voicing has one, `M` the digest in lowercase hexadecimal. - `EVENTS` is, for each event in order: ```
CONTENT