Meeting transcripts
Fetch the transcript, chat log, and recording-bot status for a scheduled meeting. Meeting transcripts are produced by Cirrus's meeting recording bots; every transcript is scoped to exactly one meeting and one org.
All endpoints require the transcripts:read scope. All hang off the /scheduled-meetings/{meetingId}/ prefix — there's one transcript per meeting and the meeting id is the only useful lookup key.
Endpoints on this page
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1alpha1/scheduled-meetings/{meetingId}/transcript | Full transcript for a meeting. Time-range windowing via query params. Details ↓ |
GET | /api/v1alpha1/scheduled-meetings/{meetingId}/transcript/chunks | Paginated chunk-by-chunk stream for very long meetings or incremental processing. Details ↓ |
GET | /api/v1alpha1/scheduled-meetings/{meetingId}/chat | Meeting-platform chat messages captured alongside the transcript. Details ↓ |
GET | /api/v1alpha1/scheduled-meetings/{meetingId}/bot | Recording-bot lifecycle status — did the bot join, is it recording, did it fail. Details ↓ |
Get a transcript
GET /api/v1alpha1/scheduled-meetings/{meetingId}/transcript
Full transcript by default. Optional time-range windowing keeps responses manageable on very long meetings.
Query parameters
| Parameter | Type | Description |
|---|---|---|
startTime | number | Seconds from meeting start. Omit to start at 0. |
endTime | number | Seconds from meeting start. Omit to include through the end. |
language | string | (optional) ISO code (e.g., en). Filters to chunks in that language. |
Response
See the Transcriptmodel model for the full field reference.
Notable fields:
hostId— populated asusr_...when the speaker matches a Cirrus user (host or assignee).nullwhen the speaker is an attendee, third party, or otherwise unresolved. Do not treat missinghostIdas "speaker not in Cirrus"; treat it as "we couldn't cross-reference confidently."compiledText— every chunk'stextjoined with speaker prefixes. Convenience field for piping directly into an LLM prompt.words[]— word-level timing. Use for search-highlight UIs that jump to the exact word offset in the underlying video/audio.isFinal— alwaystrue. Interim (in-flight) chunks that get overwritten are never emitted through this endpoint.
Errors
404 meeting-not-found— meeting id unknown or belongs to another org404 transcript-not-available— meeting exists but no bot was configured, or the bot didn't record (host declined, meeting canceled, etc.)410 transcript-expired— transcript is older than the 12-month retention window422 transcript-window-invalid—startTime > endTime, orendTimepast the meeting'sdurationSeconds
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/mtg_01H8YKQ2N9MTGXYZ/transcript?startTime=0&endTime=1800" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
meeting_id = "mtg_01H8YKQ2N9MTGXYZ"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meeting_id}/transcript",
headers={"X-Cirrus-Api-Key": token},
params={"startTime": 0, "endTime": 1800},
)
response.raise_for_status()
transcript = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var meetingId = "mtg_01H8YKQ2N9MTGXYZ";
var query = HttpUtility.ParseQueryString(string.Empty);
query["startTime"] = "0";
query["endTime"] = "1800";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meetingId}/transcript")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var transcript = await response.Content.ReadAsStringAsync();{
"meetingId": "mtg_...",
"botId": "trs_...",
"durationSeconds": 3612.4,
"windowStart": 0.0,
"windowEnd": 3612.4,
"chunks": [
{
"chunkId": "chnk_...",
"speaker": "Alex Rivera",
"hostId": "usr_...",
"language": "en",
"startTime": 0.0,
"endTime": 12.4,
"text": "Thanks for joining today — let's start by getting your take on ...",
"words": [
{ "text": "Thanks", "startTime": 0.0, "endTime": 0.4 }
],
"isFinal": true
}
],
"compiledText": "Alex Rivera: Thanks for joining today ..."
}List transcript chunks
GET /api/v1alpha1/scheduled-meetings/{meetingId}/transcript/chunks
Paginated chunk-by-chunk stream. Useful when the full transcript response would be too large, or when processing incrementally.
Query parameters
| Parameter | Type | Description |
|---|---|---|
cursor | string | Standard keyset cursor. |
limit | number | Clamped 1–100, default 25. |
sort | string | startTime only. Prefix - for descending. Default startTime (chronological). |
language | string | (optional) Filter to one language. |
speaker | string | (optional) Substring match on speaker name; case-insensitive. |
hostId | string | (optional) Filter to one identified speaker by usr_... id. |
Response
Standard paginated list of transcript chunks. Word arrays are omitted from each item to keep the response compact — fetch the full transcript endpoint with a time-range window if you need word-level detail on a specific slice.
Errors
Same as the full-transcript endpoint plus standard pagination errors (invalid-cursor, invalid-sort-field).
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/mtg_01H8YKQ2N9MTGXYZ/transcript/chunks?limit=25&language=en" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
meeting_id = "mtg_01H8YKQ2N9MTGXYZ"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meeting_id}/transcript/chunks",
headers={"X-Cirrus-Api-Key": token},
params={"limit": 25, "language": "en"},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var meetingId = "mtg_01H8YKQ2N9MTGXYZ";
var query = HttpUtility.ParseQueryString(string.Empty);
query["limit"] = "25";
query["language"] = "en";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meetingId}/transcript/chunks")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();{
"items": [
{
"chunkId": "chnk_...",
"meetingId": "mtg_...",
"speaker": "Alex Rivera",
"hostId": "usr_...",
"language": "en",
"startTime": 0.0,
"endTime": 12.4,
"text": "Thanks for joining today ..."
}
],
"nextCursor": "..."
}List chat messages
GET /api/v1alpha1/scheduled-meetings/{meetingId}/chat
Meeting-platform chat (Zoom / Teams / Google Meet in-meeting chat) captured alongside the transcript.
Query parameters
| Parameter | Type | Description |
|---|---|---|
cursor | string | Standard keyset cursor. |
limit | number | Clamped 1–100, default 25. |
Response
recipients: "everyone"for public chat,"private"for DMs. Private-chat messages are only included when the bot's owning user was one of the parties — the API doesn't leak DMs between other participants.
Errors
Same as the transcript endpoint.
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/mtg_01H8YKQ2N9MTGXYZ/chat?limit=25" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
meeting_id = "mtg_01H8YKQ2N9MTGXYZ"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meeting_id}/chat",
headers={"X-Cirrus-Api-Key": token},
params={"limit": 25},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var meetingId = "mtg_01H8YKQ2N9MTGXYZ";
var query = HttpUtility.ParseQueryString(string.Empty);
query["limit"] = "25";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meetingId}/chat")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();{
"items": [
{
"messageId": "chat_...",
"meetingId": "mtg_...",
"sender": "Sam Chen",
"hostId": "usr_...",
"sentAt": "2026-06-16T17:03:22Z",
"text": "Sharing my screen now",
"recipients": "everyone"
}
],
"nextCursor": null
}Get bot status
GET /api/v1alpha1/scheduled-meetings/{meetingId}/bot
Recording-bot lifecycle status. Answers "did the bot join?", "is it still recording?", "did it fail?".
Response
statusenum:pending | joining | waiting-for-host | recording | paused | done | failed | cancelled.recordingAvailablesignals that video/audio has finished processing. Raw recording distribution isn't in v1alpha1 (legal review pending); a follow-up endpoint will return the media URL when available.transcriptChunksAvailable— running count. Poll this to know when to fetch the transcript, rather than re-fetching the transcript repeatedly.errorReason— populated whenstatus = "failed". Human-readable. Examples:"host-denied-join","meeting-locked","platform-unavailable".
Errors
404 meeting-not-found— meeting id unknown or cross-org404 bot-not-configured— meeting exists but no bot was ever scheduled for it
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/mtg_01H8YKQ2N9MTGXYZ/bot" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
meeting_id = "mtg_01H8YKQ2N9MTGXYZ"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meeting_id}/bot",
headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
bot = response.json()using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var meetingId = "mtg_01H8YKQ2N9MTGXYZ";
var response = await client.GetAsync(
$"https://altus.cirrusinsight.com/api/v1alpha1/scheduled-meetings/{meetingId}/bot");
response.EnsureSuccessStatusCode();
var bot = await response.Content.ReadAsStringAsync();{
"botId": "trs_...",
"meetingId": "mtg_...",
"status": "recording",
"startedAt": "2026-06-16T17:00:03Z",
"endedAt": null,
"joinUrl": "https://us02web.zoom.us/j/...",
"provider": "zoom",
"recordingAvailable": false,
"transcriptChunksAvailable": 47,
"errorReason": null
}