# Notetaker custom video output

Source: https://developer.nylas.com/docs/v3/notetaker/custom-video-output/

By default, the Notetaker bot appears in a meeting as a blank participant tile. Custom video output replaces that tile with an image you upload: your product logo, a recording-notice card, or any other branding. The image appears as the bot's camera feed, so the bot looks like a participant with their camera on. This feature is sometimes called the bot avatar.

You upload the image once on a [configuration](/docs/v3/notetaker/configurations/) (or per Notetaker), and every bot governed by that configuration uses it. No `avatar` field or separate avatar API exists; everything goes through `notetaker_settings.video_output`.

## How custom video output works

`video_output` is an object inside `notetaker_settings` that holds a static image. When the bot joins a meeting on Google Meet, Microsoft Teams, or Zoom, the image shows as the bot's camera feed. On any other meeting provider, the bot joins normally without showing the image.

Two things to know when designing the image:

- Author it at a 16:9 aspect ratio, for example 1280x720; other ratios are letterboxed or cropped.
- Text in the image appears backwards (mirrored) in the recording. Keep that in mind if the image carries a readable message, such as a recording notice.

A broken image never blocks a recording: if the image can't be used at meeting time, the bot still joins, just without it.

You can set `video_output` in four places. It follows the standard [inheritance chain](/docs/v3/notetaker/configurations/#how-settings-combine), so the nearest layer that sets it wins:

| Endpoint                                                                                                              | Multipart JSON part name |
| --------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| [`POST /v3/notetakers/configs`](/docs/reference/api/notetaker-configurations/create-notetaker-config/)                 | `config`                 |
| [`PATCH /v3/notetakers/configs/<CONFIG_ID>`](/docs/reference/api/notetaker-configurations/update-notetaker-config/)    | `config`                 |
| [`POST /v3/notetakers`](/docs/reference/api/standalone-notetaker/invite-standalone-notetaker/) (or the grant-based invite) | `notetaker`          |
| [`PATCH /v3/notetakers/<NOTETAKER_ID>`](/docs/reference/api/standalone-notetaker/update-standalone-notetaker/) (or the grant-based update) | `notetaker` |

Calendar sync and event sync requests don't accept `video_output`. Set it on an application or workspace configuration instead so calendar-scheduled bots inherit it.

## Upload an image with multipart

Multipart is the recommended upload path: the file part limit is 4 MiB (4,194,304 bytes), roughly 3 times the base64 budget, with no encoding overhead. Send the request as `multipart/form-data` with the JSON body in one part and the image in another, and point `video_output.file_key` at the image part's name.

```bash
curl --request PATCH \
  --url "https://api.us.nylas.com/v3/notetakers/configs/<CONFIG_ID>" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  -F 'config={"notetaker_settings":{"video_output":{"file_key":"avatar"}}};type=application/json' \
  -F "avatar=@./brand.png;type=image/png"
```

A few rules keep multipart requests unambiguous:

- The JSON part must be named `config` on the configuration endpoints and `notetaker` on the Notetaker endpoints (see the table above). The wrong name returns `400 Bad Request` with `multipart config part is required` or `multipart notetaker part is required`.
- `file_key` can be any string, but it must match the name of exactly one file part in the same request. In the example, `avatar` is just a label; `image` or `brand` work equally well.
- Every file part in the request must be referenced by a `file_key`, or the request fails with `multipart request includes unreferenced file parts`.
- The `Content-Type` you declare on the file part isn't trusted. Nylas detects the format from the file bytes.

## Upload a small image with JSON and base64

For small images, skip multipart and send the bytes inline on a normal `application/json` request. Pass `media_type` and `data` together; the decoded image must be 1,376,256 bytes (about 1.31 MB) or smaller, and the whole JSON body is capped at 6.25 MiB.

```bash
curl --request PATCH \
  --url "https://api.us.nylas.com/v3/notetakers/configs/<CONFIG_ID>" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{
    "notetaker_settings": {
      "video_output": {
        "media_type": "image/png",
        "data": "<BASE64_IMAGE_BYTES>"
      }
    }
  }'
```

The two envelopes don't mix. `file_key` on a JSON request returns `video_output.file_key requires multipart/form-data`, and `data` on a multipart request returns `video_output.data requires application/json`. Likewise, `file_key` can't be combined with `media_type` or `data` in the same object, and the declared `media_type` must match what the bytes actually are.

## Image limits and formats

Nylas validates every upload by inspecting the actual file content, so a mislabeled file fails with `video_output media_type does not match file content` rather than being stored incorrectly. Re-uploading an identical image is harmless; it doesn't create a duplicate copy.

| Constraint                    | Value                                              |
| ----------------------------- | -------------------------------------------------- |
| Formats                       | PNG, JPEG, static WebP (animated WebP is rejected) |
| Maximum dimensions            | 3840x2160 pixels, and a pixel count of at most 8,294,400 |
| Maximum size, multipart part  | 4 MiB (4,194,304 bytes)                            |
| Maximum size, base64 (decoded) | 1,376,256 bytes (about 1.31 MB)                   |
| Whole-body cap, multipart     | About 7.6 MiB                                      |
| Whole-body cap, JSON          | 6.25 MiB                                           |

## Read the stored image metadata

Responses never echo the uploaded bytes, the `file_key`, or internal storage keys. Instead, both reads and writes return four read-only fields under `notetaker_settings.video_output` so you can confirm what's stored and preview it:

```json
{
  "notetaker_settings": {
    "video_output": {
      "media_type": "image/png",
      "size": 184320,
      "preview_url": "https://storage.googleapis.com/...",
      "preview_url_expires_at": 1786128794
    }
  }
}
```

`preview_url` is a signed URL that's valid for 1 hour; `preview_url_expires_at` is the Unix timestamp when it stops working. Don't cache the URL; fetch the configuration again when you need a fresh one. Sending `size`, `preview_url`, or `preview_url_expires_at` on a write returns `video_output response metadata is read-only`.

## Replace, keep, or disable the image

`PATCH` requests are presence-aware, so you control the image without re-sending up to 4 MiB of image bytes on every settings change. The four request shapes behave differently:

| Request body                                | Effect                                                    |
| ------------------------------------------- | --------------------------------------------------------- |
| `video_output` omitted                      | The existing image is preserved.                          |
| `"video_output": { ...new image... }`       | The existing image is replaced.                           |
| `"video_output": {}`                        | Video output is disabled at this configuration layer.     |
| `"notetaker_settings": null`                | The whole settings layer is cleared, including the image. |

`"video_output": null` isn't a supported way to clear the image; use the empty object `{}`. Disabling at one layer doesn't delete images stored on other layers, so a per-notetaker `{}` can switch off an image inherited from the application configuration for one bot.

## Troubleshoot upload errors

All validation failures return `400 Bad Request` with a specific message, so the error text tells you which rule you hit. The most common ones:

| Error message                                                              | Fix                                                                 |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `multipart config part is required` / `multipart notetaker part is required` | Name the JSON part `config` on config endpoints, `notetaker` on Notetaker endpoints. |
| `video_output.file_key must reference exactly one multipart file`          | The `file_key` value doesn't match a file part name (or matches several). |
| `multipart request includes unreferenced file parts`                        | Remove file parts that no `file_key` points to.                     |
| `video_output.media_type is required when data is present`                  | Send `media_type` and `data` together.                              |
| `video_output.data must decode to 1376256 bytes or smaller`                 | The image is too big for base64; switch to multipart.               |
| `video_output must be PNG, JPEG, or static WebP`                            | Convert the image; animated WebP is also rejected.                  |
| `video_output media_type does not match file content`                       | The declared type doesn't match the bytes; fix the `media_type`.    |
| `video_output image dimensions must be 3840x2160 pixels or smaller and no more than 8294400 total pixels` | Resize the image.                     |
| `request body is too large`                                                 | Stay under 7.6 MiB (multipart) or 6.25 MiB (JSON).                  |

Upload failures outside your control surface as a generic `500 Internal Server Error`. A failed request never leaves a partially stored image, so it's always safe to retry.

## What's next

- [Notetaker configurations](/docs/v3/notetaker/configurations/) for the inheritance chain your image travels down.
- [Notetaker custom announcements](/docs/v3/notetaker/custom-announcements/) for the chat messages the bot posts when it joins.
- [Update a Notetaker configuration](/docs/reference/api/notetaker-configurations/update-notetaker-config/) in the API reference.