{"slug": "meeting-follow-ups-without-a-human-in-the-loop", "title": "Meeting Follow-Ups Without a Human in the Loop", "summary": "Nylas released a meeting follow-up recipe that automates the process of sending recaps after calls. A recording bot joins the meeting, generates transcription, summary, and action items, and emails the recap to all attendees via a webhook handler. The system supports Google Meet, Microsoft Teams, and Zoom, and can use an Agent Account for a dedicated sender address.", "body_md": "Every team has a version of this story. The meeting ends, somebody says \"I'll send the recap,\" everyone nods, and three days later a decision nobody wrote down gets re-litigated from memory. It isn't laziness — writing up a meeting takes ten minutes that nobody has between back-to-back calls, so the follow-up quietly stops happening.\n\nThe [meeting follow-up recipe](https://developer.nylas.com/docs/cookbook/use-cases/act/automate-meeting-follow-ups/) removes the volunteer from the equation. A recording bot joins the call, transcription and summarization happen automatically, and a webhook handler emails the recap — summary plus action items — to everyone who was on the calendar invite. If you give that handler its own [Agent Account](https://developer.nylas.com/docs/v3/agent-accounts/) (currently in beta), the recap comes from a dedicated address like `meetings@yourcompany.com`\n\nand replies thread back into a mailbox your code can read.\n\nThe pipeline is short: Notetaker joins the meeting → the recording gets processed → `notetaker.media`\n\nfires → your handler downloads the summary and action items, looks up the event's attendees → sends the recap through the send endpoint. Each step fails independently, so one broken attendee lookup doesn't stall the rest. Subscribe to `notetaker.meeting_state`\n\nalongside the media trigger and you'll also know when the bot joins and leaves each call, which makes failures visible instead of silent.\n\nInviting the bot is a single request:\n\n```\ncurl --request POST \\\n  --url \"https://api.us.nylas.com/v3/grants/<NYLAS_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    \"meeting_settings\": {\n      \"audio_recording\": true,\n      \"transcription\": true,\n      \"summary\": true,\n      \"action_items\": true\n    },\n    \"name\": \"Meeting Notetaker\"\n  }'\n```\n\nGoogle Meet, Microsoft Teams, and Zoom all work — the provider is detected from the link. Omit `join_time`\n\nand the bot joins immediately; pass a Unix timestamp for scheduled meetings.\n\nThe interesting work happens when `notetaker.media`\n\narrives with `state: \"available\"`\n\n. The handler chains three lookups — media files, then the Notetaker record (which links to the calendar event), then the event itself for the attendee list — and finishes with a send:\n\n``` js\napp.post(\"/webhooks/nylas\", async (req, res) => {\n  const { type, data } = req.body;\n  if (type !== \"notetaker.media\" || data.object.state !== \"available\") {\n    return res.status(200).send(\"OK\");\n  }\n\n  const { media, id: notetakerId } = data.object;\n\n  // 1. Download summary + action items (URLs expire in 60 minutes)\n  const [summary, actionItems] = await Promise.all([\n    fetch(media.summary).then((r) => r.json()),\n    fetch(media.action_items).then((r) => r.json()),\n  ]);\n\n  // 2. Find the linked calendar event through the Notetaker record\n  const notetaker = await nylasGet(`/notetakers/${notetakerId}`);\n  const eventId = notetaker.data.event?.event_id;\n  const calendarId = notetaker.data.calendar_id;\n  if (!eventId) return res.status(200).send(\"OK\"); // ad-hoc meeting, no invite\n\n  // 3. Pull attendees off the event\n  const event = await nylasGet(`/events/${eventId}?calendar_id=${calendarId}`);\n  const attendees = event.data.participants || [];\n\n  // 4. Email the recap to everyone who was invited\n  await sendFollowUpEmail(event.data.title, summary, actionItems, attendees);\n  res.status(200).send(\"OK\");\n});\n```\n\nNote the early return when there's no linked event: a bot invited to an ad-hoc call by raw meeting link has no calendar event and therefore no attendee list, so the recap has nowhere to go. And before sending, it's worth verifying the event wasn't cancelled — a recap for a meeting that didn't happen is a special kind of confusing.\n\nThe recap itself sends from whatever grant you use on the send endpoint. That's exactly where an Agent Account slots in: the email arrives from `meetings@yourcompany.com`\n\n, and when someone replies \"actually, I disagree with action item 2,\" that reply lands in a mailbox your code reads — not in a service account nobody checks.\n\nGeneric AI summaries are where automation reputations go to die. The API takes `custom_instructions`\n\nfor both the summary and the action items, and this is the lever that matters. Tell it \"focus on decisions made and open questions, keep it under 200 words\" and \"assign each action item to the person responsible with a suggested deadline,\" and the recap reads like something a competent PM wrote rather than a transcript blender.\n\nBoth deadlines come straight from the docs and both will burn you if you ignore them:\n\nThere's a third timer on the front end too: the bot joins as a non-signed-in participant, and if a lobby or waiting room blocks it for 10 minutes, it gives up with `failed_entry`\n\n. Fully automated workflows need lobby bypass configured on the meeting provider.\n\nManually inviting the bot per meeting defeats the point. Calendar sync rules fix that — set rules on a calendar and a Notetaker gets scheduled for every matching event automatically:\n\n```\ncurl --request PUT \\\n  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars/<CALENDAR_ID>' \\\n  --header 'Authorization: Bearer <NYLAS_API_KEY>' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"notetaker\": {\n      \"meeting_settings\": {\n        \"summary\": true,\n        \"action_items\": true,\n        \"transcription\": true,\n        \"audio_recording\": true\n      },\n      \"name\": \"Meeting Notetaker\",\n      \"rules\": {\n        \"event_selection\": [\"external\"],\n        \"participant_filter\": { \"participants_gte\": 3 }\n      }\n    }\n  }'\n```\n\nThat rule reads \"join all external meetings with three or more participants\" — internal one-on-ones stay unrecorded, customer calls get recapped. Rules evaluate each event independently, so a matching recurring meeting gets a bot on every occurrence (you can override individual occurrences at the event level). Combined with the webhook handler, the loop runs without any human trigger: meeting happens, recap arrives. A bonus over manual invites: when an event is cancelled, calendar sync cancels its Notetaker too, so the skip-cancelled-meetings check comes free.\n\nThree production details worth knowing. Processing takes a few minutes after the meeting ends, so recaps aren't instant — just faster than any human. The bot leaves after 5 minutes of continuous silence by default, tunable from 10 to 3600 seconds via `leave_after_silence_seconds`\n\n. And every POST to the notetakers endpoint creates a *new* bot — there's no deduplication — so a retry loop without idempotency checks puts two bots in the same call, which is exactly as embarrassing as it sounds.\n\nTake your next recurring team meeting, invite the bot manually with the curl above, and let the webhook handler email you alone as the test recipient. If the summary quality holds up for a week, flip on calendar sync and widen the recipient list to actual attendees. Then tell me: would your team trust an auto-recap enough to stop taking their own notes?", "url": "https://wpnews.pro/news/meeting-follow-ups-without-a-human-in-the-loop", "canonical_source": "https://dev.to/qasim157/meeting-follow-ups-without-a-human-in-the-loop-1m84", "published_at": "2026-06-13 11:21:51+00:00", "updated_at": "2026-06-13 11:47:41.534320+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents"], "entities": ["Nylas", "Google Meet", "Microsoft Teams", "Zoom"], "alternates": {"html": "https://wpnews.pro/news/meeting-follow-ups-without-a-human-in-the-loop", "markdown": "https://wpnews.pro/news/meeting-follow-ups-without-a-human-in-the-loop.md", "text": "https://wpnews.pro/news/meeting-follow-ups-without-a-human-in-the-loop.txt", "jsonld": "https://wpnews.pro/news/meeting-follow-ups-without-a-human-in-the-loop.jsonld"}}