The transcript endpoint doesn’t return the transcript text directly. It returns metadata
plus a short-lived presigned URL to download the transcript as a JSONL file (one
utterance per line) hosted in S3.
Fetching a transcript
GET /v1/meetings/{uuid}/transcript returns the metadata envelope:
{
"uuid": "d8e1f2a3-4b5c-6d7e-8f90-1a2b3c4d5e6f",
"meeting_uuid": "9a1c2b3d-4e5f-6071-8293-a4b5c6d7e8f9",
"format": "jsonl",
"download_url": "https://hamlet-transcripts-prod.s3.us-west-1.amazonaws.com/…?X-Amz-Signature=…",
"url_expires_at": "2026-06-22T15:30:00.000Z",
"generated_at": "2026-05-22T09:20:00.000Z"
}
| Field | Type | Notes |
|---|
uuid | string | The transcript’s public identifier. |
meeting_uuid | string | The meeting’s public identifier; matches the path. |
format | string | Always jsonl. |
download_url | string | Presigned S3 URL for the JSONL file, valid 1 hour. |
url_expires_at | timestamp | ISO-8601 UTC time when download_url expires. |
generated_at | timestamp | ISO-8601 UTC time when the artifact was built. |
download_url expires one hour after it’s issued. Fetch the JSONL promptly, and request a
fresh metadata response to get a new URL rather than caching the link.
Not every meeting has a transcript. If a meeting you’re entitled to has no built artifact,
this endpoint returns 404 (not_found) — never a broken link. The has_transcript flag
on the meeting tells you in advance whether to call it.
Downloading the file
# 1. Get the metadata + presigned URL
curl https://api.myhamlet.com/v1/meetings/9a1c…/transcript \
-H "Authorization: Bearer hmlt_your_api_key"
# 2. Download the JSONL from the returned download_url (no auth header — the URL is presigned)
curl -o transcript.jsonl "https://hamlet-transcripts-prod.s3.us-west-1.amazonaws.com/…?X-Amz-Signature=…"
JSONL line schema
The downloaded file is JSON Lines: one JSON object per line,
ordered by line_number. Each line is one utterance:
| Field | Type | Notes |
|---|
line_number | integer | 1-based order within the transcript. |
speaker_number | integer | Speaker diarization ordinal. |
speaker_name | string | null | Identified speaker name; null when unknown. |
speaker_role | string | null | Identified speaker role (e.g. Mayor); null when unknown. |
start_time_ms | integer | Offset from media start, in milliseconds. |
end_time_ms | integer | Offset from media start, in milliseconds. |
text | string | The utterance text. |
{"line_number": 1, "speaker_number": 0, "speaker_name": "Jane Doe", "speaker_role": "Mayor", "start_time_ms": 0, "end_time_ms": 4200, "text": "Call this meeting to order."}
{"line_number": 2, "speaker_number": 1, "speaker_name": null, "speaker_role": null, "start_time_ms": 4200, "end_time_ms": 9100, "text": "Present."}
Parse it line by line — don’t load the whole file as a single JSON document:
import json, requests
meta = requests.get(
"https://api.myhamlet.com/v1/meetings/9a1c…/transcript",
headers={"Authorization": "Bearer hmlt_your_api_key"},
).json()
# The presigned URL needs no auth header.
resp = requests.get(meta["download_url"])
for line in resp.iter_lines():
if line:
utterance = json.loads(line)
print(utterance["speaker_name"], "→", utterance["text"])