Recording output reference
Use this reference to interpret recording objects and investigate missing output. Recording is a shared Delphi capability. This reference describes the common fields that Delphi writes; it is not an exhaustive JSON schema. Integration-specific metadata and application-defined nested values may also be present; use your assigned integration reference for their exact contract.
Artifact selection, processing and successful storage are separate conditions. A selected output is not evidence that its object exists. A stored processing status is a snapshot, not a live status endpoint.
Availability: Your application must select the relevant recording outputs, and your team must have a configured, accessible recording destination. To read the objects in the bucket, you need access in your storage account; a Delphi team role does not grant it. Optional integration metadata can be null even when its field is present.
To choose outputs, use recording selection. To configure the destination and retention, use team recording storage. For an end-to-end investigation, use recording diagnostics.
File roles and creation conditions
| Artifact | Writer | Condition / limit |
|---|---|---|
call-transcript.json | TelPhi recording uploader | Selected independently by the recording output plan; upload failure is caught/logged, so selection does not guarantee an object |
transcript-stt-input.json | Tasker audio-recording processor | STT transcript output selected and, from 0.9.18, at least one caller turn activity |
transcript-tts-output.json | Same Tasker processor | TTS transcript output selected and, from 0.9.18, at least one bot turn activity |
recording-processed.mp3 | Same Tasker processor | Processed MP3 output selected and a post-audioproc caller stage exists; bot audio makes this a stereo mix |
recording-raw.mp3 | Same Tasker processor | Raw MP3 output selected, and either a pre-audioproc caller stage exists or the input has outbound audio only |
From 0.9.18, Delphi omits an empty turn transcript: when the call had activity
only on the other side, the side-specific document is not written. In
0.9.17-patch2 and earlier, it was written with empty items.
Delphi writes no standalone .txt transcript. Other subsystems may produce additional files.
A completed recording job can legitimately upload only recording-raw.mp3 when its
input stages are pre-audioproc and outbound. Check the job's stage list before
treating the missing processed file as a processing failure. A logged S3 upload
is useful correlation evidence; verify the current object separately.
From 0.9.18, newly stored recordings use the date-partitioned prefix
recordings/{teamId}/{appId}/{YYYY}/{MM}/{DD}/{folder}. The date folders use
the UTC call-start date, with zero-padded month and day. folder combines the
formatted start timestamp, sanitized conversation ID and optional customer
integration ID. For example, a call starting on 17 June 2026 can use
recordings/team-1/app-1/2026/06/17/2026-06-17T13-09-51_conversation-1/.
Identifier sanitization replaces disallowed characters and truncates to 160
characters. Missing start time uses the current time. Failed or missing
conversation lookup still uses a flat legacy call-ID prefix. Recordings from
0.9.17-patch2 and earlier may use a flat timestamped prefix. Playback, listing
and deletion search the new and older layouts. Consumers should use recorded
object keys, not reconstruct them from this description.
Call document fields
| Field | Meaning / condition |
|---|---|
schemaVersion | Literal 1 |
generatedAt | ISO timestamp when the document is built |
storageProtection.sensitiveInfoOnStorageObserved | Whether the recorder observed sensitive-storage redaction |
storageProtection.storageRedacted, .redactionReason | Present for protected storage; true and sensitiveInfoOnStorage |
call.conversationId, .callId | Conversation-row IDs with channel/call input fallbacks |
call.traceId | Conversation trace identifier or null |
call.teamId, .appId | Recording configuration team; conversation app with configuration fallback |
call.callerNumber | In-memory PII snapshot when supplied, otherwise conversation value/null |
call.endReason | From 0.9.18, resolved detail after final hangup; null if unavailable or captured during a resumable pause |
call.startTime, .endTime | ISO timestamp from recorded milliseconds, or null |
call.durationSeconds | Recorded duration, otherwise computed from available start/end times, otherwise null |
call.flowDefinitionId, .flowVersion | Conversation flow identifiers/version, or null |
recording.bucket, .region | Selected recording destination values |
recording.s3Prefix, .transcriptS3Key | Actual prefix and full call-document key |
recording.recordingStatus | Snapshot: PROCESSING when stages are present, otherwise conversation status/null; not a live processing status |
recording.recordingFile | Conversation recording-file value/null at serialization time |
recording.stages | Uploaded stage descriptors supplied to the writer; empty when call JSON is written without persisted audio stages |
transcript | Sanitized transcript combined with recorded DTMF entries and sorted by timestamp |
dtmfEvents | DTMF sidecar array; field details below |
nodeHistory, actionsLog, finalContext, systemContext | Runtime data with additional sensitive-storage sanitization; not fixed complete object schemas here |
Stage descriptor fields are stage, s3Key, codec, sampleRate, bitDepth,
channels, byteCount and optional segments. Segment fields declared by the
writer type are startMs, byteOffset, byteLength, optional endMs, durationMs,
turnId, side, transcript, timestamp, provider, model, storageRedacted
and redactionReason. Optional/undefined members are not guaranteed JSON keys.
These descriptors identify the uploaded stages; their presence alone does not
verify media duration, channel layout or successful playback.
BigInts anywhere in this call payload serialize as strings. Runtime transcript, context, node and action values may contain arbitrary application data. Their nested shape depends on the application; the field list below does not define a closed schema for those runtime values.
Turn document and item fields
Both turn documents emit schemaVersion: 1, generatedAt, call and items.
The common call fields are conversationId and callId; integration metadata
may also be present.
The STT file keeps client-side items; the TTS file keeps bot-side items. From 0.9.18,
a side's file is omitted when that side has no turn activity. Protected turns
remain as redacted items even when their text and audio references are empty.
| Item field | Meaning / condition |
|---|---|
turnId | Nonblank segment turn-ID string, preserved unchanged, or null |
side | Display value Client or Bot |
speaker | Lowercase side client or bot |
timestamp | Segment timestamp, matching transcript ts/timestamp, or call-start plus segment offset; may be null |
startMs | Segment start offset |
endMs | Finite segment end, otherwise start plus available activity duration, otherwise null |
durationMs | Activity duration; may be null |
text | Segment transcript with matched transcript fallback; null for protected storage |
audio | Map of formats to relative object paths; may be empty |
s3Audio | Map of formats to full S3 object keys; may be empty |
mediaFormat | Activity media format |
provider, model | Segment metadata or null |
stage | Activity stage |
nodeId | Nonblank node-ID string from the matched transcript entry, preserved unchanged, or null |
storageRedacted, redactionReason | Added for protected items; true and the segment reason/fallback sensitiveInfoOnStorage |
Items are sorted by timestamp when both timestamps exist, otherwise by startMs.
Matching transcript fallback is sequential within each user/assistant role; do not
describe it as a verified semantic alignment of transcript and audio. The processor
supports raw/MP3 format outputs, but maps and actual objects depend on selection
and successful processing. A URL or object existence is not implied by a JSON key.
Redaction and DTMF boundaries
Protection changes what selected outputs retain. It does not select recording outputs, delete previously stored objects or turn application logging off.
| Protected output | Retained result |
|---|---|
| Full call transcript | Activities remain with redacted text and storageRedacted: true, redactionReason: "sensitiveInfoOnStorage". |
| STT/TTS turn transcripts | Items remain with text: null and protection markers; protected audio snippets are not stored. |
| Full call audio | Protected audio is replaced with equal-length silence, preserving the recording timeline. There is no generated redaction beep. |
Output selection and successful processing/storage remain separate requirements.
DTMF sidecars emit digit, startMs, durationMs, timestamp. Protected events
replace digit with null and add storageRedacted: true and the redaction reason.
startMs is the offset in milliseconds from when the call recorder started, or
from the call channel's start when no audio recorder runs. durationMs is the
keypress duration reported by telephony; when none is reported, Delphi stores 200.
timestamp is the ISO 8601 UTC time of the key press. Repeated presses of the same
digit less than 80 ms apart are stored once. An unprotected event looks like this:
{"digit": "1", "startMs": 12037, "durationMs": 260, "timestamp": "2026-09-16T06:11:55.211Z"}
From 0.9.18, dtmfEvents can also contain collector-beep entries with kind: "beep",
an empty digit (null when protected) and durationMs: 368.5. Delphi adds one
after it plays the keypad start tone: when keypad input collection starts, and to the
caller when an attended transfer connects. Beep entries do not appear in
transcript. Count only entries without kind as key presses.
The transcript's DTMF entry uses nodeId: dtmf, role: user, source: dtmf, ts
and text DTMF <digit> or [Redacted], with protection markers when applicable.
Marked transcript entries are re-sanitized even when an in-memory PII snapshot is used. For a protected call, context fields become redaction markers. Node history retains selected structural/timing/usage fields, with transition/error payloads marked; action entries retain selected structural identifiers and markers. This is not a claim that every field in the entire call document is anonymized.
Logging PII controls must not be equated with storage redaction: the writer can prefer an in-memory caller/transcript snapshot when the database row is redacted. For Timeline, Debug logs and trace output, verify diagnostic-log protection separately. A masked UI value alone does not prove that a stored recording was protected. This behavior is not a compliance or data-retention guarantee.
Investigate a missing or unexpected transcript
- Identify the conversation, product version and selected output artifact. Check the exact object key recorded for that conversation rather than reconstructing a prefix from its identifiers.
- For
call-transcript.json, inspect the recording uploader's result and errors. For either turn transcript, also check processing results and whether that side had turn activity. A call with only bot turns has no STT turn document. - If the JSON exists but audio cannot be fetched, distinguish an empty audio map, a missing object and denied storage access. A key in JSON is not a signed URL or proof of object existence.
- If text or DTMF is redacted, inspect the associated storage-protection markers. Changing a logging option does not restore redacted stored content.
- Preserve the conversation identifier, artifact key, product version and sanitized error details for investigation. Do not include production transcript text, credentials or caller numbers in a support example.
Delivery latency, retention and media interoperability depend on your storage destination.
Related
- Team recording storage: to check the destination, permissions and retention rule that apply to these objects.
- Diagnose recording storage and processing: to follow a missing output from selection to playback.
- Recording selection: to choose which of these outputs an application writes.