By area
Media
Read from the API’s own reference file every hour.
Short-lived links to message attachments, call recordings and fax documents.
Get a media link
GET/v1/
Returns a short-lived link to one file:
att_<n>: a message attachment, fromGET /v1/messagesor a message webhook. A bare number is read as an attachment too.rec_<n>: the recording of one call leg, fromrecording_urlorrecordingsinGET /v1/calls.fax_<n>: a fax document, fromGET /v1/faxes.
url is a signed link, good for 30 minutes (expires_at). Download the file with a plain GET: the signed link needs no key. Ask again for a fresh link.
A call parked and picked up again is recorded in parts. rec_<n> answers the latest part; rec_<n>?part=N answers part N (from 1). The answer names the part it holds (part of parts) with its recording_id and start_time. A part that does not exist answers 404, and the message says how many parts there are.
Two exceptions return a plain link with no expires_at: an attachment on a message you sent (you get back the URL you sent it with), and a picture received in the last few minutes, before its stored copy is ready.
Anything that is not yours answers 404. A key with the messaging scope may fetch attachments only.
Each request is written to your account's HIPAA access log.
Request
curl https://api.vocatech.com/v1/media/rec_48213907 \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch("https://api.vocatech.com/v1/media/rec_48213907", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
console.log(res.status, await res.json());import requests
r = requests.get(
"https://api.vocatech.com/v1/media/rec_48213907",
headers={"Authorization": "Bearer YOUR_API_KEY"},
timeout=30,
)
print(r.status_code, r.json())<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client([
'base_uri' => 'https://api.vocatech.com',
'headers' => ['Authorization' => 'Bearer YOUR_API_KEY'],
'http_errors' => false,
]);
$res = $client->get('/v1/media/rec_48213907');
echo $res->getStatusCode(), PHP_EOL, $res->getBody(), PHP_EOL;Parameters
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | att_<n>, rec_<n> or fax_<n>. Matches ^(att_|rec_|fax_)?[0-9]+$. |
part | query | integer | For a recording (rec_<n>) only. Which part of a recording saved in parts, from 1, oldest first. Leave it out for the latest part. recordings in GET /v1/calls gives each part's link. At least 1. For example 1. |
Answers
When type is attachment
| Field | Type | Description |
|---|---|---|
id required | string | Matches ^att_[0-9]+$. |
type required | string | Always attachment. |
source required | string | Where the message came from. One of sms or whatsapp. |
content_type required | string or null | |
filename required | string or null | |
size required | integer or null | Bytes, when known. |
url required | string (uri) | A signed link, good for 30 minutes. For an attachment on a message you sent, the URL you sent it with. |
expires_at | string (date-time) | When the signed link stops working. Left out when url is not a signed link. |
When type is recording
| Field | Type | Description |
|---|---|---|
id required | string | Matches ^rec_[0-9]+$. |
type required | string | Always recording. |
content_type required | string | audio/ and the file's extension, for example audio/mp3 or audio/wav. |
format required | string | The file's extension, for example mp3. |
duration required | integer or null | Seconds. |
url required | string (uri) | A signed link, good for 30 minutes. |
expires_at required | string (date-time) | |
part required | integer | Which part this is, from 1. Without ?part=, the latest part. |
parts required | integer | How many parts the recording has. Nearly always 1. |
recording_id required | integer | The recording's id, as in recordings and the call.transcription event. |
start_time required | string (date-time) or null | When this part started. UTC, ISO 8601 with milliseconds. |
When type is fax
| Field | Type | Description |
|---|---|---|
id required | string | Matches ^fax_[0-9]+$. |
type required | string | Always fax. |
content_type required | string | Always application/pdf. |
format required | string | Always pdf. |
pages required | integer or null | |
url required | string (uri) | A signed link, good for 30 minutes. |
expires_at required | string (date-time) |
{
"id": "att_90211",
"type": "attachment",
"source": "sms",
"content_type": "image/jpeg",
"filename": "photo-0921.jpg",
"size": 184233,
"url": "https://files.example.com/signed/att_90211.jpg?sig=7c2e9a41",
"expires_at": "2026-09-24T14:35:09.000Z"
}{
"id": "rec_48213907",
"type": "recording",
"content_type": "audio/mp3",
"format": "mp3",
"duration": 309,
"url": "https://files.example.com/signed/rec_48213907.mp3?sig=b71f0c9e",
"expires_at": "2026-09-24T14:35:09.000Z",
"part": 2,
"parts": 2,
"recording_id": 10577702,
"start_time": "2026-09-21T14:04:31.000Z"
}{
"id": "fax_77120",
"type": "fax",
"content_type": "application/pdf",
"format": "pdf",
"pages": 3,
"url": "https://files.example.com/signed/fax_77120.pdf?sig=3d5a8c20",
"expires_at": "2026-09-24T14:35:09.000Z"
}Example bodies
{
"error": {
"code": 400,
"type": "BAD_REQUEST",
"message": "Invalid recording segment ID."
}
}{
"error": {
"code": 400,
"type": "BAD_REQUEST",
"message": "Invalid attachment ID."
}
}{
"error": {
"code": 400,
"type": "BAD_REQUEST",
"message": "Invalid fax ID."
}
}{
"error": {
"code": 400,
"type": "BAD_REQUEST",
"message": "Invalid recording part. Use a whole number from 1."
}
}messaging key asked for a recording or a fax, the account has no call recording set up, or the key's scopes or address.Example bodies
{
"error": {
"code": 403,
"type": "FORBIDDEN",
"message": "This API key is limited to messaging and cannot fetch recording media."
}
}{
"error": {
"code": 403,
"type": "FORBIDDEN",
"message": "Company not configured for call recordings."
}
}Example bodies
{
"error": {
"code": 404,
"type": "NOT_FOUND",
"message": "Attachment not found."
}
}{
"error": {
"code": 404,
"type": "NOT_FOUND",
"message": "Recording not found."
}
}{
"error": {
"code": 404,
"type": "NOT_FOUND",
"message": "Recording file not yet available."
}
}{
"error": {
"code": 404,
"type": "NOT_FOUND",
"message": "Recording part not found. This recording has 2 parts."
}
}{
"error": {
"code": 404,
"type": "NOT_FOUND",
"message": "Fax document not found."
}
}{
"error": {
"code": 404,
"type": "NOT_FOUND",
"message": "Fax document not yet available."
}
}Example body
{
"error": {
"code": 410,
"type": "ERROR",
"message": "Media file no longer available."
}
}Retry-After seconds. The body names the counter, the window and the limit.| Header | Type | Description |
|---|---|---|
Retry-After | integer | Seconds to wait before you try again. |
Example body
{
"error": {
"code": 500,
"type": "SERVER_ERROR",
"message": "Fax service not configured."
}
}| Header | Type | Description |
|---|---|---|
Retry-After | integer | Seconds to wait before you try again. |
Example body
{
"error": {
"code": 503,
"type": "ERROR",
"message": "Recording storage access failed on our side. The recording exists. Contact support."
}
}