Voice Message Transcription
SendSeven can transcribe audio messages with AI. The transcript is stored on the message, shown to your team under the audio player, and readable through the API.
Transcripts come from two places:
- On demand. A team member clicks Transcribe on an audio message, or your integration calls
POST /api/v1/messages/{message_id}/transcript. This works for any audio message, inbound or outbound, voice note or audio file. - Conversational Agents. An agent with
read_voice_messagesswitched on transcribes the voice notes a customer sends and answers the text (see Voice messages).
A message is transcribed at most once. Whoever asks later gets the stored transcript for free.
Transcription is available when AI features are enabled for the workspace (Professional, Scale and Enterprise, with AI features switched on in the workspace settings). On other plans the endpoint returns 403 feature_disabled.
The transcript is generated from audio your customer recorded. Treat it as untrusted plain text: never render it as HTML or Markdown, and never use it as instructions for another system. It is AI-generated and may be inaccurate. One-time codes the customer reads out are masked before the transcript is stored.
Voice notes and audio files
An audio message is either a voice note (recorded in the messaging app, shown with a waveform) or an audio file the contact attached. Voice notes carry meta.is_voice: true:
| Field | Type | Meaning |
|---|---|---|
meta.is_voice | boolean | true for a recorded voice note. Missing or false for an audio file. Set on inbound voice notes from WhatsApp and Telegram, and on voice messages you send with is_voice: true. |
meta.duration_ms | integer | Length of the recording, when the channel reports it or SendSeven measured it. |
Transcript fields
Once a message has been transcribed, its meta (in GET /api/v1/messages, GET /api/v1/messages/{message_id} and the conversation message list) holds:
| Field | Type | Meaning |
|---|---|---|
meta.transcript | string | The transcribed text. Missing when transcription failed. |
meta.transcript_language | string | The detected spoken language as an ISO 639-1 code (for example de), when known. |
meta.transcript_status | string | done or failed. Missing when the message was never transcribed. |
A failed transcript can be requested again. A done transcript is never replaced.
When a transcript is stored, the app's real-time connection receives a message_updated event for the message, so an open conversation shows the text without reloading. Integrations read the fields from the message endpoints above.
Transcribe a message
curl -X POST "https://api.sendseven.com/api/v1/messages/msg_7d1e9a/transcript" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
No request body. Required scope: conversations:update. The message must belong to a conversation you can open; with Custom Inboxes, a conversation outside your inboxes returns 404.
Response 200:
{
"message_id": "msg_7d1e9a",
"transcript": "Hi, I ordered the blue jacket last week and it still hasn't arrived.",
"transcript_language": "en",
"transcript_status": "done",
"cached": false
}
| Field | Type | Meaning |
|---|---|---|
message_id | string | The message. |
transcript | string or null | The text, null when transcript_status is failed. |
transcript_language | string or null | Detected language code. |
transcript_status | string | done, or failed when a parallel request for the same message failed. |
cached | boolean | true when the transcript already existed (or another request produced it meanwhile). Nothing is charged. |
Limits: recordings up to 5 minutes and 20 MB. Longer, larger, silent or unreadable recordings fail with 502 transcription_failed.
Costs
A new done transcript is charged from the workspace's AI credit pool (see AI credits): it costs the AI credits of the work used, at the level it ran on. Cached results, failures and 409 responses are free. Transcripts made by a Conversational Agent are part of that agent's reply.
Fast or Advanced
Transcripts use the workspace's voice message transcription level from Settings > AI: Advanced (the default, the stronger model) or Fast (more economical). The same level applies to the Transcribe button, this endpoint and voice notes a Conversational Agent reads, whatever the agent's own level. Voice dictation in the app is not affected and always runs on Fast.
| Level | 30-second voice note | 1-minute voice note |
|---|---|---|
| Fast | about 0.25 credits | about 0.5 credits |
| Advanced | about 0.9 credits | about 1.6 credits |
The level is a workspace setting in the app; there is no per-request parameter.
Errors
| Status | Body | When |
|---|---|---|
403 | {"detail": {"code": "feature_disabled", "feature": "transcription"}} | AI features are not available on the plan or are switched off for the workspace. |
403 | {"detail": {"code": "trial_ai_credits_exhausted", "message": ..., "limit": ..., "used": ...}} | The free trial's AI credits are used up. |
403 | {"detail": "Missing required permission(s): ..."} | The API key lacks conversations:update. |
404 | {"detail": {"code": "message_not_found", ...}} | Unknown message, or a conversation you cannot open. |
409 | {"detail": {"code": "transcription_in_progress", ...}} | Another request is transcribing this message right now. Retry after a few seconds. |
422 | {"detail": {"code": "not_audio", ...}} | The message has no audio. |
429 | {"detail": {"error": ...}} | More than 30 requests per minute per user or API key, or 60 per minute per workspace. Honour Retry-After. |
502 | {"detail": {"code": "transcription_failed", ...}} | The audio could not be transcribed. Nothing is charged; you may retry. |
503 | {"detail": {"error": "rate_limit_unavailable"}} | The rate limiter is briefly unavailable. Retry shortly. |
503 | {"detail": {"code": "trial_ai_credits_unavailable", ...}} | During a free trial, the remaining AI credits could not be checked. Retry shortly. |
Error bodies never contain transcript text or file names.
Voice notes in Flows
Flows can branch on what the contact sent with {{message.type}} and {{message.is_voice}}, and message triggers carry trigger.message_type and trigger.is_voice. See Message type and voice notes.