> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aefer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Send bots

> Send the notetaker to a call yourself: from a button for an ad-hoc call, or from your backend when you already know which meetings to record.

A bot is one recording of one meeting. You send one when \_æfer doesn't learn about
the call from a calendar: your user pastes a link, or your backend already decides which
meetings to record. Either way, the bot belongs to a [meeting](https://docs.aefer.ai/guides/meetings.md) and the
results are on that meeting.

## From a button

A "Send bot to meeting" button lets your user paste a link to a call that is on no
calendar.

![A Send bot to meeting button in the meetings screen opens a form with a meeting link and a Send bot button. Notes: Send bot is POST /bots with member\_id and meeting\_url; the results come from GET /meetings/\{meeting\_id}/results.](https://mintcdn.com/meeting-kit/WXugirGtEPkJGTVC/images/diagrams/send-bot.svg?fit=max\&auto=format\&n=WXugirGtEPkJGTVC\&q=85\&s=b5a06d9159f390757a668060e2be74b1)

```bash theme={null}
curl -X POST "https://api.aefer.ai/api/v1/bots" \
  -H "Authorization: $AEFER_API_KEY" \
  -H "Aefer-Version: 2026-11-13" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_id": "wks_01k6a2r9s8x7c2dvq3m5n6p4ab",
    "member_id": "mem_01k69x9s8x7c2dvq3m5n6p4ab",
    "meeting_url": "https://meet.google.com/abc-defg-hij"
  }'
```

```json theme={null}
{
  "id": "bot_01k6b0r9s8x7c2dvq3m5n6p4ab",
  "object": "bot",
  "meeting_id": "mtg_01k6b1r9s8x7c2dvq3m5n6p4ab",
  "meeting_url": "https://meet.google.com/abc-defg-hij",
  "join_at": null,
  "status": "queued",
  "…": "…"
}
```

Without `join_at` the notetaker joins as soon as it can: within seconds, and always within
three minutes. `member_id` makes the meeting this user's, so it shows in their
[meetings list](https://docs.aefer.ai/guides/meetings.md) with `source: "bot"`.

`member_id` comes from [connecting a calendar](https://docs.aefer.ai/guides/calendar-connection.md). For a user
who never connected one, create their member once and store the `mem_` id:

```bash theme={null}
curl -X POST "https://api.aefer.ai/api/v1/members" \
  -H "Authorization: $AEFER_API_KEY" \
  -H "Aefer-Version: 2026-11-13" \
  -H "Content-Type: application/json" \
  -d '{ "workspace_id": "wks_01k6a2r9s8x7c2dvq3m5n6p4ab", "email": "ana@acme.com", "name": "Ana García" }'
```

Sending the same email again returns the same member, unchanged: its `name` and `metadata`
stay as they are. Change them with `PATCH /members/{id}`.

## From your backend

If your product already knows which meetings to record, from your own calendar sync or
your users' choices, your backend sends a bot per meeting and keeps it in step with your
calendar.

### Schedule it

Set `join_at` to the meeting's start, as an ISO 8601 time with an offset:

```bash theme={null}
curl -X POST "https://api.aefer.ai/api/v1/bots" \
  -H "Authorization: $AEFER_API_KEY" \
  -H "Aefer-Version: 2026-11-13" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_id": "wks_01k6a2r9s8x7c2dvq3m5n6p4ab",
    "member_id": "mem_01k69x9s8x7c2dvq3m5n6p4ab",
    "meeting_url": "https://zoom.us/j/123456789",
    "join_at": "2026-10-13T14:00:00Z",
    "participants": [
      { "name": "Ana García", "email": "ana@acme.com" },
      { "name": "Leo Park", "email": "leo@client.com" }
    ],
    "metadata": { "calendar_event_id": "evt_98741" }
  }'
```

The bot stays `queued` until `join_at`, then joins on time. Send it as soon as the meeting
is on your calendar, and at least ten minutes before it starts: scheduled bots are
guaranteed a seat. `join_at` must be in the future.

* **`participants`** are the people you expect, with names and emails. \_æfer uses
  them to name the [speakers](https://docs.aefer.ai/guides/results.md#what-each-part-gives-you) instead of
  "Speaker 1". Up to 100.
* **`metadata`** holds your own ids: flat string values, merged on update. It comes back on
  the bot and the `bot.*` webhooks. It is not copied to the meeting: set the meeting's own
  with `PATCH /meetings/{id}`.

### One bot per meeting

When several of your users are invited to the same meeting, you want one bot, not one per
attendee. A request with the same `workspace_id`, `meeting_url` and `join_at` as a bot that
is still `queued`, `joining` or `in_call` returns that bot instead of sending a second one.

* Send the meeting's start as `join_at`, the same value for every attendee.
* Send the join link exactly as it appears in the invite.
* It is per workspace: two customers who meet each other get one bot each.

The first request wins: its participants and metadata stay on the bot. This also makes
creation safe to retry: if a `POST` times out, send it again and you get the bot that was
created.

The meeting belongs to the member of that first request. A request naming a different
`member_id` answers `409 bot_already_recording`, with the id of the bot already there: send
the bot once per meeting, with the member who owns it, such as the organizer.

### Keep it in step with your calendar

Store the bot `id` next to your meeting, then:

| When your calendar says… | Do |
| - | - |
| A meeting to record appears | `POST /bots` with `join_at` = start. Store the `id`. |
| It moves, or its link changes | `PATCH /bots/{id}` with the new `join_at` or `meeting_url`. |
| The attendee list changes | `PATCH /bots/{id}` with the new or renamed `participants`. |
| It is cancelled | `DELETE /bots/{id}`. |
| Another attendee has the same meeting | Nothing: it is the same bot. |

`join_at` and `meeting_url` can change until the bot starts joining; `participants` until
the call ends; `metadata` until the bot ends. Past that, `PATCH` answers
`409 bot_not_editable`. Cancelling a bot that is still `queued` or `joining` makes it
`cancelled`; cancelling a bot in the call makes it leave, and what it recorded is kept. Once a day, re-read the next seven days of your calendar and reconcile them with your
bots: calendar notifications get lost. List the bots you sent for a member with
[`GET /bots?workspace_id=…&member_id=…`](https://docs.aefer.ai/api-reference/bots/list-bots), narrowed with
`created_after` or `meeting_id`, and compare them with your calendar: send the missing ones, and
cancel the ones whose meeting is gone.

## How the bot looks and records

A bot follows the workspace's and the member's [recording choices](https://docs.aefer.ai/guides/meeting-settings.md#record-my-meetings)
and the workspace's [branding](https://docs.aefer.ai/guides/branding.md). Override them for one bot:

| Field | What it sets |
| - | - |
| `bot_name` | The notetaker's name in the call, up to 64 characters. |
| `bot_avatar` | A base64 JPEG up to 256 KB, shown as its video tile. Never returned. |
| `welcome_message` | The chat message it posts when it joins. |
| `recording_mode` | `audio_and_video` or `audio_only`. |
| `recording_start` | `on_join`, or `on_command` to wait until someone in the call asks it to. |
| `silence_alert` | Post a message after a long silence. |
| `language` | The transcription language: a tag such as `es`, `auto` or `multi`: see [Languages](https://docs.aefer.ai/api-reference/languages.md). |
| `summary_language` | The summary's language. Defaults to the transcript's. |
| `summary_template` | A built-in template such as `discovery_call`, or a `tpl_` id. |

These values become the meeting's own settings, so the bot and its meeting never disagree.
Before the call, change the language, summary or silence alert with `PATCH /settings/{id}`, the
`settings_id` of the meeting's own assignment in its `settings.assignments`. See
[Company settings](https://docs.aefer.ai/guides/settings.md).

Your users record calls with people outside your product. Tell them, and their guests, that
calls are recorded.

## What happens next

![A bot moves from queued to joining, in\_call, processing, and completed. It can also end as failed or cancelled.](https://mintcdn.com/meeting-kit/WXugirGtEPkJGTVC/images/diagrams/notetaker-lifecycle.svg?fit=max\&auto=format\&n=WXugirGtEPkJGTVC\&q=85\&s=3d4f503afaad38e0afe84e1bccd4b8ab)

| `status` | Meaning |
| - | - |
| `queued` | Waiting to join, now or at `join_at`. A calendar bot stays queued, under its id, while a calendar or settings change sends it again. |
| `joining` | Joining the call, or waiting in the waiting room for someone to admit it. |
| `in_call` | In the call. It records unless it waits for a command (`recording_start: "on_command"`) or the call does not let it record yet. |
| `processing` | The call ended; the recording is being processed. |
| `completed` | Done: the results are on the meeting. |
| `failed` | Ended without a result. `reason` says why: see [the bot object](https://docs.aefer.ai/api-reference/bots/object.md). |
| `cancelled` | Cancelled before it finished. Final: a cancelled bot is never sent again. |

The `bot.*` webhooks follow these steps. The results are on the meeting: when
`meeting.finished` fires for the bot's `meeting_id`, read
[`GET /meetings/{id}/results`](https://docs.aefer.ai/guides/results).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.