# Send the recording when Notetaker is ready

Source: https://developer.nylas.com/docs/cookbook/workflows/notetaker-media-ready/

Notetaker finishes processing minutes after a call ends, and until someone checks the API nobody knows the recording exists. A `notetaker.media` workflow pushes it out the moment it's ready.

![An email announcing the recording is ready, with download links for the recording and transcript, the file format, and a watch button](/_images/workflows/notetaker-media-ready.png)

## When does `notetaker.media` fire?

When processing completes and the media is downloadable. The payload arrives with `state` set to `available` and a `media` object holding signed URLs for each artefact Notetaker produced.

```json
"media": {
  "recording": "<SIGNED_URL>",
  "transcript": "<SIGNED_URL>",
  "summary": "<SIGNED_URL>",
  "thumbnail": "<SIGNED_URL>",
  "recording_duration": "1800",
  "recording_file_format": "mp4"
}
```

Which keys are present depends on what the session was configured to capture. A session with `transcription` off has no `transcript`, and one with `video_recording` off has no `recording`. Guard every one of them, because the renderer throws on a missing key and sends nothing.

## Who receives this email?

Not the meeting attendees. `notetaker.media` carries an `event` object with only `ical_uid`, `event_id`, and `master_event_id`, so there is no participant list to address. Send a test to confirm which address receives the message before you enable this for customers. A standalone Notetaker has no grant behind it at all.

If attendees need the recording, send it from your own code: take `event.event_id` from the payload, fetch the event to get its participants, and send through [transactional send](/docs/v3/getting-started/transactional-send/). A workflow can't do that lookup.

## How do I create the workflow?

Point an application-level workflow at `notetaker.media`. Notetaker sessions are created deliberately, so the volume is low and an application-level workflow is safe here.

```bash
curl -X POST 'https://api.us.nylas.com/v3/workflows' \
  -H "Authorization: Bearer $NYLAS_API_KEY" -H 'Content-Type: application/json' \
  -d '{
    "name": "Recording ready",
    "trigger_event": "notetaker.media",
    "template_id": "<TEMPLATE_ID>",
    "delay": 0,
    "is_enabled": true,
    "from": { "email": "recordings@yourdomain.com", "name": "Your Company" }
  }'
```

Use `delay: 0`. The value of this email decays fast, and the links it carries have a limited life of their own.

## What about the signed URLs expiring?

Say so in the email. The URLs in `media` are signed and time-limited, so a recipient who opens the message a week later finds dead links and assumes the recording is gone.

Two ways to handle it. The simple one is to tell people to download what they want to keep, which the template below does in its footer. The durable one is to point the button at a page in your own product that fetches a fresh URL on demand, using the Notetaker `id` from the payload:

```handlebars
<a href="https://yourapp.com/recordings/{{id}}">Watch the recording</a>
```

That costs you a route and removes the expiry problem entirely. It also means you control who can open the recording, which a signed URL forwarded in an email doesn't.

## How does the template handle missing media?

Every media key is guarded separately, so a session that recorded audio but not video still produces a sensible email rather than failing to render.

```handlebars
{{#if media}}{{#if media.recording}}
  <a href="{{media.recording}}">Download the recording</a>
{{else}}not available{{/if}}{{/if}}
```

Both levels are needed. `media` itself is absent on a session that produced nothing at all, and reaching through it throws. The `{{else}}` branch matters as much as the guard: a row that silently disappears leaves the reader wondering whether the transcript failed or was never requested.

`recording_duration` is a string of seconds rather than a formatted length, so `1800` means 30 minutes. There's no helper for converting it, so either print it as seconds, as the template does, or format it in your own product and link out.

## The complete template

This renders the email above and handles a session missing any combination of recording, transcript, or duration.

