How to download recording media

Get a recording download URL from a bot or recording, then read the JSON schemas for transcripts, participants, speaker timelines, and separate audio or video.

Download URLs are populated once the relevant media object has transitioned to done status. For accessing media data during recording, you can use Real-Time Endpoints

How to get a download URL

A download URL is a presigned link on a recording. Create Bot does not return the file. After the artifact's status is done, read the URL from the recording and fetch it.

  1. Create a bot with Create Bot. Mixed video is produced by default. Request other artifacts, such as a transcript, in recording_config.
  2. Wait until that media object's status is done. Subscribe to Recording Webhooks (recording.done, transcript.done, and the other artifact events), or poll Retrieve Bot or Retrieve Recording.
  3. Read the URL from recordings[].media_shortcuts on the bot, or from media_shortcuts on the recording.
  4. Fetch the URL with a normal HTTP GET. Do not send an Authorization header. The URL already includes authentication, and it expires. See Do I need an Authorization header when fetching a download URL?.

Mixed video

media_shortcuts.video_mixed.data.download_url is the MP4. See Bot Recording.

Participant events

These URLs are on media_shortcuts.participant_events.data. See Meeting Participants & Events and Speaker Timelines.

FieldWhat you get
participants_download_urlParticipant list.
participant_events_download_urlJoins, leaves, and the other events.
speaker_timeline_download_urlWho spoke, and when.

Transcript

These URLs are on media_shortcuts.transcript.data. See Transcription overview and Accessing text-to-speech provider-specific fields.

FieldWhat you get
download_urlNormalized transcript.
provider_data_download_urlRaw provider payload.

Separate video and audio

These are not on media_shortcuts. Each object from List Video Separate and List Audio Separate has a data.download_url.

FieldWhat you get
video_separate.data.download_urlJSON index of separate video parts. Each part has its own download_url for the media file.
audio_separate.data.download_urlJSON index of separate audio parts. Each part has its own download_url for the media file.

See How to get Separate Videos per Participant (Async) and How to get Separate Audio per Participant (Async).

The schemas below describe the JSON those URLs return. Mixed video, and the media files linked from separate audio and video parts, are binary files and do not have a JSON schema here.

Schema Timestamps

There are two types of timestamps you'll see in the schemas below:

  • relative - represents the time in seconds from the start of the recording (calculated from the latest in_call_recording bot status change event). For example, "relative": 9.73 means approximately 9.73 seconds after the recording began
  • absolute - represents the exact date and time this event occurred in ISO 8601 format. For example, "absolute": "2025-07-17T00:00:09.730066Z" shows the precise moment when something occurred


[JSON] participants_download_url

Participant list. Full path: media_shortcuts.participant_events.data.participants_download_url. Where to find this URL.

[
  {
    "id": number, // Id of the participant in the meeting. This id is not unique across meetings.
    "name": string | null, // Display name of the participant.
    "is_host": boolean | null, // Whether the participant is the host of the meeting.
    "platform": string | null, // Meeting platform constant. values: "desktop", "dial_in", "unknown"
    "extra_data": json | null, // Extra data about the participant from the meeting platform.
    "email": string | null,  // Email, if participant identification is turned on
  }
]


[JSON] participant_events_download_url

Joins, leaves, and the other participant events. Full path: media_shortcuts.participant_events.data.participant_events_download_url. Where to find this URL.

[
  {
    "id": string, // Id of the participant event.
    "action": string, // The action that the participant took. values: "join" | "leave" | "update" | "speech_on" | "speech_off" | "webcam_on" | "webcam_off" | "screenshare_on" | "screenshare_off" | "chat_message"
    "participant": {
      "id": integer, // Id of the participant in the meeting. This id is not unique across meetings.
      "name": string | null, // Display name of the participant.
      "is_host": boolean | null, // Whether the participant is the host of the meeting.
      "platform": string | null, // Meeting platform constant
      "extra_data": json | null, // Extra data about the participant from the meeting platform.
      "email": string | null, // Email, if participant identification is turned on
    },
    "timestamp": {
      "absolute": string, // ISO 8601
      "relative": number, // seconds
    },
    "data": 
    	{
        "text": string, // The text of chat message
        "to": string, // The recipient of the chat message. values: "everyone" | "only_bot"
      } // populated if the action is a `chat_message`
    	| null
  }
]


[JSON] speaker_timeline_download_url

Who spoke, and when. Full path: media_shortcuts.participant_events.data.speaker_timeline_download_url. Where to find this URL.

