You can send images, videos, documents, and other files as iMessage attachments.
Incoming messages with attachments include metadata and a download URL for each file.
Send an attachment
Upload a file first, then reference it by ID when sending a message:
import { createClient } from "@messages-dev/sdk";
const client = createClient();
const file = await client.uploadFile({
from: "+15551234567",
file: Buffer.from(imageData),
filename: "photo.jpg",
mimeType: "image/jpeg",
});
await client.sendMessage({
from: "+15551234567",
to: "+15559876543",
text: "Check out this photo!",
attachments: [file.id],
});
await client.sendMessage({
from: "+15551234567",
to: "+15559876543",
attachments: [file.id],
});
| Field | Required | Description |
|---|
to | Yes | Recipient phone number or Apple ID |
text | No | Optional message text (can be omitted when sending a file) |
attachments | No | Array of file IDs (max 1) from the upload endpoint |
At least one of text or attachments must be provided.
Supported file types
iMessage supports most common file types. The recipient’s device determines how
the attachment is displayed:
| Type | Examples | Display |
|---|
| Images | .jpg, .png, .gif, .heic, .webp | Inline preview |
| Videos | .mp4, .mov | Inline player |
| Audio | .mp3, .m4a, .aac | Audio player |
| Documents | .pdf, .docx, .xlsx | File icon with preview |
| Other | Any file type | Generic file attachment |
Receive attachments
Incoming messages with attachments include an attachments array with metadata
and a download URL for each file:
{
"event": "message.received",
"data": {
"id": "msg_abc123",
"sender": "+15559876543",
"text": null,
"attachments": [
{
"filename": "IMG_1234.jpg",
"mime_type": "image/jpeg",
"size": 245760,
"url": "https://storage.convex.cloud/..."
}
],
"sent_at": 1710000005000
}
}
Handle attachments in your webhook:
import { verifyWebhook } from "@messages-dev/sdk";
app.post("/webhooks", async (req, res) => {
const event = await verifyWebhook(
req.body,
req.headers["x-webhook-signature"],
"your_webhook_secret",
);
if (event.event === "message.received") {
if (event.data.attachments?.length) {
for (const att of event.data.attachments) {
console.log(`Received: ${att.filename} (${att.mimeType}, ${att.size} bytes)`);
console.log(`Download: ${att.url}`);
}
}
if (event.data.text) {
console.log(`Text: ${event.data.text}`);
}
}
res.sendStatus(200);
});
You can also retrieve attachment URLs when listing messages:
const messages = await client.listMessages({
from: "+15551234567",
to: "+15559876543",
});
for (const msg of messages.data) {
for (const att of msg.attachments) {
console.log(att.url);
console.log(att.filename);
console.log(att.mimeType);
}
}