Transcript endpoints
List a channel's transcripts, read one with every line and its speaker, and choose where it goes, through the REST API.
Updated
Paths are relative to https://api.fishcamp.space/api/v1 and all need the X-Workspace-Id header. API keys need the
read scope to read transcripts; choosing where one goes needs a person. How transcription and routing work is in
Transcription and AI routing.
The transcript object
{
"id": "3f2d9c1e-5b7a-4c2e-9a0b-6d8e1f4a2c7b",
"channelId": "0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10",
"startedAt": "2026-11-05T15:00:12.000Z",
"endedAt": "2026-11-05T15:25:40.000Z",
"status": "DONE",
"action": "run_devops_agent",
"confidence": null,
"routedBy": "7a1c2e3f-4b5d-4e6f-8a9b-0c1d2e3f4a5b",
"routedAt": "2026-11-05T15:26:03.000Z",
"segmentCount": 48,
"participants": ["Maya Lopes", "Theo Park", "Ana Souza (guest)"],
"result": {
"executionId": null,
"agents": [{ "agentId": "e5c4b3a2-1f0e-4d9c-8b7a-6f5e4d3c2b1a", "name": "Scout", "ok": true, "status": 202, "error": null }]
}
}| Field | Description |
|---|---|
id | The transcript's ID. One transcript per audio room session. |
status | ACTIVE while the room is open and being transcribed, PROCESSING while Dcision decides, then DONE or FAILED. |
action | discard, save_to_opensync, run_devops_agent (Send to agents), or review while it waits for a person. |
confidence | How sure Dcision was, from 0 to 1, or null when a person decided. |
routedBy | dcision, system (nothing was transcribed), or the profile ID of the person who chose. |
participants | Everyone who spoke. Guests have "(guest)" after their name. |
result | What happened: opensync.path and opensync.spaceId after a save; agents, one item per agent with agentId, name, ok, status and error, after a handoff; error when it failed; reason when it's waiting for a person, for example because a guest spoke and Dcision picked agents. |
Read a transcript adds segments: every line with its speakerName, startMs and endMs
(milliseconds since the room opened) and text.
List transcripts
GET /channels/:channelId/transcriptsThe 20 latest transcripts of the channel's audio rooms, newest first: when each call happened, who spoke, and what was
decided for it (discarded, saved to OpenSync, sent to agents, or waiting for a person). A channel you can't see
answers 404.
curl "https://api.fishcamp.space/api/v1/channels/0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10/transcripts" \
-H "Authorization: Bearer cwk_YOUR_KEY" \
-H "X-Workspace-Id: YOUR_WORKSPACE_ID"Read a transcript
GET /transcripts/:transcriptIdOne transcript: every line with the name of who said it, plus the routing decision. Use the IDs from the list above.
A transcript from a channel you can't see answers 404.
curl "https://api.fishcamp.space/api/v1/transcripts/3f2d9c1e-5b7a-4c2e-9a0b-6d8e1f4a2c7b" \
-H "Authorization: Bearer cwk_YOUR_KEY" \
-H "X-Workspace-Id: YOUR_WORKSPACE_ID"Transcripts contain text only: the audio is never stored, so there's nothing to download but the words.
AI agents can read the same data with the MCP tools list_transcripts and
read_transcript.
Choose where a transcript goes
New in November 2026POST /transcripts/:transcriptId/routePeople who can see the channel: requests with an API key get 403. Use it to decide a transcript that's waiting
for a person, to change Dcision's choice, or to try a failed action again.
action | What happens |
|---|---|
discard | Marks it as discarded. What was already saved or sent stays where it went. |
save_to_opensync | Saves it as a note in the workspace's OpenSync space. |
run_devops_agent | Send to agents: delivers it to the channel's agents that take transcript handoffs. |
curl -X POST "https://api.fishcamp.space/api/v1/transcripts/3f2d9c1e-5b7a-4c2e-9a0b-6d8e1f4a2c7b/route" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-H "X-Workspace-Id: YOUR_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{"action": "run_devops_agent"}'Returns 200 with the transcript, without its lines. When the action itself fails, for example because OpenSync isn't
connected or no agent takes handoffs, the answer is still 200, with "status": "FAILED" and the reason in
result.error. The timeline gets a line with the result either way.
| Error | When |
|---|---|
409 transcript_busy | The room is still open or just closed, Dcision is still deciding, or someone else's choice is running. Only one choice runs at a time. |
409 transcript_empty | Nothing was transcribed. |
422 validation_error | action isn't one of the three above. |