DEV Community

Qasim
Qasim

Posted on

Record and transcribe meetings with the Nylas Notetaker API

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.

The 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.

I work on the CLI, so the terminal commands below are exactly what I run when I'm testing a notetaker against a live meeting.

Two ways to run a notetaker: grant-scoped or standalone

Before any code, there's one architectural choice worth understanding, because it changes the endpoint you call.

A 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.

A 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.

Same 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.

Before you begin

You 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:

nylas init        # create an account, generate an API key
nylas auth login  # only needed for grant-scoped notetakers
Enter fullscreen mode Exit fullscreen mode

The supported meeting providers are Zoom, Google Meet, and Microsoft Teams. Any meeting link from those three works as the target.

Send a notetaker into a meeting

Creating 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:

# Join right now
nylas notetaker create --meeting-link "https://zoom.us/j/123456789"

# Join at a specific time, with a custom display name
nylas notetaker create \
  --meeting-link "https://meet.google.com/abc-defg-hij" \
  --join-time "2026-06-23 14:00" \
  --bot-name "Acme Notetaker"
Enter fullscreen mode Exit fullscreen mode

The --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.

Over HTTP, the request body fields are meeting_link (required), join_time, name, and meeting_settings:

curl --request POST \
  --url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/notetakers" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{
    "meeting_link": "https://meet.google.com/abc-defg-hij",
    "name": "Acme Notetaker",
    "meeting_settings": {
      "video_recording": true,
      "audio_recording": true,
      "transcription": true
    }
  }'
Enter fullscreen mode Exit fullscreen mode

For 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.

There'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.

Follow the lifecycle

A 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:

# All notetakers (20 by default)
nylas notetaker list

# Only the finished ones, ready for media
nylas notetaker list --state complete
Enter fullscreen mode Exit fullscreen mode

The --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.

Pull the recording and transcript

Once 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.

nylas notetaker media <notetaker-id>
Enter fullscreen mode Exit fullscreen mode

Over HTTP, it's a GET against the notetaker's media path:

curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/notetakers/<NOTETAKER_ID>/media" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"
Enter fullscreen mode Exit fullscreen mode

The 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.

Leave, cancel, or delete — they're different

This is the distinction that catches people, because the three sound interchangeable and aren't:

  • 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.
  • 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.
  • 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.

From the CLI:

# End a live recording but keep the media
nylas notetaker leave <notetaker-id>

# Remove a notetaker and its media
nylas notetaker delete <notetaker-id>
Enter fullscreen mode Exit fullscreen mode

Reaching 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.

Capabilities and notes

Dimension Value Notes
Providers Zoom, Google Meet, Microsoft Teams Any meeting link from these three
Models Grant-scoped and standalone /v3/grants/{id}/notetakers vs /v3/notetakers
Capture toggles video, audio, transcription meeting_settings flags; transcription requires both recordings on
Media URLs Expiring Download promptly; store your own copy
Lifecycle scheduled → connecting → attending → complete Simplified CLI states; the API exposes more granular values

The 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.

Wrapping up

The 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.

Where to go next:


Written by Qasim Muhammad and Pouya Sanooei.

Top comments (0)