# Quickstart: Notetaker API

Source: https://developer.nylas.com/docs/v3/getting-started/notetaker/

The Nylas Notetaker API sends a bot to Zoom, Microsoft Teams, or Google Meet calls to record audio, video, and generate transcripts. You can send it to a specific meeting on demand, or configure it to automatically join every meeting on a user's calendar.

This quickstart covers both approaches -- sending a one-off notetaker and setting up automatic calendar-based recording for your users.

## Before you begin

You need two things from Nylas to make API calls:

1. **An API key** -- authenticates your application. You'll pass it as a Bearer token.
2. **A grant ID** -- identifies which user's account to act on. You get one when you connect an account to Nylas.

If you don't have these yet, follow one of the setup guides first:

- **[Get started with the CLI](/docs/v3/getting-started/cli/)** -- run `nylas init` to create an account, generate an API key, and connect a test account in one command.
- **[Get started with the Dashboard](/docs/v3/getting-started/dashboard/)** -- do the same steps through the web UI.

Then install the Nylas SDK for your language:

```bash [installSdk-Node.js]
npm install nylas
```

```bash
pip install nylas
```

```bash
gem install nylas
```

For Java and Kotlin, see the [Kotlin/Java SDK setup guide](/docs/v3/sdks/kotlin-java/).

## Send a notetaker to a meeting

The simplest use case: give Notetaker a meeting link and it joins, records, and transcribes. Works with any Zoom, Microsoft Teams, or Google Meet URL.

```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://zoom.us/j/123456789",
    "name": "Meeting Notes"
  }'
```

```js [sendNotetaker-Node.js]


const nylas = new Nylas({ apiKey: process.env.NYLAS_API_KEY });

const notetaker = await nylas.notetakers.create({
  identifier: process.env.NYLAS_GRANT_ID,
  requestBody: {
    meetingLink: "https://zoom.us/j/123456789",
    name: "Meeting Notes",
  },
});

console.log("Notetaker created:", notetaker.data.id);
```

```python
from nylas import Client

nylas = Client(os.environ["NYLAS_API_KEY"])

notetaker = nylas.notetakers.invite(
    request_body={
        "meeting_link": "https://zoom.us/j/123456789",
        "name": "Meeting Notes",
    },
    identifier=os.environ["NYLAS_GRANT_ID"],
)

print("Notetaker created:", notetaker.data.id)
```

```ruby
require 'nylas'

nylas = Nylas::Client.new(api_key: ENV['NYLAS_API_KEY'])

notetaker, _ = nylas.notetakers.create(
  identifier: ENV['NYLAS_GRANT_ID'],
  request_body: {
    meeting_link: 'https://zoom.us/j/123456789',
    name: 'Meeting Notes'
  }
)

puts "Notetaker created: #{notetaker[:id]}"
```

```java
public class SendNotetaker {
  public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
    NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

    CreateNotetakerRequest request = new CreateNotetakerRequest.Builder()
        .meetingLink("https://zoom.us/j/123456789")
        .name("Meeting Notes")
        .build();

    Response<Notetaker> notetaker = nylas.notetakers().create("<NYLAS_GRANT_ID>", request);

    System.out.println("Notetaker created: " + notetaker.getData().getId());
  }
}
```

```kt
fun main() {
    val nylas = NylasClient(apiKey = "<NYLAS_API_KEY>")

    val request = CreateNotetakerRequest(
        meetingLink = "https://zoom.us/j/123456789",
        name = "Meeting Notes"
    )

    val notetaker = nylas.notetakers().create("<NYLAS_GRANT_ID>", request)

    println("Notetaker created: ${notetaker.data.id}")
}
```

The bot joins the meeting within seconds. Replace the Zoom URL with any Microsoft Teams or Google Meet link -- the API is the same.

## Auto-join all meetings on a user's calendar

For most apps, you'll want Notetaker to automatically join every meeting on a user's calendar rather than sending it to individual links. Update the user's calendar settings to enable this.

```bash
curl --request PUT \
  --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/primary" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{
    "notetaker": {
      "name": "Meeting Notes",
      "meeting_settings": {
        "video_recording": true,
        "audio_recording": true,
        "transcription": true
      },
      "rules": {
        "event_selection": ["all"]
      }
    }
  }'
```

