Two endpoints that look alike
Both endpoints take the same two IDs: the sub-account's location ID and the message ID of the call. What differs is the order.
- Transcript: /conversations/locations/{locationId}/messages/{messageId}/transcription
- Recording: /conversations/messages/{messageId}/locations/{locationId}/recording
Location first for the transcript. Message first for the recording.
The 404 that looks like a missing transcript
The natural move is to take the recording path you already have working and swap its last segment for transcription. In our builds that guess returned a 404. It reads like “this call has no transcript,” when the real problem is the path.
The reverse mistake shows up too: a request to the recording path comes back as audio, in audio/x-wav, and code expecting JSON breaks on it. If you want both the audio and the text, make two calls to two paths.
Making the request
Each of these comes straight from HighLevel's transcription reference.
- Take the location ID of the sub-account and the message ID of the call. Both are required, and both are strings.
- Put them in the transcription path in that order: locations, then messages.
- Send a bearer token scoped to that sub-account.
- Send the Version header for the documentation version you're using. The transcription page pinned to 2021-04-15 asks for Version: 2021-04-15.
What comes back
The response is the call broken into sentences. Each one carries these fields, and it pays to keep all of them when you store it.
- transcript: the text of that sentence.
- sentenceIndex: its position, so you can put the call back in order.
- startTime and endTime: where the sentence starts and stops, in milliseconds, so a reviewer can jump to that moment in the recording.
- mediaChannel: the channel the sentence came from.
- confidence: how sure the transcription is, useful for flagging lines a person should listen to.
Telling the two speakers apart
In our builds, mediaChannel was enough to separate one side of the call from the other without sending the audio to a second transcription service. HighLevel's schema doesn't say which channel value is the caller and which is the other party, though.
Work that out on a call you placed yourself, where you know who said what, and set your mapping from that. Don't promise anyone speaker labels until you have.
Parse it defensively
HighLevel's schema defines the times, index and confidence as numbers, but the example on the same page shows them in quotes, as strings. Check what real responses send before you fix the types in your parser.
Keep one API version throughout. The recording reference opens on HighLevel's newer v3 docs by default, while the transcription reference here is pinned to 2021-04-15. Confirm both endpoints on the same version before you ship code that calls them.
Some things this guide can't tell you: how soon after a call the transcript is ready, which OAuth scope the call needs, and what the response looks like when there's no transcript. Test those on your own account before you build a job that depends on them.
Rather have it set up for you?
We'd set up your calling, recordings and transcript pulls in HighLevel so every call can be read back, starting with a 30-minute 1:1 with Rob.