```html [notetakerMedia-notetaker.media]

<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Your recording is ready</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
        Helvetica, Arial, sans-serif;
      line-height: 1.6;
      color: #333333;
      margin: 0;
      padding: 0;
      background-color: #f9fbfe;
      font-size: 16px;
    }

    .container {
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }

    .header {
      text-align: center;
      padding: 20px 0;
    }

    .content {
      background-color: #ffffff;
      padding: 30px;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }

    .detail-label {
      color: #666666;
      font-size: 14px;
      padding: 6px 16px 6px 0;
      vertical-align: top;
      white-space: nowrap;
    }

    .detail-value {
      padding: 6px 0;
      vertical-align: top;
    }

    .footer {
      text-align: center;
      padding: 20px 0;
      color: #666666;
      font-size: 14px;
    }

    @media only screen and (max-width: 600px) {
      .container {
        width: 100% !important;
        padding: 10px !important;
      }

      .content {
        padding: 20px !important;
      }
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="header">
      <img src="https://brand.nylas.com/assets/site_images/Nylas-Logo_Horizontal-Blue.png" alt="Nylas Logo"
        style="max-width: 150px;" />
    </div>

    <div class="content">
      <h1 style="margin-top: 0; color: #1A1A1A;">Your recording is ready</h1>

      {{#if recipient}}{{#if recipient.first_name}}<p>Hi {{recipient.first_name}},</p>{{else}}<p>Hi there,</p>{{/if}}{{else}}<p>Hi there,</p>{{/if}}<p>The recording and transcript from your {{#if meeting_provider}}{{meeting_provider}} {{/if}}meeting are ready.</p>{{#if media}}{{#if media.recording_duration}}<p>Length: {{media.recording_duration}} seconds.</p>{{/if}}{{/if}}

      <hr style="border: 0; border-top: 1px solid #eeeeee; margin: 20px 0;" />

      <h2 style="color: #4D4D4D; font-weight: 500;">Details</h2>

      <table cellpadding="0" cellspacing="0" border="0" style="width: 100%;">
        <tr><td class="detail-label">Recording</td><td class="detail-value">{{#if media}}{{#if media.recording}}<a href="{{media.recording}}" style="color: #0D6EFD;">Download the recording</a>{{else}}not available{{/if}}{{/if}}</td></tr>
        <tr><td class="detail-label">Transcript</td><td class="detail-value">{{#if media}}{{#if media.transcript}}<a href="{{media.transcript}}" style="color: #0D6EFD;">Read the transcript</a>{{else}}not available{{/if}}{{/if}}</td></tr>
        <tr><td class="detail-label">Format</td><td class="detail-value">{{#if media}}{{#if media.recording_file_format}}{{media.recording_file_format}}{{/if}}{{/if}}</td></tr>
        <tr><td class="detail-label">Meeting</td><td class="detail-value">{{#if meeting_link}}<a href="{{meeting_link}}" style="color: #0D6EFD;">{{meeting_link}}</a>{{/if}}</td></tr>
      </table>
      <div style="text-align: center; margin: 30px 0;"><a href="{{#if media}}{{#if media.recording}}{{media.recording}}{{/if}}{{/if}}" style="background-color: #0D6EFD; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Watch the recording</a></div>
      <p style="font-size: 14px; color: #666666; text-align: center;">These links are signed and expire, so download anything you want to keep.</p>
    </div>

    <div class="footer">
      <p>&copy; 2026 <a href="https://www.nylas.com/" style="color: #0D6EFD;">Nylas</a></p>
      <p style="font-size: 12px; color: #999999; margin-top: 4px;">
        {{#if id}}Notetaker: {{id}}{{/if}}
      </p>
    </div>
  </div>
</body>

</html>


```

Render it with `POST /v3/templates/render` against a payload with `media` absent as well as a full one. The [Notetaker notification reference](/docs/reference/notifications/notetaker/) publishes the full payload shape.

## What's next

- [Tell the host when Notetaker couldn't join](/docs/cookbook/workflows/notetaker-failed-to-join/) for the case where no media is ever produced
- [Tell Scheduler attendees the meeting is recorded](/docs/cookbook/workflows/recording-notice-scheduler/) for the disclosure that should precede this
- [Notetaker](/docs/v3/notetaker/) for session settings and what each one captures
- [Notetaker notifications](/docs/reference/notifications/notetaker/) for the full `notetaker.media` payload