Send Media Messages
SendSeven supports sending rich media messages -- images, documents, audio files, and videos -- across all channels that support them.
Required Scopes
| Scope | Purpose |
|---|---|
messages:create | Send media messages |
attachments:create | Upload media files |
Supported Media Types
| Type | Description | Channels |
|---|---|---|
image | JPEG, PNG, WebP images with optional caption | WhatsApp, Telegram, Messenger, Instagram, Email, Live Chat |
document | PDF, DOCX, XLSX, and other file types | WhatsApp, Telegram, Messenger, Email, Live Chat |
audio | MP3, OGG, AAC audio files | WhatsApp, Telegram, Live Chat |
video | MP4, MOV video files | WhatsApp, Telegram, Messenger, Instagram, Email, Live Chat |
Use the Channel Capabilities API to check which media types are supported on a specific channel before sending.
Two-Step Process
Sending media messages requires two steps:
- Upload the file via
POST /api/v1/attachments(multipart form upload) - Send a message with the returned attachment ID in the
attachmentsarray
Step 1: Upload the File
POST /api/v1/attachments
Upload the media file using multipart form data. The API returns an attachment object with an id you will reference when sending the message.
curl -X POST "https://api.sendseven.com/api/v1/attachments" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-F "[email protected]"
{
"id": "att_abc123",
"filename": "photo.jpg",
"content_type": "image/jpeg",
"size": 245000,
"created_at": "2026-02-10T15:10:00Z"
}
Step 2: Send a Message with Attachments
Send an Image
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_8f3a2b1c",
"text": "Your product is ready for pickup!",
"message_type": "image",
"attachments": ["att_abc123"]
}'
The text field serves as the image caption. It is optional for image messages on most channels.
Send a Document
# Step 1: Upload the document
curl -X POST "https://api.sendseven.com/api/v1/attachments" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-F "[email protected]"
# Step 2: Send the message (using the returned attachment ID)
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_8f3a2b1c",
"text": "Here is your invoice for January 2026.",
"message_type": "document",
"attachments": ["att_def456"],
"attachment_filenames": ["Invoice-2026.pdf"]
}'
The attachment_filenames field controls the display filename the recipient sees -- for example, the filename shown under a PDF in WhatsApp. See Custom Display Filenames below.
Custom Display Filenames
By default, the recipient sees the filename that was stored when you uploaded the file (the filename returned by the Attachments API). You can override this per message with the optional attachment_filenames array, so the recipient sees a clean, meaningful name -- for example Invoice-2026.pdf instead of an internal object name like att_def456.pdf or tmp_upload_9f3a.pdf.
This is especially useful for PDFs and other documents, where the filename is shown prominently to the recipient (e.g. Meta's filename field on a WhatsApp document).
How It Works
attachment_filenamesis a list of strings, aligned positionally by index to theattachmentsarray: entryNoverrides the filename ofattachments[N].- When omitted, or when an entry is
nullor an empty string, the send path falls back to the original filename stored on the attachment record. - If the list is shorter than
attachments, only the leading attachments are overridden; extra entries beyond the number of attachments are ignored.
Every entry takes effect. When a send carries several attachments on a chat channel, each attachment is delivered as its own message, so entry N is the display filename of the message carrying attachments[N]. See Sending Several Attachments at Once.
Example: Rename a PDF for the Recipient
# Step 1: Upload a PDF that happens to have an unfriendly stored name
curl -X POST "https://api.sendseven.com/api/v1/attachments" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-F "file=@export_20260117_final_v3.pdf"
# -> { "id": "att_def456", "filename": "export_20260117_final_v3.pdf", ... }
# Step 2: Send it with a clean display filename
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_8f3a2b1c",
"text": "Here is your invoice for January 2026.",
"message_type": "document",
"attachments": ["att_def456"],
"attachment_filenames": ["Invoice-2026.pdf"]
}'
The recipient now sees Invoice-2026.pdf as the document name, regardless of how the file was named when it was uploaded.
Include the file extension (.pdf, .docx, .xlsx) in the display filename so the recipient's device renders the correct file-type icon and opens it with the right application.
Send Audio
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_8f3a2b1c",
"text": "",
"message_type": "audio",
"attachments": ["att_audio789"]
}'
Send Video
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_8f3a2b1c",
"text": "Watch our product demo!",
"message_type": "video",
"attachments": ["att_vid012"]
}'
Sending Several Attachments at Once
attachments accepts more than one ID. What happens next depends on the channel:
| Channel | Result |
|---|---|
| One email with all files attached as MIME parts. | |
| Live Chat | One widget message carrying all files. |
| WhatsApp, Telegram, Messenger, Instagram, RCS, SMS, Browser Push | One message per attachment. Those channels accept exactly one media object per message, so the send is split. |
When the send is split:
- Your
textis used as the caption of the first message only, so the recipient does not read it several times. - Each message gets its own type derived from its own file, so a mixed batch (an image plus a PDF) arrives as an image message and a document message rather than two images.
- The messages are delivered in the order you listed them.
- Each message has its own status and its own delivery webhooks. One attachment failing does not roll back the ones already delivered.
- Each message is billed as a message. Three attachments to a WhatsApp contact are billed as three messages.
The Response
POST /api/v1/messages returns a single message object — the first message of the send, exactly as it always has. The IDs of the additional messages are in the additive related_message_ids array:
{
"id": "msg_a1b2c3",
"conversation_id": "conv_8f3a2b1c",
"direction": "outbound",
"message_type": "image",
"text": "Here are the three photos.",
"status": "pending",
"attachments": [
{ "id": "att_abc123", "filename": "photo-1.jpg", "content_type": "image/jpeg" }
],
"related_message_ids": ["msg_d4e5f6", "msg_g7h8i9"],
"created_at": "2026-08-06T14:30:00Z"
}
related_message_ids is always present and is an empty array for every send that was not split — including single-attachment sends, text messages, and email. To follow the delivery of every part, poll each ID with GET /api/v1/messages/{id}, or subscribe to the message.* webhooks, which fire once per message.
Single-Attachment Sends Are Unchanged
A request with one attachment (or a plain text message) behaves exactly as before: one message, one status, related_message_ids: [].
For convenience, attachments also accepts a bare string instead of an array — "attachments": "att_abc123" is treated as ["att_abc123"].
Request Body Reference
| Field | Type | Required | Description |
|---|---|---|---|
conversation_id | string | Yes* | Target conversation ID |
text | string | No | Caption text (optional for most media types) |
message_type | string | Yes | image, document, audio, or video |
attachments | array or string | Yes | Attachment IDs from the Attachments API. A bare string is accepted as a one-element list. Several IDs are split into one message per attachment on chat channels — see Sending Several Attachments at Once. |
attachment_filenames | array | No | Per-message display-filename overrides, aligned positionally by index to attachments. Falls back to the stored filename when omitted, null, or empty. See Custom Display Filenames. |
Response Fields
| Field | Type | Description |
|---|---|---|
related_message_ids | array | IDs of the additional messages created by the same request, in delivery order. Empty for every send that was not split. |
*Or use contact_method_id / contact_id + channel_id as in the text message guide.
Python Example
import requests
BASE_URL = "https://api.sendseven.com/api/v1"
HEADERS = {
"Authorization": "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
}
# Step 1: Upload the image
with open("receipt-001.png", "rb") as f:
upload_response = requests.post(
f"{BASE_URL}/attachments",
headers=HEADERS,
files={"file": f},
)
attachment = upload_response.json()
print(f"Uploaded: {attachment['id']}")
# Step 2: Send the image message
HEADERS["Content-Type"] = "application/json"
response = requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"contact_id": "contact_d4e5f6a7",
"channel_id": "ch_wa_001",
"text": "Here is your receipt.",
"message_type": "image",
"attachments": [attachment["id"]],
},
)
print(f"Image sent: {response.json()['id']}")
# Send a document to a conversation
with open("contract.pdf", "rb") as f:
upload_response = requests.post(
f"{BASE_URL}/attachments",
headers={"Authorization": HEADERS["Authorization"]},
files={"file": ("Service-Contract-2026.pdf", f)},
)
doc_attachment = upload_response.json()
response = requests.post(
f"{BASE_URL}/messages",
headers=HEADERS,
json={
"conversation_id": "conv_8f3a2b1c",
"text": "Please find the contract attached.",
"message_type": "document",
"attachments": [doc_attachment["id"]],
# Recipient sees "Service-Contract-2026.pdf" instead of the stored name
"attachment_filenames": ["Service-Contract-2026.pdf"],
},
)
print(f"Document sent: {response.json()['id']}")
JavaScript Example
const BASE_URL = "https://api.sendseven.com/api/v1";
const AUTH_HEADER = "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00";
// Step 1: Upload the image
const formData = new FormData();
formData.append("file", fileBlob, "new-product.jpg");
const uploadResponse = await fetch(`${BASE_URL}/attachments`, {
method: "POST",
headers: { Authorization: AUTH_HEADER },
body: formData,
});
const attachment = await uploadResponse.json();
console.log(`Uploaded: ${attachment.id}`);
// Step 2: Send the image message
const response = await fetch(`${BASE_URL}/messages`, {
method: "POST",
headers: {
Authorization: AUTH_HEADER,
"Content-Type": "application/json",
},
body: JSON.stringify({
conversation_id: "conv_8f3a2b1c",
text: "Check out our new product!",
message_type: "image",
attachments: [attachment.id],
}),
});
const message = await response.json();
console.log(`Image sent: ${message.id}`);
Response
All media messages return the same response format:
{
"id": "msg_media_001",
"conversation_id": "conv_8f3a2b1c",
"direction": "outbound",
"text": "Here is your invoice for January 2026.",
"message_type": "document",
"status": "queued",
"sender_type": "api",
"created_at": "2026-02-10T15:15:00Z"
}
Error Responses
| Status | Error Code | Description |
|---|---|---|
| 401 | INVALID_TOKEN | Token is invalid or expired |
| 403 | INSUFFICIENT_SCOPE | Token lacks messages:create or attachments:create |
| 404 | RESOURCE_NOT_FOUND | Conversation, contact, or attachment not found |
| 422 | VALIDATION_ERROR | Unsupported file type, invalid attachment ID, or missing required fields |
Next Steps
- Send Text Messages -- plain text messaging
- Interactive Messages -- buttons and lists
- Carousel Messages -- multi-card carousels