{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreic5uvxulwver7uawkgls36dcvtkvshfc5ibdicfufn6alk65n7ugq",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3motdnkimfax2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreig6xffqsh73lf22dq4wwywof74ne3l7j3v6gdbt6x6jbwwprmbe4i"
},
"mimeType": "image/webp",
"size": 61042
},
"path": "/mqasimca/record-and-transcribe-meetings-with-the-nylas-notetaker-api-2emk",
"publishedAt": "2026-06-21T21:27:02.000Z",
"site": "https://dev.to",
"tags": [
"api",
"ai",
"devtools",
"productivity",
"Nylas CLI",
"Notetaker overview",
"invite notetaker",
"notetaker create",
"notetaker list",
"get notetaker media",
"notetaker media",
"notetaker delete",
"Invite a notetaker",
"Notetaker API reference",
"Nylas CLI command reference",
"Qasim Muhammad",
"Pouya Sanooei"
],
"textContent": "Meeting notes are the feature everyone wants and nobody wants to build. The hard part isn't the summary — an LLM handles that. The hard part is getting into the meeting: a bot that joins Zoom, Google Meet, and Microsoft Teams, survives each platform's waiting room and admission flow, records cleanly, and produces a transcript you can feed downstream. Each provider has its own join mechanics, and none of them ships a tidy \"record this meeting\" API.\n\nThe Nylas Notetaker API is that bot as a service. You point it at a meeting link, it joins on schedule, records, and generates a transcript, and you fetch the recording and transcript through one endpoint. This post walks the Notetaker surface from the HTTP API and the Nylas CLI, which mirrors the whole lifecycle for terminal use and quick testing.\n\nI work on the CLI, so the terminal commands below are exactly what I run when I'm testing a notetaker against a live meeting.\n\n## Two ways to run a notetaker: grant-scoped or standalone\n\nBefore any code, there's one architectural choice worth understanding, because it changes the endpoint you call.\n\nA **grant-scoped** notetaker is tied to a connected account and lives under `/v3/grants/{grant_id}/notetakers`. Use it when the bot acts on behalf of a specific user — it can read that user's calendar and join their meetings as them.\n\nA **standalone** notetaker has no grant at all and lives under `/v3/notetakers`. You hand it a raw meeting link and it joins, no connected account required. This is the one to reach for when you just have a URL and want a recording — a public webinar, a meeting on an account you haven't connected, or a system that deals in links rather than users.\n\nSame request body, same lifecycle, same media output; the only difference is whether there's a `grant_id` in the path. See the Notetaker overview for how both models fit together.\n\n## Before you begin\n\nYou need a Nylas API key. If you're using a grant-scoped notetaker you also need a connected account; for standalone, the API key alone is enough. The CLI gets you started:\n\n\n\n nylas init # create an account, generate an API key\n nylas auth login # only needed for grant-scoped notetakers\n\n\nThe supported meeting providers are Zoom, Google Meet, and Microsoft Teams. Any meeting link from those three works as the target.\n\n## Send a notetaker into a meeting\n\nCreating a notetaker is one call. The only required field is the meeting link; everything else has a sensible default. The CLI joins immediately if you don't pass a time:\n\n\n\n # Join right now\n nylas notetaker create --meeting-link \"https://zoom.us/j/123456789\"\n\n # Join at a specific time, with a custom display name\n nylas notetaker create \\\n --meeting-link \"https://meet.google.com/abc-defg-hij\" \\\n --join-time \"2026-06-23 14:00\" \\\n --bot-name \"Acme Notetaker\"\n\n\nThe `--bot-name` flag sets what the other participants see in the attendee list, which matters more than it sounds — a bot labeled \"Acme Notetaker\" reads as intentional, while a generic name makes people wonder who joined. The `--join-time` flag accepts an absolute time, a relative offset like `30m`, or natural phrasing like `tomorrow 9am`.\n\nOver HTTP, the request body fields are `meeting_link` (required), `join_time`, `name`, and `meeting_settings`:\n\n\n\n curl --request POST \\\n --url \"https://api.us.nylas.com/v3/grants/<GRANT_ID>/notetakers\" \\\n --header \"Authorization: Bearer <NYLAS_API_KEY>\" \\\n --header \"Content-Type: application/json\" \\\n --data '{\n \"meeting_link\": \"https://meet.google.com/abc-defg-hij\",\n \"name\": \"Acme Notetaker\",\n \"meeting_settings\": {\n \"video_recording\": true,\n \"audio_recording\": true,\n \"transcription\": true\n }\n }'\n\n\nFor a standalone notetaker, drop the grant from the path and POST the same body to `/v3/notetakers`. The `meeting_settings` object controls what gets captured — `video_recording`, `audio_recording`, and `transcription` toggles. They aren't fully independent, though: if `transcription` is `true`, both `video_recording` and `audio_recording` must also be `true`, because the transcript is generated from the recording. So \"transcript only, no recording\" isn't a valid combination — you can record audio-only, but a transcript always implies a recording exists.\n\nThere's also a `transcription_settings` object with an `expected_languages` array. It's a language _declaration_ , not translation: the hints help the speech recognizer pick between the languages you expect in the meeting, and Notetaker never translates the transcript or forces it into one language. The full request body is in the invite notetaker reference, and the CLI flags are at notetaker create.\n\n## Follow the lifecycle\n\nA notetaker moves through states, and your app usually waits for it to finish before pulling media. The CLI's `--state` filter accepts friendly names — `scheduled` (created, waiting to join), `connecting`, `attending` (in the meeting, recording), `complete` (done, media ready), `cancelled`, and `failed`. The raw API `state` field is more granular: a notetaker object surfaces values like `waiting_for_entry`, `processing`, and `available` (the API's equivalent of the CLI's \"complete\"). The list endpoint's `state` query filter uses its own enum (for example `media_available`), so don't assume the filter value and the object's `state` string are spelled identically. List notetakers and filter by state to find the ones worth acting on:\n\n\n\n # All notetakers (20 by default)\n nylas notetaker list\n\n # Only the finished ones, ready for media\n nylas notetaker list --state complete\n\n\nThe `--state` filter is how you build a polling loop: ask for `complete` notetakers, process their media, move on. The CLI returns 20 by default and the notetaker list command documents the rest. To inspect a single notetaker's current state and metadata, use `nylas notetaker show <notetaker-id>`. In production, rather than polling, you'd subscribe to notetaker webhooks so Nylas tells you when a session reaches `complete`.\n\n## Pull the recording and transcript\n\nOnce a notetaker is `complete`, its media is ready. The media endpoint returns URLs for the recording (the video or audio file) and the transcript (the text). One thing to watch: these URLs expire, so download promptly rather than storing the URL and fetching it days later.\n\n\n\n nylas notetaker media <notetaker-id>\n\n\nOver HTTP, it's a `GET` against the notetaker's media path:\n\n\n\n curl --request GET \\\n --url \"https://api.us.nylas.com/v3/grants/<GRANT_ID>/notetakers/<NOTETAKER_ID>/media\" \\\n --header \"Authorization: Bearer <NYLAS_API_KEY>\"\n\n\nThe response gives you the download links; pull the bytes to your own storage immediately if you need them long-term. From there the transcript is plain text you can hand to an LLM for a summary, action items, or a searchable index. The get notetaker media reference and the notetaker media command cover the details.\n\n## Leave, cancel, or delete — they're different\n\nThis is the distinction that catches people, because the three sound interchangeable and aren't:\n\n * **Leave** tells an _active_ notetaker to exit the meeting now. It stops the recording, triggers media generation, and keeps the notetaker record and its media. Use it to cleanly end a live recording early.\n * **Cancel** stops a _scheduled_ notetaker before it ever joins. There's nothing to record, so there's no media. The API exposes this as a dedicated cancel endpoint.\n * **Delete** permanently removes the notetaker and all its media. If the notetaker is still scheduled or active, delete cancels it first, and any recording or transcript you haven't already downloaded is lost. In the CLI, `cancel` and `rm` are aliases of `delete`.\n\n\n\nFrom the CLI:\n\n\n\n # End a live recording but keep the media\n nylas notetaker leave <notetaker-id>\n\n # Remove a notetaker and its media\n nylas notetaker delete <notetaker-id>\n\n\nReaching for `delete` when you meant `leave` permanently throws away the recording you just made, so it's worth getting right. The notetaker delete command and the API's separate leave and cancel endpoints keep these operations distinct on purpose.\n\n## Capabilities and notes\n\nDimension | Value | Notes\n---|---|---\nProviders | Zoom, Google Meet, Microsoft Teams | Any meeting link from these three\nModels | Grant-scoped and standalone | `/v3/grants/{id}/notetakers` vs `/v3/notetakers`\nCapture toggles | video, audio, transcription | `meeting_settings` flags; transcription requires both recordings on\nMedia URLs | Expiring | Download promptly; store your own copy\nLifecycle | scheduled → connecting → attending → complete | Simplified CLI states; the API exposes more granular values\n\nThe standalone model is the feature I'd point a new integration at first. Not needing a connected account to record a meeting removes the entire OAuth step for the common case where you already have a link, and you can always move to grant-scoped notetakers later when the bot needs to act as a specific user.\n\n## Wrapping up\n\nThe value here is that the genuinely hard part — a bot that reliably joins three different meeting platforms and comes back with clean media — is the part you don't write. You make one call with a meeting link, wait for `complete`, and pull a recording and transcript. The summary layer on top is a solved problem; getting a trustworthy transcript out of a live meeting was the bottleneck, and that's what the API removes. The CLI mirrors the full lifecycle, so you can record a real meeting and inspect the transcript from your terminal before committing any of it to code.\n\nWhere to go next:\n\n * Notetaker overview — grant-scoped and standalone models, lifecycle, and webhooks\n * Invite a notetaker — the full create request body\n * Notetaker API reference — media, leave, cancel, and list endpoints\n * Nylas CLI command reference — every `nylas notetaker` subcommand\n\n\n\n_Written by Qasim Muhammad and Pouya Sanooei._",
"title": "Record and transcribe meetings with the Nylas Notetaker API"
}