> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daily.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Recording event JSON file

> Format of the .event.json file written when a recording is started with dataOutputs: ["event-json"].

A time-stamped log of call events (tracks, active speaker, chat, transcript) written next to a `cloud`, `cloud-audio-only`, or `raw-tracks` recording. Live streams never write it.

## Enable

Pass `dataOutputs: ["event-json"]` to [`startRecording()`](https://docs.daily.co/reference/daily-js/instance-methods/start-recording) or to [`POST /rooms/:name/recordings/start`](https://docs.daily.co/reference/rest-api/rooms/recordings/start) (camelCase in REST too). Other values: `transcript-webvtt`, `chat-webvtt`. The file only captures events; it does not start transcription or chat.

## Location and timing

* Key: `<s3key>.event.json`, in the recording's bucket (Daily's, or your own [S3](https://docs.daily.co/docs/guides/features/recording/custom-s3-storage.md) / [OCI](https://docs.daily.co/docs/guides/features/recording/custom-oci-storage.md) bucket). `s3key` is in [`GET /recordings/:id`](https://docs.daily.co/reference/rest-api/recordings/get-recording) and the [`recording.ready-to-download`](https://docs.daily.co/reference/rest-api/webhooks/events/recording-ready-to-download) webhook (`s3_key`).
* Download link: `data_outputs["event-json"]` in [`GET /recordings/:id/access-link`](https://docs.daily.co/reference/rest-api/recordings/get-recording-link). `GET /recordings/:id` lists `data_outputs`.
* Uploaded when the recording stops. If the call moves to another server mid-recording, a partial copy is uploaded at that point, so the file existing does not mean the log is complete. Not uploaded if it would exceed 50 MB.
* The `recording.ready-to-download` webhook does not list this file and does not wait for it. If the file is missing when the webhook arrives, retry.

## Format

```json theme={null}
{
  "format_id": "daily-event-json",
  "format_version": "2026-04-30",
  "meeting_session_id": "<meeting session id>",
  "recording": { "id": "<recording id>", "type": "cloud-recording | raw-tracks", "instance_id": "<instance id>" },
  "events": [ { "ts": 1760500001.204, "type": "<event type>", "participant_id": "<session id>", "data": {} } ]
}
```

`recording.type` is `cloud-recording` for `cloud` and `cloud-audio-only`. Each event has `ts` (Unix seconds, float, clock time), `type`, optional `participant_id` (session ID), optional `data` (per type). Skip unknown types.

## Event types

| `type` | Recording types | When | `data` |
| - | - | - | - |
| `recording-media-started` | cloud | once, media start; always index 0 of `events`; no `participant_id` | `meeting_session_id`, `recording_id`, `instance_id`, `recording_type` |
| `recording-media-started` | raw-tracks | once per track file, when Daily starts writing it | `trackSessionNum`, `uri` (`s3://<region>/<bucket>/<key>`), `contentType`, `mediaStartTime` (Unix seconds), `user_name`, `user_id` |
| `recording-media-finished` | raw-tracks | track file reached final upload state | `trackSessionNum`, `uploadStatus` (`complete` \| `failed` \| `no-media`) |
| `track-added` | all | track joined the recording (includes tracks present at start) | `trackSessionNum`, `trackId`, `kind` (`audio` \| `video`), `trackType` (string; common values `cam-audio`, `cam-video`, `screen-audio`, `screen-video`; others such as `rmpAudio` / `rmpVideo` for remote media players can appear), `displayName`, `paused` |
| `track-removed` | all | track left the recording | `trackSessionNum`, `trackId`, `kind`, `trackType` |
| `track-paused` / `track-resumed` | all | participant muted / unmuted the track | `trackSessionNum` |
| `active-speaker` | all | active speaker changed to a different participant; an entry at recording start is not guaranteed | none; speaker is `participant_id` |
| `transcript` | all, only while [live transcription](https://docs.daily.co/docs/guides/features/transcription.md) runs | transcript message arrived, including non-final results | the transcript message: `session_id`, `user_id`, `user_name`, `trackType`, `text`, `timestamp` (ISO 8601), `speech_timestamp`, `duration_seconds`, `transcription_delay`, `instanceId`, `is_final`; optional `rawResponse`, `speaker`, `speech_final` |
| `chat-msg` | all | app message (broadcast or targeted) with `event` of `chat-msg` or `message-reaction` ([Prebuilt](https://docs.daily.co/docs/prebuilt.md) chat sends these) | the message as sent; check `data.event` to tell reactions from messages |

`trackSessionNum` is unique per track within the recording and links all `track-*` and `recording-media-*` entries for that track. A republished track gets a new `trackSessionNum`.

## Rules

* `events` is not sorted by `ts`: the cloud `recording-media-started` entry is forced to index 0. Sort if you need time order.
* Offset into media, cloud: `event.ts - start.ts`, where `start` is the `recording-media-started` entry. Raw-tracks: `event.ts - start.data.mediaStartTime`, where `start` is the `recording-media-started` entry with the same `trackSessionNum` (its `ts` is when the notification was logged, not the media start). Negative offsets (tracks present before media start) mean "at the start".
* Raw-tracks `mediaStartTime` is when writing started, not the first media sample. Good for ordering, not sample-exact sync.
* `active-speaker` is written only on change. Silence is never written.
* Stopping the recording does not write `track-removed`; a track without one ran to the end.
* Raw-tracks tracks that are paused when they would be added get `track-added` on first resume.

## Related

* [Recording calls](https://docs.daily.co/docs/guides/features/recording)
* [`DailyStreamingOptions`](https://docs.daily.co/reference/daily-js/types/daily-streaming-options)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.