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
Section titled “Before you begin”You need two things from Nylas to make API calls:
- An API key — authenticates your application. You’ll pass it as a Bearer token.
- 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 — run
nylas initto create an account, generate an API key, and connect a test account in one command. - Get started with the Dashboard — do the same steps through the web UI.
Then install the Nylas SDK for your language:
npm install nylaspip install nylasgem install nylasFor Java and Kotlin, see the Kotlin/Java SDK setup guide.
Send a notetaker to a meeting
Section titled “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.
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" }'import Nylas from "nylas";
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);import osfrom nylas import Client
nylas = Client(os.environ["NYLAS_API_KEY"])
notetaker = nylas.notetakers.create( os.environ["NYLAS_GRANT_ID"], request_body={ "meeting_link": "https://zoom.us/j/123456789", "name": "Meeting Notes", },)
print("Notetaker created:", notetaker.data.id)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]}"import com.nylas.NylasClient;import com.nylas.models.*;
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()); }}import com.nylas.NylasClientimport com.nylas.models.CreateNotetakerRequest
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
Section titled “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.
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"] } } }'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");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")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"import com.nylas.NylasClient;import com.nylas.models.*;import java.util.Collections;
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"); }}import com.nylas.NylasClientimport com.nylas.models.*
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 for details.
Get the recording and transcript
Section titled “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.
curl --request GET \ --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/notetakers/<NOTETAKER_ID>/media" \ --header "Authorization: Bearer <NYLAS_API_KEY>"const media = await nylas.notetakers.media({ identifier: process.env.NYLAS_GRANT_ID, notetakerId: "<NOTETAKER_ID>",});
console.log("Recording:", media.data);media = nylas.notetakers.media( os.environ["NYLAS_GRANT_ID"], notetaker_id="<NOTETAKER_ID>",)
print("Recording:", media.data)media, _ = nylas.notetakers.media( identifier: ENV['NYLAS_GRANT_ID'], notetaker_id: '<NOTETAKER_ID>')
media.each do |file| puts "#{file[:type]}: #{file[:url]}"endResponse<List<NotetakerMedia>> media = nylas.notetakers().media("<NYLAS_GRANT_ID>", "<NOTETAKER_ID>");
for (NotetakerMedia file : media.getData()) { System.out.println(file.getType() + ": " + file.getUrl());}val media = nylas.notetakers().media("<NYLAS_GRANT_ID>", "<NOTETAKER_ID>")
media.data.forEach { file -> println("${file.type}: ${file.url}")}{ "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
Section titled “Check notetaker status”List all notetakers for a user to see which are waiting to join, currently recording, or finished.
curl --request GET \ --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/notetakers" \ --header "Authorization: Bearer <NYLAS_API_KEY>"const notetakers = await nylas.notetakers.list({ identifier: process.env.NYLAS_GRANT_ID,});
notetakers.data.forEach((nt) => { console.log(`${nt.name} -- status: ${nt.status}`);});notetakers = nylas.notetakers.list(os.environ["NYLAS_GRANT_ID"])
for nt in notetakers.data: print(f"{nt.name} -- status: {nt.status}")notetakers, _ = nylas.notetakers.list(identifier: ENV['NYLAS_GRANT_ID'])
notetakers.each do |nt| puts "#{nt[:name]} -- status: #{nt[:status]}"endListResponse<Notetaker> notetakers = nylas.notetakers().list("<NYLAS_GRANT_ID>");
for (Notetaker nt : notetakers.getData()) { System.out.println(nt.getName() + " -- status: " + nt.getStatus());}val notetakers = nylas.notetakers().list("<NYLAS_GRANT_ID>").data
for (nt in notetakers) { println("${nt.name} -- status: ${nt.status}")}What’s next
Section titled “What’s next”- Authentication — set up OAuth so your users can connect their accounts
- Calendar sync deep dive — event selection rules, participant filters, and per-calendar configuration
- Handling media files — downloading, storing, and processing recordings and transcripts
- Webhooks — get notified when a notetaker finishes recording
- Notetaker API reference — full endpoint documentation