```js [autoJoin-Node.js]
const calendar = await nylas.calendars.update({
  identifier: process.env.NYLAS_GRANT_ID,
  calendarId: "primary",
  requestBody: {
    notetaker: {
      name: "Meeting Notes",
      meetingSettings: {
        videoRecording: true,
        audioRecording: true,
        transcription: true,
      },
      rules: {
        eventSelection: ["all"],
      },
    },
  },
});

console.log("Calendar sync enabled");
```

```python
calendar = nylas.calendars.update(
    os.environ["NYLAS_GRANT_ID"],
    calendar_id="primary",
    request_body={
        "notetaker": {
            "name": "Meeting Notes",
            "meeting_settings": {
                "video_recording": True,
                "audio_recording": True,
                "transcription": True,
            },
            "rules": {
                "event_selection": ["all"],
            },
        },
    },
)

print("Calendar sync enabled")
```

```ruby
calendar, _ = nylas.calendars.update(
  identifier: ENV['NYLAS_GRANT_ID'],
  calendar_id: 'primary',
  request_body: {
    notetaker: {
      name: 'Meeting Notes',
      meeting_settings: {
        video_recording: true,
        audio_recording: true,
        transcription: true
      },
      rules: {
        event_selection: ['all']
      }
    }
  }
)

puts "Calendar sync enabled"
```

```java
public class AutoJoinMeetings {
  public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
    NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

    CalendarNotetaker.MeetingSettings meetingSettings = new CalendarNotetaker.MeetingSettings.Builder()
        .videoRecording(true)
        .audioRecording(true)
        .transcription(true)
        .build();

    CalendarNotetaker.Rules rules = new CalendarNotetaker.Rules.Builder()
        .eventSelection(Collections.singletonList("all"))
        .build();

    CalendarNotetaker notetaker = new CalendarNotetaker.Builder()
        .name("Meeting Notes")
        .meetingSettings(meetingSettings)
        .rules(rules)
        .build();

    UpdateCalendarRequest request = new UpdateCalendarRequest.Builder()
        .notetaker(notetaker)
        .build();

    nylas.calendars().update("<NYLAS_GRANT_ID>", "primary", request);

    System.out.println("Calendar sync enabled");
  }
}
```

```kt
fun main() {
    val nylas = NylasClient(apiKey = "<NYLAS_API_KEY>")

    val request = UpdateCalendarRequest(
        notetaker = CalendarNotetaker(
            name = "Meeting Notes",
            meetingSettings = CalendarNotetaker.MeetingSettings(
                videoRecording = true,
                audioRecording = true,
                transcription = true
            ),
            rules = CalendarNotetaker.Rules(
                eventSelection = listOf("all")
            )
        )
    )

    nylas.calendars().update("<NYLAS_GRANT_ID>", "primary", request)

    println("Calendar sync enabled")
}
```

Once enabled, Notetaker automatically joins all meetings that have a video conferencing link. Replace `primary` with a specific calendar ID to target a different calendar. You can also add a `participant_filter` to limit which meetings Notetaker joins -- see the [calendar sync docs](/docs/v3/notetaker/calendar-sync/) for details.

> **Info:** 
> **Silence detection.** By default, Notetaker leaves after 5 minutes of continuous silence. Customize with `leave_after_silence_seconds` in `meeting_settings` (up to 3600 seconds). The timer only starts after at least one participant has spoken, so the bot won't leave an empty lobby.

## Get the recording and transcript

After a meeting ends, retrieve the recording and transcript. You'll need the notetaker ID from when you created it, or from listing your notetakers.

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/notetakers/<NOTETAKER_ID>/media" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"
```

```js [getMedia-Node.js]
const media = await nylas.notetakers.media({
  identifier: process.env.NYLAS_GRANT_ID,
  notetakerId: "<NOTETAKER_ID>",
});

console.log("Recording:", media.data);
```

```python
media = nylas.notetakers.get_media(
    notetaker_id="<NOTETAKER_ID>",
    identifier=os.environ["NYLAS_GRANT_ID"],
)

print("Recording:", media.data)
```

```ruby
media, _ = nylas.notetakers.media(
  identifier: ENV['NYLAS_GRANT_ID'],
  notetaker_id: '<NOTETAKER_ID>'
)

