How to transcribe a Zoom, Meet, or Teams recording with speaker labels
Export the meeting recording as MP4 or M4A and send its URL to POST /transcribe with include_diarization set to true. Every segment comes back with start and end timestamps and a speaker label. From there, you can feed the labeled transcript to your own LLM to extract minutes and action items. Note that the API only processes recorded files you give it; it does not join live calls.
Transcribe the recording with speaker labels
Upload the file via POST /upload (up to 100 MiB, or extract the audio with ffmpeg first for longer recordings) or pass any public URL, then start the job. num_speakers is optional, though you should set it if you know how many people spoke.
curl https://app.kolwrite.com/transcribe \
-H "x-api-key: $KOLWRITE_API_KEY" -H "Content-Type: application/json" \
-d '{"audio_url": "https://example.com/weekly-sync.mp4", "model": "oracle", "language": "auto",
"include_diarization": true, "num_speakers": 4, "output_format": "json"}'
Poll GET /transcribe/{jobId} for the result, or pass a webhook_url so we notify your server when a long recording is done.
Every segment includes the speaker label, timestamps, and word-level breakdown:
{
"jobId": "34c63d45-4cb5-473b-bc60-25c175e69c57",
"status": "COMPLETED",
"result": {
"language": "en",
"segments": [
{
"start": 1.0,
"end": 3.75,
"text": "Hello everyone and welcome to the show.",
"speaker": "Speaker 1",
"words": [
{ "start": 1.0, "end": 1.35, "word": "Hello" },
{ "start": 1.4, "end": 1.75, "word": "everyone" }
]
}
]
}
}
Map speaker labels to real names
Speaker labels come back as generic strings (Speaker 1, Speaker 2). It is up to you to match them to real people, such as by checking the attendee list or seeing who introduces themselves first. A simple mapping in your code keeps it clear:
minutes.py
# minutes.py — turn the diarized result into text an LLM can summarize
NAMES = {"Speaker 1": "Dana", "Speaker 2": "Omar", "Speaker 3": "Lee"} # you decide who is who
def clock(seconds):
m, s = divmod(int(seconds), 60)
h, m = divmod(m, 60)
return f"{h}:{m:02}:{s:02}" if h else f"{m}:{s:02}"
def meeting_text(result):
lines = []
for segment in result["segments"]:
who = NAMES.get(segment.get("speaker"), segment.get("speaker", "Unknown"))
lines.append(f'[{clock(segment["start"])}] {who}: {segment["text"]}')
return "\n".join(lines)
PROMPT = """Below is a meeting transcript with timestamps. Write: (1) a five-line summary,
(2) decisions, (3) action items as "owner - task - [timestamp]". Use only what is in the transcript.
"""
# Send PROMPT + meeting_text(result) to the model you use.
Minutes and action items
KolWrite gives you the timed, speaker-labeled transcript. For summaries and action items, pass that text to whichever LLM you already use. Leave the timestamps in the prompt so each action item can reference the exact moment in the recording, and instruct the model to stick strictly to what is in the transcript.
Call recordings with one speaker per channel
If you have a stereo call recording with the agent on the left channel and the customer on the right, you don't need diarization. Split the speakers by audio channel instead. Channel separation supports 2 to 8 channels and cannot be used alongside include_diarization.
{
"audio_url": "https://example.com/support-call.wav",
"model": "oracle",
"channel_speaker_separation": "standard",
"channel_labels": { "0": "Agent", "1": "Customer" },
"output_format": "json"
}
Use overlap instead of standard when people talk over each other; it bills the model rate for the greater of the recording length or the total detected speech across channels.
Pricing
| Job | Model | Cost |
|---|---|---|
| One-hour meeting with speaker labels | oracle | US$1.44 |
| Same meeting with Pro diarization for tough audio | oracle + pro_diarization | US$1.68 |
| 30 minutes, difficult audio, Pro diarization | oracle_max + pro_diarization | US$1.92 |
Standard diarization and standard channel separation don't cost anything extra. Full rates: /llms/pricing.md.
Things to keep in mind
- Meetings contain personal data. Make sure everyone on the call knows they are being recorded and transcribed, and secure the transcripts just as carefully as the audio files.
- Telling speakers apart is hardest when people talk over each other, have similar voices, or use poor microphones. For difficult audio, try
pro_diarizationor channel separation. - If a meeting is in a single language, set
languageexplicitly. Auto-detection only picks one language for the whole file. - Have a human review names, numbers, and final decisions before anyone acts on the generated minutes.
Questions and answers
How do I transcribe a Zoom recording with speaker names?
Download the recording and send it to POST /transcribe with include_diarization: true. The API identifies different voices as Speaker 1, Speaker 2, and so on. You then map those labels to actual names in your own code.
Can it join my Google Meet or Teams call?
No. KolWrite only transcribes recorded files you send it. It does not join live calls or transcribe in real time.
Does it write meeting minutes and action items?
It gives you a timed transcript with speaker labels. You then use your own LLM to write the minutes and action items from that transcript.
How are phone calls with two speakers handled?
When each speaker has a separate channel, skip diarization and use channel_speaker_separation with channel_labels.
Which languages work for meetings?
All 100 languages in the language list, including Hebrew and Arabic. If everyone in the meeting speaks the same language, set the language parameter.