# Notetaker custom announcements

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

By default, the Notetaker bot posts a short notice in the meeting chat a few minutes after it joins, telling attendees that the bot is recording and that consent is the meeting host's responsibility. **Custom announcements** let you write that notice yourself and add a second message that fires when a new participant joins.

You configure announcements through the `messages` field on `notetaker_settings`. Like the rest of `notetaker_settings`, you can set them on a single Notetaker, save them on a configuration so every bot picks them up, or attach them to a calendar or event so calendar sync uses them.

> **Info:** 
> **Custom announcements replace the default consent notice.** When you configure `messages.on_join`, your messages are what attendees see. Make sure your `on_join` content still mentions that the meeting is being recorded and transcribed if your product or jurisdiction requires that disclosure.

## When messages fire

Custom announcements have two triggers, and you can configure either, both, or neither:

- **`on_join`**: Messages posted after the bot joins the meeting. Each message has its own `send_after_ms` delay, measured from the moment the bot joins, and messages post in the order their delays expire. Use this for greetings, consent notices, or links to a recording policy.
- **`on_participant_join`**: Messages posted when a new participant joins. Each message uses a trailing `debounce_ms` window: the bot waits that long after a join before posting, and another join inside the window resets the timer, so a burst of 5 arrivals produces 1 message. Use this to welcome late arrivals or to tell them recording is already in progress.

Pass an empty array to disable a trigger while keeping the other one active. Messages post sequentially, so order is preserved, and if one message fails to post because of a temporary provider-side issue, the bot moves on to the next. The bot can't post at all if the meeting provider's chat is unavailable, restricted to specific roles, or disabled by the host.

## The `messages` schema

The `messages` object has two fields, each an array of up to 10 message objects:

| Field | Type | Description |
| ---- | ---- | ---- |
| `on_join` | `OnJoinMessage[]` | Messages posted after the bot joins. Up to 10 messages. |
| `on_participant_join` | `OnParticipantJoinMessage[]` | Messages posted when a new participant joins. Up to 10 messages. |

### `OnJoinMessage`

| Field | Type | Required | Constraints | Description |
| ---- | ---- | ---- | ---- | ---- |
| `type` | string | Yes | Must be `"text"` | Message format. Only `text` is supported today; the field exists so new formats can be added without a breaking change. |
| `content` | string | Yes | 1 to 1,000 characters | The text the bot posts to the meeting chat. HTML is stripped before sending, and content that's empty after stripping is rejected. |
| `send_after_ms` | integer | Yes | 1 to 600,000 (10 minutes) | Delay in milliseconds between the bot joining and this message posting. |

### `OnParticipantJoinMessage`

| Field | Type | Required | Constraints | Description |
| ---- | ---- | ---- | ---- | ---- |
| `type` | string | Yes | Must be `"text"` | Message format. Only `text` is supported today. |
| `content` | string | Yes | 1 to 1,000 characters | The text the bot posts to the meeting chat. HTML is stripped before sending, and content that's empty after stripping is rejected. |
| `debounce_ms` | integer | Yes | 1 to 600,000 (10 minutes) | Trailing debounce window in milliseconds. Another join inside the window resets the timer. |

Anything outside these constraints returns `400 Bad Request` with a validation error.

## Configure announcements

You can set `messages` anywhere `notetaker_settings` is accepted. On a single Notetaker it applies to that bot only; on a configuration it applies to every bot that inherits from it; and on a [calendar sync](/docs/v3/notetaker/calendar-sync/) or event request Nylas saves it as a calendar or event configuration that every Notetaker from that sync uses.

### Inline on a single Notetaker

Pass `messages` inside `notetaker_settings` on a [`POST /v3/notetakers`](/docs/reference/api/standalone-notetaker/invite-standalone-notetaker/) or [`POST /v3/grants/<NYLAS_GRANT_ID>/notetakers`](/docs/reference/api/notetaker/invite-notetaker/) request. Per-Notetaker settings are the most specific layer, so they override anything inherited from a configuration, calendar, or event. This example posts a greeting 5 seconds after joining and a welcome 3 seconds after each burst of late arrivals.

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/notetakers' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "meeting_link": "https://meet.google.com/abc-defg-hij",
    "notetaker_settings": {
      "recording_type": "audio_video",
      "transcription": true,
      "messages": {
        "on_join": [
          {
            "type": "text",
            "content": "👋 Hey team! I am your AI notetaker, here to capture the key points. Recording and transcription are running.",
            "send_after_ms": 5000
          }
        ],
        "on_participant_join": [
          {
            "type": "text",
            "content": "👋 Welcome! Recording and transcription are running.",
            "debounce_ms": 3000
          }
        ]
      }
    }
  }'
```

The legacy `meeting_settings` shape also accepts `messages` in the same form. See [Migrate to `notetaker_settings`](/docs/v3/notetaker/migrate-from-legacy-settings/).

### On a Notetaker configuration

To use the same announcements across many Notetakers, save them on a [Notetaker configuration](/docs/v3/notetaker/configurations/) at the application or workspace scope. Every Notetaker that inherits from that configuration uses the announcements automatically, and changing them later updates every bot that hasn't joined yet. This example sets a single consent notice that posts 3 seconds after joining.

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/notetakers/configs' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "display_name": "Company defaults",
    "notetaker_settings": {
      "recording_type": "audio_video",
      "transcription": true,
      "messages": {
        "on_join": [
          {
            "type": "text",
            "content": "Recording and transcription are running. By staying in the meeting, you consent to being recorded.",
            "send_after_ms": 3000
          }
        ]
      }
    }
  }'
```

## How `messages` inherits

`messages` follows the standard [inheritance chain](/docs/v3/notetaker/configurations/#how-settings-combine): the most specific layer that sets it wins, and if no layer sets it, the bot posts the platform default consent notice.

> **Warn:** 
> **`messages` is replaced as a whole, not merged per message.** If a more specific layer sets `messages`, it completely replaces the parent's value. You can't inherit `on_join` from an application configuration and add an `on_participant_join` at the event scope; send the full `messages` object you want at that layer. To fall back to the parent, omit `messages` from the request rather than sending an empty object.

## How `messages` appears in webhook payloads

Webhook payloads carry settings under the legacy name `meeting_settings` (see the [migration note](/docs/v3/notetaker/migrate-from-legacy-settings/) for why). When announcements are configured, the resolved `messages` object appears inside `meeting_settings` on the `notetaker.created`, `notetaker.updated`, `notetaker.meeting_state`, and `notetaker.media` triggers. If no layer sets `messages`, the field is omitted from the payload. The example below shows a `notetaker.created` payload with one `on_join` message and an empty `on_participant_join` array.

```json
{
  "type": "notetaker.created",
  "data": {
    "application_id": "<NYLAS_APPLICATION_ID>",
    "object": {
      "id": "<NOTETAKER_ID>",
      "meeting_settings": {
        "video_recording": true,
        "audio_recording": true,
        "transcription": true,
        "messages": {
          "on_join": [
            {
              "type": "text",
              "content": "👋 Hey team! Recording is running.",
              "send_after_ms": 5000
            }
          ],
          "on_participant_join": []
        }
      }
    }
  }
}
```

## Related topics

- [Using Nylas Notetaker](/docs/v3/notetaker/) for the invite flow and the rest of `notetaker_settings`.
- [Notetaker configurations](/docs/v3/notetaker/configurations/) for the full inheritance model.
- [Notetaker custom video output](/docs/v3/notetaker/custom-video-output/) for the other branding control, the image the bot shows as its camera feed.