Screening completed
Type:
com.auditdata.listo.screening.completed.v1Emitted when: The Listo iPad app submits a finished screening session and the Listo backend successfully persists it.
This is the only event type emitted in v1. There are no update events, no deletion events, and no lifecycle events.
Envelope
The envelope follows the standard described on Envelope. The envelope is present on Service Bus deliveries only; the Sessions API returns the screening payload directly without the envelope.
For this event type:
| Attribute | Value |
|---|---|
specversion | "1.0" |
type | "com.auditdata.listo.screening.completed.v1" |
datacontenttype | "application/json" |
source | Stable per environment, e.g. "https://api.<environment>/screener" |
Payload schema
New optional fields may be added to this payload within the .v1
type without a version bump. Existing fields will not change meaning,
type, or required-ness within .v1. See
Versioning & compatibility.
The data field carries the full screening session. Fields are grouped
below by topic; the flat, exhaustive table is at
Data schema reference.
All field names are camelCase. All timestamps are RFC 3339 date-times.
testTime and emailSentDateTime preserve the offset they were
recorded with (matching the underlying datetimeoffset storage) and
may carry any offset — e.g. 2026-05-08T11:25:33+02:00. birthday is
emitted at midnight UTC (1972-11-04T00:00:00Z). All identifiers are
either UUIDs or signed integers as documented per field.
Identifiers
| Field | Type | Nullable | Notes |
|---|---|---|---|
id | integer | no | Listo's database ID for the session. Stable across the Sessions API and redelivery. Use as your primary application-level idempotency key. |
tenantId | string (UUID) | no | The Tenant that owns this session. Matches one of the TenantIds linked to your account. |
Patient demographics
All fields in this group are nullable strings unless noted. Fields are captured by the iPad app at intake; what's filled in depends on the tenant's configured intake flow.
| Field | Type | Nullable | Notes |
|---|---|---|---|
firstname | string | yes | Up to 100 characters. |
lastname | string | yes | Up to 100 characters. |
title | string | yes | Up to 50 characters. Free-form (e.g. "Mr.", "Dr."). |
email | string | yes | Up to 100 characters. Not validated as RFC 5321 — may be free text. |
phone | string | yes | Up to 50 characters. Free-form, no canonical format. |
birthday | string (RFC 3339 date-time) | yes | The patient's birth date. Emitted as an RFC 3339 date-time at midnight UTC, e.g. "1972-11-04T00:00:00Z". The time component is always 00:00:00Z; the date is the meaningful part. |
Office and location
The location where the session was performed.
| Field | Type | Nullable | Notes |
|---|---|---|---|
officeId | integer | no | Listo's internal office ID. |
officeName | string | yes | Up to 200 characters. |
officeExternalId | string | yes | The tenant's own ID for the office, if integrated. |
officeExternalAltId | string | yes | Alternate external ID, if integrated. |
Device, transducer, and test flow
What hardware and configuration produced the result.
| Field | Type | Nullable | Notes |
|---|---|---|---|
deviceType | string | yes | Free-form device-type string. |
deviceName | string | yes | Up to 100 characters. The device's display name. |
transducerName | string | yes | Up to 50 characters. The headphones / transducer model. |
testFlowName | string | yes | Up to 250 characters. The display name of the configured test flow. |
Test results
The numerical outcome of the screening.
| Field | Type | Nullable | Notes |
|---|---|---|---|
leftTestResult | integer | no | Coded outcome for the left ear. See Test result codes below. |
rightTestResult | integer | no | Coded outcome for the right ear. See Test result codes below. |
stimulusType | integer | no | Coded stimulus presented to the patient. See Stimulus type codes below. |
noiseLevel | string | yes | Coded ambient-noise level reported by the iPad. One of "1", "2", "3" — see Noise level codes below. |
Test result codes
A coded summary of one ear's screening outcome. Set independently for
left and right ears (leftTestResult, rightTestResult).
| Value | Meaning |
|---|---|
0 | Unknown / not computed. |
1 | Normal hearing. |
2 | Some hearing loss. |
3 | Significant hearing loss. |
New result codes can appear in future versions; default to "unknown" in your handler.
Stimulus type codes
The kind of acoustic stimulus presented to the patient.
| Value | Meaning |
|---|---|
0 | Unknown. |
1 | Tone — pure tone. |
2 | Pulse — pulsed tone. |
3 | Warble — frequency-modulated tone. |
4 | PulseWarble — pulsed warble tone. |
Treat unknown integer values as opaque codes; the meanings above reflect the v1 deployment.
Noise level codes
The iPad's reported ambient-noise level. Stored as a string in the database; values come from a fixed set chosen at intake.
| Value | Meaning |
|---|---|
"1" | Silent. |
"2" | Some noise. |
"3" | A lot of noise. |
Treat unknown string values as opaque; the values above reflect the v1 intake configuration.
Audiograms
There are three audiogram-shaped objects on the payload — two measured audiograms (one per ear) and the noise-correction baseline.
Each is an object of the form:
{
"f250": null,
"f500": 15,
"f1000": 20,
"f2000": 25,
"f4000": 30,
"f6000": null,
"f8000": 35
}
Every field is a nullable integer in dB at the labelled frequency in Hz.
A null value means "no threshold available at this frequency"
(frequency wasn't tested, no response, or out of range). The seven
frequencies are fixed and complete — there are no more, no fewer.
| Field | Type | Nullable (object) | Meaning |
|---|---|---|---|
leftAudiogram | object | no (object always present) | Measured thresholds for the left ear. |
rightAudiogram | object | no | Measured thresholds for the right ear. |
noiseCorrection | object | no | The ambient-noise baseline subtracted from raw measurements. |
The audiogram object itself is always present; individual frequency values inside it are nullable.
Consent flags
All consent fields are nullable integers. The encoding is:
| Value | Meaning |
|---|---|
0 | Refused |
1 | Granted |
null | Not asked / not applicable for this tenant's intake flow |
| Field | Notes |
|---|---|
consentAgreeWithTermsConditions | Agreed to terms and conditions. |
consentContactEmail | Consents to be contacted by email. |
consentContactPhone | Consents to be contacted by phone. |
consentCustom1 | Tenant-defined custom consent slot 1. Meaning is configured per tenant. |
consentCustom2 | Tenant-defined custom consent slot 2. |
consentCustom3 | Tenant-defined custom consent slot 3. |
Status and timestamps
Lifecycle timestamps. All RFC 3339 date-times.
| Field | Type | Nullable | Notes |
|---|---|---|---|
testTime | string (RFC 3339) | no | When the screening was performed on the iPad. The offset is preserved as recorded by the device (e.g. 2026-05-08T11:25:33+02:00); not normalized to UTC. This is the field the Sessions API filters on. |
emailSentDateTime | string (RFC 3339) | yes | When the patient-facing results email was sent. The offset is preserved as recorded (e.g. 2026-05-08T11:30:14+02:00); not normalized to UTC. null on the Service Bus event — email sending is async and runs after the event is published. The Sessions API returns the accurate value once the email has been dispatched. |
emailStatus | string | yes | Email delivery status. One of "Sent", "Failed", or null (not attempted). null on the Service Bus event for the same reason; the Sessions API returns the accurate value once the email has been dispatched. |
Complete worked example (Service Bus envelope)
The example below is the full CloudEvent as it arrives on Service Bus —
envelope plus screening payload. The Sessions API returns just the
contents of data directly under each items[i], with no envelope
wrapper around it.
{
"specversion": "1.0",
"id": "7c4e6a41-7e70-4f3d-9e24-1c3f0c1c2a01",
"source": "https://api.<environment>/screener",
"type": "com.auditdata.listo.screening.completed.v1",
"time": "2026-05-08T09:30:12Z",
"datacontenttype": "application/json",
"data": {
"id": 1138291,
"tenantId": "0d3e2b51-4e7a-44b6-8d5e-1c3a9b2f7c40",
"firstname": "Avery",
"lastname": "Larsen",
"title": "Ms.",
"email": "avery.larsen@example.org",
"phone": "+45 12 34 56 78",
"birthday": "1972-11-04T00:00:00Z",
"officeId": 9412,
"officeName": "Hearing & Co. Copenhagen Central",
"officeExternalId": "HC-CPH-CENTRAL",
"officeExternalAltId": null,
"deviceType": "iPad",
"deviceName": "Listo-iPad-04",
"transducerName": "Sennheiser HDA 280",
"testFlowName": "Standard adult — 7 frequency",
"leftTestResult": 1,
"rightTestResult": 2,
"stimulusType": 0,
"noiseLevel": "2",
"leftAudiogram": {
"f250": 10, "f500": 15, "f1000": 20,
"f2000": 25, "f4000": 30, "f6000": null, "f8000": 35
},
"rightAudiogram": {
"f250": 5, "f500": 10, "f1000": 15,
"f2000": 20, "f4000": 30, "f6000": 35, "f8000": 40
},
"noiseCorrection": {
"f250": 5, "f500": 0, "f1000": 0,
"f2000": 0, "f4000": 0, "f6000": 0, "f8000": 5
},
"consentAgreeWithTermsConditions": 1,
"consentContactEmail": 1,
"consentContactPhone": 0,
"consentCustom1": 1,
"consentCustom2": null,
"consentCustom3": null,
"testTime": "2026-05-08T11:25:33+02:00",
"emailSentDateTime": null,
"emailStatus": null
}
}
Where to next
- Data schema reference — flat, exhaustive table of every field.
- Delivery semantics — at-least-once, ordering, retention.