How to Download a File Attachment
When an incoming webhook payload includes file attachments, you need to request a short-lived signed URL before you can download the file. This is a two-step process: identify the attachment IDs from the webhook, then exchange them for signed download URLs.
Attachments in the Webhook Payload
Messages with attachments include an attachments array in webhook.data. Each entry has a type field — only "file" attachments require a signed URL. "link" attachments have a link field that is a directly accessible URL.
| Field | Description |
|---|---|
id | The attachment ID — use this to request a signed download URL |
type | "file" for uploaded files, "link" for web URLs |
link | For type: "link": the URL itself. For type: "file": the internal storage path (not directly accessible) |
filename | Original filename (e.g. document.pdf) |
mime_type | MIME type (e.g. application/pdf) |
length_in_bytes | File size in bytes |
Example webhook payload with a file attachment:
{
"event": "message.posted.to.channel",
"data": {
"id": "message-uuid",
"conversation_id": "channel-uuid",
"attachments": [
{
"id": "attachment-uuid",
"type": "file",
"filename": "report.pdf",
"mime_type": "application/pdf",
"length_in_bytes": 204800
}
]
}
}
Get a Signed Download URL (Single File)
GET /v5/attachments/download/signedurl/:attachment_id
Authorization: Bearer <your-pat>
Response — a plain string containing the signed URL:
"https://storage.example.com/files/report.pdf?X-Amz-Signature=..."
The signed URL is temporary. Download the file promptly after receiving it.
Example:
curl -X GET https://api.carbonvoice.app/v5/attachments/download/signedurl/attachment-uuid \
-H "Authorization: Bearer <your-pat>"
Get Signed Download URLs in Bulk
If a message has multiple attachments, fetch all signed URLs in a single request:
POST /v5/attachments/download/signedurl/bulk
Content-Type: application/json
Authorization: Bearer <your-pat>
{
"ids": ["attachment-uuid-1", "attachment-uuid-2"]
}
Response — an array of objects, one per requested ID:
| Field | Description |
|---|---|
attachment_id | The attachment ID from the request |
signed_url | The signed download URL for that attachment |
[
{
"attachment_id": "attachment-uuid-1",
"signed_url": "https://storage.example.com/files/report.pdf?X-Amz-Signature=..."
},
{
"attachment_id": "attachment-uuid-2",
"signed_url": "https://storage.example.com/files/photo.jpg?X-Amz-Signature=..."
}
]
Full Example — Webhook Handler
async function handleWebhook(webhook) {
const fileAttachments = (webhook.data.attachments ?? []).filter(
(a) => a.type === 'file',
);
if (fileAttachments.length === 0) return;
const ids = fileAttachments.map((a) => a.id);
const response = await fetch(
'https://api.carbonvoice.app/v5/attachments/download/signedurl/bulk',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CV_PAT}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids }),
},
);
const signedUrls = await response.json();
for (const { attachment_id, signed_url } of signedUrls) {
const file = await fetch(signed_url);
// process file.body stream...
}
}