[
  {
    "participant": {
      "id": integer, // Id of the participant in the meeting. This id is not unique across meetings.
      "name": string | null, // Display name of the participant.
      "is_host": boolean | null, // Whether the participant is the host of the meeting.
      "platform": string | null, // Meeting platform constant
      "extra_data": json | null, // Extra data about the participant from the meeting platform.
      "email": string | null, // Email, if participant identification is turned on
    },
    "start_timestamp": {
      "absolute": string, // ISO 8601
      "relative": number, // seconds
    },
    "end_timestamp": {
      "absolute": string, // ISO 8601
      "relative": number, // seconds
    }
  }
]


[JSON] transcript.data.download_url

Normalized transcript. Full path: media_shortcuts.transcript.data.download_url. Where to find this URL.

[
  {
    "participant": {
      "id": number, // Id of the participant in the meeting. This id is not unique across meetings.
      "name": string | null, // Display name of the participant.
      "is_host": boolean | null, // Whether the participant is the host of the meeting.
      "platform": string | null, // Meeting platform constant
      "extra_data": json | null, // Extra data about the participant from the meeting platform.
      "email": string | null, // Email, if participant identification is turned on
    },
    "language_code": str, // The language code from the transcription provider, normalized to BCP-47.
                          // The simple code is .split('-')[0], and beware that some languages require
                          // 3-character codes (e.g. yue and haw)
    "words": [
      {
        "text": string, // The text of the word.
        "start_timestamp": {
          "absolute": string, // ISO 8601, will return null for async transcription
          "relative": number // seconds
        },
        "end_timestamp": {
          "absolute": string, // ISO 8601, will return null for async transcription
          "relative": number // seconds
        }
      }
    ]
  }
]


[JSON] video_separate.data.download_url

Separate video parts. This is data.download_url on each object from List Video Separate. Each part in the array has its own download_url for the media file. Where to find this URL.

[
  {
    "id": string, // Id of the separate part
    "participant": {
      "id": integer, // Id of the participant in the meeting. This id is not unique across meetings.
      "name": string | null, // Display name of the participant.
      "is_host": boolean | null, // Whether the participant is the host of the meeting.
      "platform": string | null, // Meeting platform constant
      "extra_data": json | null, // Extra data about the participant from the meeting platform.
      "email": string | null, // Email, if participant identification is turned on
    },
    "start_timestamp": {
      "absolute": string, // ISO 8601
      "relative": number, // seconds
    },
    "duration": number, // seconds
    "type": "webcam" | "screenshare", // the type of video part
    "download_url": string, // URL to download the media file from
  }
]


[JSON] audio_separate.data.download_url

Separate audio parts. This is data.download_url on each object from List Audio Separate. Each part in the array has its own download_url for the media file. Where to find this URL.

[
  {
    "id": string, // Id of the separate part
    "participant": {
      "id": integer, // Id of the participant in the meeting. This id is not unique across meetings.
      "name": string | null, // Display name of the participant.
      "is_host": boolean | null, // Whether the participant is the host of the meeting.
      "platform": string | null, // Meeting platform constant
      "extra_data": json | null, // Extra data about the participant from the meeting platform.
      "email": string | null, // Email, if participant identification is turned on
    },
    "start_timestamp": {
      "absolute": string, // ISO 8601
      "relative": number, // seconds
    },
    "duration": number, // seconds
    "download_url": string, // URL to download the media file from
  }
]


[JSON] transcript.data.provider_data_download_url

Raw transcript provider payload. Full path: media_shortcuts.transcript.data.provider_data_download_url. Where to find this URL.

{
  "parts": [
    {
      "id": string,
      "created_at": string, // ISO 8601
      
      // context of audio which was the transcription source for this part
      "source_audio": {
        	"type": "mixed" | "separate",
          "offset": float, // seconds from recording start
        	"participant": {
            "id": integer, // Id of the participant in the meeting. This id is not unique across meetings.
            "name": string | null, // Display name of the participant.
            "is_host": boolean | null, // Whether the participant is the host of the meeting.
            "platform": string | null, // Meeting platform constant
            "extra_data": json | null, // Extra data about the participant from the meeting platform.
            "email": string | null, // Email, if participant identification is turned on
          } | null  // participant data (only available for type=separate)
      },
      
      // part type, dictates the structure of 'data' key
      "type": "webhook" | "http_response" | "websocket_message",
      "data": 
      	{ payload: object } | // type=webhook,
        { url: string, status_code: integer, body: object } | // type=http_response
        { url: string, event: string, payload: object }, // type=websocket_message
    }
  ]
}
🚧

Provider data is currently not available when using:

FAQs

Do I need an Authorization header when fetching a download URL?

No - a download_url is presigned and already includes authentication. Fetch it without an Authorization header.



Did this page help you?