message.comDevelopers

Voicemails Live

Voicemails are recorded when a caller is sent to voicemail (out-of-hours, no available agents, or explicit caller choice). Each voicemail carries the recording URL and an automatically-generated transcript so agents can triage from text without listening.

List voicemails

GET/api/v1/voicemailsAuth: Bearer

Cursor-paginated list of voicemails. Filters: status (unread / read), phoneNumberId, fromE164.

Code samples

cURL
curl 'https://app.message.com/api/v1/voicemails?status=unread' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/voicemails?status=unread', {
  headers: { Authorization: 'Bearer ' + token }
});
const { voicemails } = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/voicemails",
    headers={"Authorization": f"Bearer {token}"},
    params={"status": "unread"},
)
voicemails = r.json()["voicemails"]
Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/voicemails?status=unread")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  req = Net::HTTP::Get.new(uri)
  req["Authorization"] = "Bearer #{token}"
  http.request(req)
end
voicemails = JSON.parse(res.body)["voicemails"]
PHP
<?php
$ctx = stream_context_create([
  "http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$voicemails = json_decode(file_get_contents("https://app.message.com/api/v1/voicemails?status=unread", false, $ctx), true)["voicemails"];

Get one voicemail

GET/api/v1/voicemails/:idAuth: Bearer
200 OK
{
  "id": "uuid",
  "conversationId": "uuid",
  "phoneNumberId": "uuid",
  "fromE164": "+14155550100",
  "duration_ms": 22300,
  "recordingUrl": "https://cdn.message.com/voicemails/...mp3",
  "transcript": "Hi, this is Jane. I had a question about my order...",
  "transcriptConfidence": 0.94,
  "readAt": null,
  "createdAt": "2026-05-13T15:30:00Z"
}

Mark read / unread

POST/api/v1/voicemails/:id/readAuth: Bearer
POST/api/v1/voicemails/:id/unreadAuth: Bearer

Toggles the readAt timestamp. Used by the dashboard to mark a voicemail as triaged.

Delete a voicemail

DELETE/api/v1/voicemails/:idAuth: Bearer

Removes the voicemail record and the recording file from object storage. The parent conversation keeps a system message noting the deletion.

Voicemail deletion is permanent. Customers on regulated plans should rely on retention policies rather than ad-hoc deletes; see Concept: voicemail transcription.

Transcript details

Transcripts come from our speech-to-text pipeline with a confidence score in transcriptConfidence (0 to 1). Scores below 0.7 should be treated as advisory; the recording is the source of truth. Multilingual voicemails fall back to language detection; we do not currently translate.

Errors

CodeWhen
403 forbiddenAgent lacks call channel scope.
404 not_foundVoicemail does not exist in this workspace.
410 goneVoicemail was deleted previously.

Common pitfalls

  • Trusting a low-confidence transcript. Always show the recording link alongside the transcript in your UI. Numbers and proper nouns get mis-transcribed; check before action.
  • Treating recordingUrl as permanent. URLs are signed and expire after 24 hours. Re-fetch the voicemail record to get a fresh URL.
  • Forgetting the conversation link. Every voicemail belongs to a call conversation. Reply via /messages on that conversation, not the voicemail record directly.