media.each do |file|
  puts "#{file[:type]}: #{file[:url]}"
end
```

```java
Response<List<NotetakerMedia>> media = nylas.notetakers().media("<NYLAS_GRANT_ID>", "<NOTETAKER_ID>");

for (NotetakerMedia file : media.getData()) {
  System.out.println(file.getType() + ": " + file.getUrl());
}
```

```kt
val media = nylas.notetakers().media("<NYLAS_GRANT_ID>", "<NOTETAKER_ID>")

media.data.forEach { file ->
    println("${file.type}: ${file.url}")
}
```

```json
{
  "request_id": "5fa64c92-e840-4357-86b9-2aa364d35b88",
  "data": {
    "action_items": {
      "created_at": 1703088000,
      "expires_at": 1704297600,
      "name": "meeting_action_items.json",
      "size": 289,
      "ttl": 3600,
      "type": "application/json",
      "url": "https://storage.googleapis.com/nylas-notetaker-uc1-prod-notetaker/..."
    },
    "recording": {
      "created_at": 1703088000,
      "duration": 1800,
      "expires_at": 1704297600,
      "name": "meeting_recording.mp4",
      "size": 52428800,
      "ttl": 3600,
      "type": "video/mp4",
      "url": "https://storage.googleapis.com/nylas-notetaker-uc1-prod-notetaker/..."
    },
    "summary": {
      "created_at": 1703088000,
      "expires_at": 1704297600,
      "name": "meeting_summary.json",
      "size": 437,
      "ttl": 3600,
      "type": "application/json",
      "url": "https://storage.googleapis.com/nylas-notetaker-uc1-prod-notetaker/..."
    },
    "thumbnail": {
      "created_at": 1703088000,
      "expires_at": 1704297600,
      "name": "thumbnail.png",
      "size": 437,
      "ttl": 3600,
      "type": "image/png",
      "url": "https://storage.googleapis.com/nylas-notetaker-uc1-prod-notetaker/..."
    },
    "transcript": {
      "created_at": 1703088000,
      "expires_at": 1704297600,
      "name": "transcript.json",
      "size": 10240,
      "ttl": 3600,
      "type": "application/json",
      "url": "https://storage.googleapis.com/nylas-notetaker-uc1-prod-notetaker/..."
    }
  }
}


```

The response includes download URLs for the video recording, audio recording, and transcript. Media files are available shortly after the meeting ends.

## Check notetaker status

List all notetakers for a user to see which are waiting to join, currently recording, or finished.

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/notetakers" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"
```

```js [listNotetakers-Node.js]
const notetakers = await nylas.notetakers.list({
  identifier: process.env.NYLAS_GRANT_ID,
});

notetakers.data.forEach((nt) => {
  console.log(`${nt.name} -- status: ${nt.status}`);
});
```

```python
notetakers = nylas.notetakers.list(os.environ["NYLAS_GRANT_ID"])

for nt in notetakers.data:
    print(f"{nt.name} -- status: {nt.status}")
```

```ruby
notetakers, _ = nylas.notetakers.list(identifier: ENV['NYLAS_GRANT_ID'])

notetakers.each do |nt|
  puts "#{nt[:name]} -- status: #{nt[:status]}"
end
```

```java
ListResponse<Notetaker> notetakers = nylas.notetakers().list("<NYLAS_GRANT_ID>");

for (Notetaker nt : notetakers.getData()) {
  System.out.println(nt.getName() + " -- status: " + nt.getStatus());
}
```

```kt
val notetakers = nylas.notetakers().list("<NYLAS_GRANT_ID>").data

for (nt in notetakers) {
    println("${nt.name} -- status: ${nt.status}")
}
```

## What's next

- **[Authentication](/docs/v3/auth/)** -- set up OAuth so your users can connect their accounts
- **[Calendar sync deep dive](/docs/v3/notetaker/calendar-sync/)** -- event selection rules, participant filters, and per-calendar configuration
- **[Handling media files](/docs/v3/notetaker/media-handling/)** -- downloading, storing, and processing recordings and transcripts
- **[Webhooks](/docs/v3/notifications/)** -- get notified when a notetaker finishes recording
- **[Notetaker API reference](/docs/reference/api/)** -- full endpoint documentation