Skip to content
Skip to main content

Tell the host when Notetaker can't join

Last updated:

Notetaker joins meetings as a guest. If the meeting has a waiting room, or is limited to people in the organization, somebody has to let it in. When nobody does within 10 minutes of the join time it gives up, and without a notice the host finds out when they look for a recording that doesn’t exist.

A notetaker.meeting_state workflow tells them while the meeting is still happening, when they can still fix it.

An email telling the host the Notetaker couldn't join, explaining the waiting room cause, with the meeting link, status, and reason

On every change to the Notetaker’s state during a meeting. The payload carries 2 fields that together say what happened: status holds one of 4 top-level states, and meeting_state holds one of 20 sub-states explaining the reason.

statusMeans
connectingJoining the meeting
attendingIn the meeting
disconnectedNo longer in the meeting
failed_entryNever got in

meeting_state is where the reason lives, and the 20 values group into 5 kinds of outcome.

Groupmeeting_state valuesWho can fix it
Waiting or refused entrywaiting_for_entry, entry_denied, admission_timeout, sign_in_required, meeting_capacity_reachedThe host, during the meeting
Bad link or codebad_meeting_code, bad_meeting_link, cannot_joinWhoever scheduled it
Ran normallyrecording_active, meeting_ended, no_participants, no_activityNobody, this is the happy path
Removed from the meetingkicked, api_request, cancelledDepends on who removed it
Something brokenetwork_error, no_response, error, internal_error, unknownYou, from the logs

The first 2 groups are the ones worth emailing about, because somebody can act on them while the meeting is still running. admission_timeout and waiting_for_entry are the most common: Notetaker joins as a guest, so a waiting room or an org-only meeting needs somebody to admit it, and it gives up 10 minutes after the scheduled join time.

sign_in_required and entry_denied are worth separating in your copy. The first means the platform wants an account, the second means a human refused the bot, and the advice differs.

One per state change. A workflow on notetaker.meeting_state sends on every transition, including connecting and attending on a meeting that went fine.

Two ways to handle that:

Write the template to suit every state. The template in this recipe branches on status and meeting_state with the eq helper, so each message describes what actually happened: joining, recording, left, or couldn’t get in. This suits an internal or operations address, or a host who wants live updates.

Use the webhook to send only on failures. Subscribe to notetaker.meeting_state, check status in your handler, and send only on failed_entry through transactional send with the same template. Use this for customer-facing mail where one message per meeting is the right amount.

Explain the cause, because failed_entry and admission_timeout mean nothing to the person reading them. The host doesn’t know Notetaker joins as a guest, and will assume the integration is broken rather than that a waiting room blocked it.

For a waiting-room failure, the template states three things: the recording doesn’t exist, the Notetaker was waiting to be admitted, and it waited 10 minutes. The fix is for the host to admit the bot next time. A bad link or a sign-in requirement gets its own sentence, because the fix there is on the calendar event or the meeting settings instead.

Include the meeting link so the host can jump back in if the call is still running. meeting_link is in the payload, and on a 60-minute meeting a notice at minute 10 still leaves time to admit the bot and capture what remains.

Point an application-level workflow at notetaker.meeting_state.

curl -X POST 'https://api.us.nylas.com/v3/workflows' \
-H "Authorization: Bearer $NYLAS_API_KEY" -H 'Content-Type: application/json' \
-d '{
"name": "Notetaker join failure",
"trigger_event": "notetaker.meeting_state",
"template_id": "<TEMPLATE_ID>",
"delay": 0,
"is_enabled": true,
"from": { "email": "[email protected]", "name": "Your Company" }
}'

Use delay: 0. A notice about a meeting in progress is worthless five minutes late.

The notetaker.meeting_state payload has no participant list. Send a test to confirm which address receives the message before you enable this for customers.

The template picks its wording with eq, checking status first and then meeting_state for the detail. Handlebars {{else if}} keeps the chain flat:

{{#if (eq status "failed_entry")}}
{{#if (eq meeting_state "bad_meeting_link")}}The meeting link didn't work.
{{else if (eq meeting_state "entry_denied")}}Somebody declined to admit the Notetaker.
{{else}}The Notetaker was waiting to be admitted.
{{/if}}
{{else if (eq status "disconnected")}}
The Notetaker has left the meeting.
{{else if (eq status "attending")}}
The Notetaker is in the meeting and recording.
{{else}}
The Notetaker is joining.
{{/if}}

eq on a missing top-level field is false rather than an error, so a transition without meeting_state falls through to the {{else}} branch. There’s no or helper, so each value you want to handle gets its own {{else if}}.

The Details table prints status and meeting_state as they arrive, each in its own guard:

{{#if status}}{{status}}{{/if}}
{{#if meeting_state}}{{meeting_state}}{{/if}}

The raw values are there on purpose. failed_entry and waiting_for_entry are what a host will paste into a support conversation, and matching the string they saw to the string in your logs saves a round of questions.

meeting_provider is guarded inline so the sentence reads correctly whether or not it’s present:

your {{#if meeting_provider}}{{meeting_provider}} {{/if}}meeting

With the provider it reads “your Google Meet meeting”, without it “your meeting”. The trailing space inside the guard is deliberate.

Render it with POST /v3/templates/render once for each status value, and once with only id present. Notetaker troubleshooting covers the join failures this email reports.