Agent Quickstarts
Quick copy/pasteable instructions for agents to implement transcription with bots
Create a bot and retrieve the final transcript after the meeting ends
# Recall.ai quickstart for agents: create a bot and retrieve the final transcript after the meeting ends
This quickstart is for agents building a Recall.ai integration on the v1.11 API that:
- sends a bot to a meeting
- retrieves the **final transcript after the meeting ends**
- returns both:
- the **original transcript payload**
- a **human-readable sentence-by-sentence transcript**
By default, this quickstart builds:
- a **direct Create Bot** flow
- a **webhook-driven completion flow** to track bot, recording, and transcript status
- **automatic transcript generation** by defining transcript configuration in the Create Bot request
- a **transcript retrieval helper** that:
- looks up the stored transcript state
- retrieves the transcript when it is done
- returns the transcript failure `sub_code` when it failed
- returns both the original transcript and a readable transcript
- an **optional endpoint** that calls the same transcript retrieval helper
This quickstart is intentionally for **post-meeting transcript retrieval**, not real-time transcription.
It can be used in any environment, but before going to production, if the workflow usually knows the meeting URL and start time ahead of time, you must implement **scheduled bots** separately. This quickstart does **not** cover scheduling.
Do **not** use this quickstart for:
- live transcript UX during the meeting
- real-time transcript streaming
- voice agents that need transcript chunks while the meeting is still happening
- scheduled bot workflows
- calendar-driven bot scheduling
- or any other real-time transcription or scheduling-specific use case
If the human asks for real-time transcription, the agent must say this quickstart does not apply, point them to [Bot Real-Time Transcription](https://docs.recall.ai/docs/bot-real-time-transcription), and abort this implementation.
If the human asks for scheduled bots or calendar-driven scheduling, the agent must say this quickstart does not cover scheduling, point them to [Creating and scheduling bots](https://docs.recall.ai/docs/creating-and-scheduling-bots), and abort this implementation.
---
## Outline
- [1. Use-case fit](#1-use-case-fit)
- [2. Mandatory acknowledgement](#2-mandatory-acknowledgement)
- [3. Human-required blocking setup](#3-human-required-blocking-setup)
- [4. Default implementation choices](#4-default-implementation-choices)
- [5. Minimal state model](#5-minimal-state-model)
- [6. Critical rules](#6-critical-rules)
- [7. Step 1: create the webhook endpoint first](#7-step-1-create-the-webhook-endpoint-first)
- [8. Step 2: create the bot](#8-step-2-create-the-bot)
- [9. Step 3: handle bot, recording, and transcript webhooks](#9-step-3-handle-bot-recording-and-transcript-webhooks)
- [10. Step 4: implement the transcript retrieval helper](#10-step-4-implement-the-transcript-retrieval-helper)
- [11. Required failure handling](#11-required-failure-handling)
- [12. Verification checklist](#12-verification-checklist)
- [13. Minimal implementation outline](#13-minimal-implementation-outline)
- [14. Required human-facing explanations](#14-required-human-facing-explanations)
---
## 1. Use-case fit
Use this quickstart when the human wants all of the following:
- a Recall.ai bot joins a meeting
- the app gets the **final transcript after the meeting ends**
- the app does **not** need transcript chunks during the meeting
- the app is able to rely on **webhooks**
- the app wants to retrieve the final transcript only after Recall marks it done
- the app wants either:
- a function that can retrieve the transcript after completion, or
- an endpoint that returns the transcript after completion
This quickstart uses **automatic transcript generation** by including transcript configuration in the Create Bot request. There is **no separate “start transcription” step** in this guide.
Stop and use a different guide if the human says any of the following:
- “I need transcript chunks during the call”
- “I need live captions”
- “I need a websocket or realtime webhook transcript stream”
- “I want to fetch transcript-so-far while the meeting is still in progress”
- “I need scheduled bots”
- “I need calendar integration”
- or any other real-time transcription or scheduling-specific use case
---
## 2. Mandatory acknowledgement
Do **not** start implementation until the human acknowledges the following.
Tell the human:
```text
This quickstart uses direct bot creation and is intended for development, testing, and truly last-minute meetings.
It does not cover scheduled bots or calendar-driven scheduling. In production, if meeting details are usually known ahead of time, you should implement scheduled bots instead so that your bots will join on time.
Ad-hoc bot creation can return HTTP 507 when temporary bot capacity is unavailable and a bot will not join your call, so the implementation in this guide includes the required retry behavior (every 30 seconds for up to 10 attempts) until you claim a bot or retries finish.
Just to be clear:
1. this guide does not cover scheduling
2. this guide is for direct bot creation + post-meeting transcript retrieval
3. scheduled bots should be implemented separately before using this as the default production path
Once you acknowledge with "yes", I will start.Wait for acknowledgement before continuing.
Reference:
3. Human-required blocking setup
The agent must stop and wait for completion or acknowledgement on each of these before proceeding.
3.1 Create a Recall API key
Tell the human:
Head to the Recall dashboard via one of the following links (create an account if needed):
+-----------------------------------------------------------------------------------+
| Recall Region | Dashboard URL |
+-----------------------------------------------------------------------------------+
| `us-west-2` | https://us-west-2.recall.ai/dashboard/developers/api-keys |
| `us-east-1` | https://us-east-1.recall.ai/dashboard/developers/api-keys |
| `eu-central-1` | https://eu-central-1.recall.ai/dashboard/developers/api-keys |
| `ap-northeast-1` | https://ap-northeast-1.recall.ai/dashboard/developers/api-keys |
+-----------------------------------------------------------------------------------+
Create a:
- Recall API key
- Workspace verification secret
Note down the following:
- Recall region
- API key
- Workspace verification secret
Let me know when this step is complete.Reference:
Wait for completion.
3.2 Create a stable public webhook endpoint
Tell the human:
Set up a stable public HTTPS base URL for local development or the target environment.
If you are developing locally, use a stable tunneling URL (for example, a static ngrok domain), not one that changes on restart.
You can create a static ngrok domain by following this guide:
https://docs.recall.ai/docs/local-webhook-development
Make sure the stable public URL is running and reachable.
Let me know when this step is complete.Reference:
Wait for completion.
3.3 Add the webhook endpoint in the Recall dashboard
Tell the human:
Head to the Recall webhooks dashboard for the same Recall region you chose earlier:
+------------------------------------------------------------------------+
| Recall Region | Dashboard URL |
+------------------------------------------------------------------------+
| `us-west-2` | https://us-west-2.recall.ai/dashboard/webhooks |
| `us-east-1` | https://us-east-1.recall.ai/dashboard/webhooks |
| `eu-central-1` | https://eu-central-1.recall.ai/dashboard/webhooks |
| `ap-northeast-1` | https://ap-northeast-1.recall.ai/dashboard/webhooks |
+------------------------------------------------------------------------+
Add the following webhook endpoint in the dashboard, where SERVER_DOMAIN is the stable public base URL you generated in the previous step:
`SERVER_DOMAIN/api/webhooks/recallai`
Subscribe to at least the following events:
- `bot.*`
- `recording.done`
- `recording.failed`
- `transcript.done`
- `transcript.failed`
Note down the following:
- Stable public webhook base URL
Let me know when this step is complete.Reference:
Wait for completion.
3.4 Provide the required values to the agent
The human must provide the required values to the agent.
Required values:
RECALL_REGION=...
RECALL_API_KEY=...
RECALL_WORKSPACE_VERIFICATION_SECRET=...
SERVER_DOMAIN=https://your-public-domain.exampleAgent behavior:
- the human provides the required values
- the agent sets the environment variables in the implementation/runtime environment
- the agent must not proceed until the human has provided all required values
Tell the human:
Please provide the following values so I can configure the environment for the implementation:
- RECALL_REGION
- RECALL_API_KEY
- RECALL_WORKSPACE_VERIFICATION_SECRET
- SERVER_DOMAIN
I will wait until all four values are available before continuing.Wait for completion.
4. Default implementation choices
Unless the human explicitly overrides them, use these defaults:
- transcript provider:
recallai_streaming - transcript language code:
auto - transcript mode:
prioritize_accuracy - diarization:
use_separate_streams_when_available=true
Important behavior:
- the transcript is automatically generated because it is defined in the Create Bot request
- there is no separate transcript-start API call in this guide
- once the transcript status becomes
transcript.done, the transcript is ready to retrieve - the transcript will not change after it reaches
transcript.done
This quickstart shows how to retrieve:
- the original transcript payload
- a readable transcript derived from the original payload
Reference:
5. Minimal state model
Store only the minimum state needed to drive the workflow.
5.1 Required fields
Persist at least:
bot_id(required; this can be the primary identifier for this quickstart)internal_lookup_key(optional; use this only if the customer already has a separate app-specific identifier)recording_id(nullable until known)transcript_id(nullable until known)bot_status(scheduleduntil first bot webhook, then latestbot.*status)recording_status(nullable until known, then latest stored recording event such asrecording.doneorrecording.failed)transcript_status(nullable until known, then latest stored transcript event such astranscript.doneortranscript.failed)transcript_failure_sub_code(nullable; set when transcript fails)
5.2 Status meanings
Use a simple model like this:
-
bot_statusscheduleduntil first bot status change webhook- then the latest bot event, for example:
bot.joining_callbot.in_waiting_roombot.in_call_not_recordingbot.recording_permission_allowedbot.recording_permission_deniedbot.in_call_recordingbot.call_endedbot.donebot.fatal
-
recording_statusnulluntil known- then the latest recording event that the app stores, for example:
recording.donerecording.failed
-
transcript_statusnulluntil known- then the latest transcript event that the app stores, for example:
transcript.donetranscript.failed
5.3 Why this model exists
This state model supports:
- webhook-driven progress tracking
- knowing whether the transcript is ready without polling
- retrieving the transcript by
bot_id - optionally retrieving it by an app-specific lookup key if the customer has one
- returning the failure
sub_codewhen transcript generation fails
6. Critical rules
These are required, not optional.
- Do not ever use polling to wait for bot, recording, or transcript completion.
- Do not implement this as a real-time transcription workflow.
- Do not add a separate “start transcription” step. In this guide, the transcript is automatically generated because it is configured in the Create Bot request.
- Do not go to production with direct/ad-hoc bot creation as the default path if meetings are usually known ahead of time. Implement scheduled bots separately.
- Do not use
localhost,127.0.0.1, or private callback URLs in Recall-facing configuration. - Do not use an unstable/ephemeral tunnel URL that changes on restart.
- Do not assume webhooks arrive only once.
- Do not assume webhook order is guaranteed.
- Do not block transcript updates on bot state or recording state being in any specific order first.
- Do not update unrelated artifact fields from a webhook event.
- bot events update bot fields
- recording events update recording fields
- transcript events update transcript fields
- Do not ignore
429. Respect theRetry-Afterheader. - Do not treat ad-hoc
507as final until the required retries are exhausted. - Do not fetch transcript data before
transcript.done. - Do not rely on webhook retries to drive application logic. Return
2xximmediately and do follow-up work separately.
7. Step 1: create the webhook endpoint first
Create the webhook route before creating bots.
Example route:
POST /api/webhooks/recallai7.1 Required webhook behavior
The webhook handler must:
- read the raw request body
- verify the Recall signature before trusting the payload
- identify the webhook event type
- update the relevant persistent state for that event type
- trigger any follow-up work separately if needed
- return
2xximmediately to prevent Recall from retrying - be idempotent
Important wording:
- do the follow-up work separately
- do not wait for all downstream processing to finish before returning
2xx
Reference:
7.2 Required event subscriptions
At minimum, listen to:
bot.*recording.donerecording.failedtranscript.donetranscript.failed
7.3 Required update behavior by event type
Update only the fields related to the webhook you received.
-
On
bot.*- update
bot_status - optionally store the bot event
sub_codefor debugging
- update
-
On
recording.done- set
recording_id - set
recording_status=recording.done
- set
-
On
recording.failed- set
recording_id - set
recording_status=recording.failed
- set
-
On
transcript.done- set
transcript_id - set
transcript_status=transcript.done
- set
-
On
transcript.failed- set
transcript_id - set
transcript_status=transcript.failed - set
transcript_failure_sub_codefrom the webhook payload
- set
Do not fetch the transcript body in the webhook handler.
8. Step 2: create the bot
Use the regional Create Bot endpoint from the correct region and define transcript configuration directly in the Create Bot request.
Example request:
curl --request POST \
--url "https://$RECALL_REGION.recall.ai/api/v1/bot/" \
--header "Authorization: Token $RECALL_API_KEY" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data '{
"meeting_url": "CONFIGURED_MEETING_URL",
"join_at": "CONFIGURED_JOIN_TIME_ISO8601",
"recording_config": {
"transcript": {
"provider": {
"recallai_streaming": {
"mode": "prioritize_accuracy",
"language_code": "auto"
}
},
"diarization": {
"use_separate_streams_when_available": true
}
}
}
}'Because transcript configuration is included in this request, the transcript will be generated automatically after the meeting completes. There is no separate transcript-start step.
8.1 Required persistence after Create Bot succeeds
Persist at least:
bot_idbot_status=scheduled
If the app has its own meeting identifier, it may also store:
internal_lookup_key
For this quickstart, bot_id is sufficient as the required unique identifier.
Do not continue as if the bot exists unless Create Bot returned a successful response.
Reference:
9. Step 3: handle bot, recording, and transcript webhooks
The webhook layer is the source of truth for completion state.
When a webhook is received:
- verify the request
- update only the fields related to that webhook event
- trigger any follow-up work separately if needed
- return
2xximmediately
9.1 Verify the webhook request
You can see this guide for how to verify requests from Recall using Recall’s HMAC signature verification method:
9.2 Bot webhook schema
The bot webhook schema follows:
{
"event": "bot.\*",
"data": {
"data": {
"code": "string",
"sub_code": "string | null",
"updated_at": "string"
},
"bot": {
"id": "string",
"metadata": {}
}
}
}Required behavior:
- persist the latest bot state needed for debugging and product UX
- do not hardcode the implementation to only one tiny subset of bot states
At minimum, the app should be able to tell whether the bot:
- is scheduled
- is in the waiting room
- is in the meeting and not recording
- is in the meeting recording
- has completed the call
- has fatally failed
9.3 Recording webhook schema
The recording webhook schema follows:
{
"event": "recording.done | recording.failed",
"data": {
"data": {
"code": "string",
"sub_code": "string | null",
"updated_at": "string"
},
"recording": {
"id": "string",
"metadata": {}
},
"bot": {
"id": "string",
"metadata": {}
}
}
}Required behavior:
-
if
event=recording.done- set
recording_id - set
recording_status=recording.done
- set
-
if
event=recording.failed- set
recording_id - set
recording_status=recording.failed
- set
9.4 Transcript webhook schema
The transcript webhook schema follows:
{
"event": "transcript.done | transcript.failed",
"data": {
"data": {
"code": "string",
"sub_code": "string | null",
"updated_at": "string"
},
"transcript": {
"id": "string",
"metadata": {}
},
"recording": {
"id": "string",
"metadata": {}
},
"bot": {
"id": "string",
"metadata": {}
}
}
}Required behavior:
-
if
event=transcript.done- set
transcript_id - set
transcript_status=transcript.done
- set
-
if
event=transcript.failed- set
transcript_id - set
transcript_status=transcript.failed - set
transcript_failure_sub_code
- set
9.5 Out-of-order webhook handling
Webhook order should not be assumed.
Required behavior:
- rely on the event payload you received
- update only the related artifact fields
- do not require some other artifact to already be in a specific state before updating the current one
Examples:
- if a
transcript.doneevent arrives, update transcript fields even if the app has not yet seen the other final events yet - if a
recording.failedevent arrives, update recording fields even if transcript fields are still null - if duplicate deliveries arrive and you have already safely applied the same state update, returning early is fine
Reference:
10. Step 4: implement the transcript retrieval helper
Implement a helper function that returns the transcript result for a bot.
This helper can be used in either of these ways:
- from a background process after
transcript.doneortranscript.failed - from an API endpoint when a client asks for the transcript
The point of the helper is to retrieve the transcript. What the application does next with the transcript is use-case specific.
10.1 Required helper behavior
The helper must:
- look up the bot by
bot_id - inspect stored
transcript_status,transcript_id, and related transcript fields - return the current status immediately if the transcript is not yet terminal
- return a failure response if the transcript failed
- if
transcript_idis present and the transcript is done, retrieve the transcript - return both:
- the original transcript payload
- a readable transcript
Do not poll Recall from this helper.
10.2 Helper behavior by transcript state
If transcript_status or transcript_id is null
transcript_status or transcript_id is nullReturn a non-terminal status response.
Example:
{
"status": {
"code": "pending",
"sub_code": null,
"message": "The transcript has not been processed yet"
},
"raw_transcript": null,
"readable_transcript": null
}If transcript_status = "transcript.failed"
transcript_status = "transcript.failed"Return a failure response using the stored sub_code when available.
Example:
{
"status": {
"code": "failed",
"sub_code": "zoom_global_captions_disabled",
"message": "Transcript generation failed"
},
"raw_transcript": null,
"readable_transcript": null
}If transcript_status = "transcript.done"
transcript_status = "transcript.done"Retrieve the transcript and return both formats.
Recommended sequence:
- use the stored
transcript_id - retrieve the transcript by
transcript_id - fetch the
download_urlfrom the transcript object - parse the downloaded transcript payload as the original transcript
- convert it into a readable transcript
- return both
Example:
{
"status": {
"code": "done",
"sub_code": null,
"message": null
},
"raw_transcript": [...],
"readable_transcript": [...],
}10.3 List transcripts for a recording
If needed, call the List Transcript endpoint for a recording using the recording ID to get the list of transcripts.
Reference request:
curl --request GET \
--url "https://$RECALL_REGION.recall.ai/api/v1/transcript/?recording_id=RECORDING_ID" \
--header "Authorization: Token $RECALL_API_KEY" \
--header "accept: application/json"10.4 Retrieve the transcript by transcript ID
Once you have the transcript ID, call Retrieve Transcript using that ID, then read transcript.data.download_url from the response and fetch the payload from that URL.
10.5 Return both transcript formats
The helper should return:
raw_transcript: the original downloaded transcript payloadreadable_transcript: a sentence-based readable transcript derived from the original payload
For the readable transcript, you can use the helper function here or implement equivalent logic in your own app:
Example success response:
{
"status": {
"code": "done",
"sub_code": null,
"message": null
},
"raw_transcript": [
{
"words": [
{
"text": "Hello.",
"start_timestamp": {
"relative": 0.0
},
"end_timestamp": {
"relative": 0.5
}
}
],
"language_code": "en",
"participant": {
"id": 1,
"name": "Speaker 1",
"is_host": false,
"platform": "zoom",
"extra_data": {},
"email": null
}
}
],
"readable_transcript": [
{
"speaker": "Speaker 1",
"paragraph": "Hello.",
"start_timestamp": {
"relative": 0.0,
"absolute": null
},
"end_timestamp": {
"relative": 0.5,
"absolute": null
}
}
]
}11. Required failure handling
This section is mandatory.
11.1 HTTP 507 on ad-hoc bot creation
Ad-hoc bot creation can return 507 when temporary bot capacity is unavailable.
Required behavior:
- retry every 30 seconds
- retry for up to 10 attempts
- after the 10th failed attempt, stop and surface a clear failure
Required failure message:
Ad-hoc bot capacity was unavailable after 10 retries spaced 30 seconds apart. For production, if meeting details are known ahead of time, implement scheduled bots instead of relying on ad-hoc bot creation as the default path.Reference:
11.2 HTTP 429 rate limiting
For any Recall API request that returns 429:
- read the
Retry-Afterheader - wait exactly that long before retrying
- add small jitter to space out requests
- do not use tight retry loops
- do not ignore the header
11.3 No polling
Do not poll Recall to wait for:
- bot completion
- recording completion
- transcript completion
Recall is designed for an async event-driven implementation. The transcript becomes available after Recall sends transcript.done. Wait for the webhook and then retrieve it when needed.
If you are not receiving a webhook, ensure that:
- the bot has joined the call and completed the recording
- the webhooks dashboard in the set region has the webhook endpoint configured and is not disabled
- the webhooks dashboard in the set region has the correct webhook domain and endpoint configured
11.4 Invalid callback URLs
If Recall-facing configuration uses any of the following:
localhost127.0.0.1- private LAN URLs
- a tunnel URL that changes on restart
stop and fix it before testing.
Required explanation:
Recall must be able to reach a stable public HTTPS callback URL.
Localhost and private addresses are not reachable by Recall. Unstable tunnel URLs that change on restart will break webhook delivery.
In some setups, invalid callback values can also result in HTTP 403 errors from CloudFront. Use a stable public HTTPS URL instead.11.5 Webhook idempotency, duplicates, and out-of-order delivery
Webhook handlers must tolerate:
- duplicate deliveries
- out-of-order delivery
- repeated terminal events
Required behavior:
- verify the webhook
- update only the fields for the artifact in that event
- make repeated updates safe
- do not require a specific prior webhook to have been seen first
Terminal states to treat carefully:
-
Bots:
bot.donebot.fatal
-
Recording:
recording.donerecording.failed
-
Transcript:
transcript.donetranscript.failed
12. Verification checklist
Do not call the integration complete until all of these pass.
12.1 Functional verification
- create a test meeting
- create the bot
- confirm Create Bot returns an
id - confirm the bot joins the meeting via the
bot.in_call_recordingwebhook - end the meeting
- confirm the app receives bot webhook event
bot.done - confirm the app receives either:
recording.done, orrecording.failed
- confirm the app receives either:
transcript.done, ortranscript.failed
- confirm the database stores:
bot_idrecording_idtranscript_id- bot/recording/transcript statuses
- transcript failure
sub_codewhen relevant
- call the transcript retrieval helper or the optional endpoint
- confirm that when transcript succeeds, the app returns:
- the original transcript payload
- the readable transcript
- confirm that when transcript fails, the app returns the failure
sub_code
13. Minimal implementation outline
A minimal implementation should look like this:
- get human acknowledgement that this guide is only for direct bot creation + post-meeting transcript retrieval
- wait for all human-required setup to be completed
- configure the environment variables
- create the webhook endpoint
- verify webhook signatures
- persist
bot_idand statuses - create the bot with transcript config included
- update artifact fields from webhook events
- implement a transcript retrieval helper that:
- checks stored status
- returns failure info when transcript failed
- retrieves the transcript when transcript is done
- returns both original and readable transcript
- optionally expose an endpoint that returns the helper result
- verify success and failure paths
14. Required human-facing explanations
14.1 If the human asks for real-time transcription
Use this wording:
I cannot implement that with this quickstart because this guide is only for final transcript retrieval after the meeting ends. For real-time transcript chunks, live captions, or during-call transcript access, use the Bot Real-Time Transcription guide instead.14.2 If the human asks for scheduled bots or calendar-driven scheduling
Use this wording:
This quickstart does not cover scheduled bots or calendar-driven scheduling. It only covers direct bot creation plus post-meeting transcript retrieval. For production scheduling, consult the Creating and Scheduling Bots documentation or the separate scheduling guide.14.3 If the human does not yet have a stable public callback URL
Use this wording:
I cannot complete a reliable implementation until you provide a stable public HTTPS webhook URL. If you are developing locally, use a stable tunnel URL rather than one that changes on restart.14.4 If the human asks what they can do after retrieving the transcript
Use this wording:
This quickstart shows how to retrieve both the original transcript payload and a readable transcript after the meeting ends. What you do next depends on your product: you can print it, return it from an endpoint, store it, summarize it, analyze it, or push it to another system.14.5 If the human asks whether they must use an internal lookup key
Use this wording:
No. For this quickstart, bot_id is enough as the required unique identifier. If your application already has its own meeting or run identifier, you may also store that as an optional internal lookup key.14.6 If the human asks about the 507 error
Use this wording:
HTTP 507 during ad-hoc bot creation means temporary bot capacity is unavailable right now. Retry every 30 seconds for up to 10 attempts; if all 10 attempts fail, stop and surface a clear failure, and for production flows with known meeting details use scheduled bots instead of relying on ad-hoc creation as the default path.14.7 If the human asks about the 403 or CloudFront error
Use this wording:
An HTTP 403 or CloudFront error usually means the callback URL or webhook configuration is not valid for Recall to reach. Localhost, `127.0.0.1`, private LAN addresses, and unstable tunnel URLs will not work; switch to a stable public HTTPS URL and verify the webhook endpoint is correctly configured and enabled in the dashboard for the correct region.Updated about 1 hour ago