Attachments · MachineMail Docs
Send an attachment
Add an attachments array to a send, reply, reply-all or forward. Each attachment has either content (base64) or url.
A message can carry at most 100 attachments and 10 MB of files in total (decoded), whether they come from content or url; a request body is at most 16 MB. Larger mail is refused with a 400 validation error before anything is sent: link to big files instead. content must be valid base64 (standard or URL-safe alphabet, padding and line breaks optional); anything else is a 400 whose param names it, for example attachments[0].content. Filenames can use any characters, including non-Latin scripts.
| Field | Meaning |
|---|---|
filename |
File name shown to the recipient. With url, defaults to the last path segment. |
content_type |
MIME type. With url, defaults to the response’s Content-Type. |
content |
The file, base64 encoded. |
url |
A public http or https URL that MachineMail downloads when it sends. |
content_disposition |
attachment (default) or inline. |
content_id |
For inline images: the ID your HTML refers to as cid:<content_id>. |
Example Code
curl
curl https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/send \
-H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{\n "to": ["ada@example.com"],\n "subject": "Your report",\n "text": "Report attached.",\n "attachments": [\
{ "filename": "report.csv", "content_type": "text/csv", "content": "ZGF0ZSx0b3RhbAoyMDI2LTEwLTA4LDQy" },\
{ "url": "https://files.acme.dev/q3-summary.pdf" }\
]\n }'
TypeScript
import { readFile } from 'node:fs/promises';
const content = (await readFile('report.csv')).toString('base64');
await fetch('https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/send', {
method: 'POST',
headers: { Authorization: `Bearer \\$MACHINEMAIL_API_KEY`, 'Content-Type': 'application/json' },
body: JSON.stringify({
to: ['ada@example.com'],
subject: 'Your report',
text: 'Report attached.',
attachments: [{ filename: 'report.csv', content_type: 'text/csv', content }],
}),
});
Python
import base64, os, requests
with open("report.csv", "rb") as f:
content = base64.b64encode(f.read()).decode()
requests.post(
"https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/send",
headers={"Authorization": f"Bearer {os.environ['MACHINEMAIL_API_KEY']}"},
json={
"to": ["ada@example.com"],
"subject": "Your report",
"text": "Report attached.",
"attachments": [{"filename": "report.csv", "content_type": "text/csv", "content": content}],
},
).raise_for_status()
Attachments from a URL
With url, MachineMail fetches the file at send time. The rules:
- Only
httpandhttpsURLs that resolve to public addresses. Private, loopback and link-local addresses are refused. - Redirects are not followed. Use the final URL; for signed storage links, sign the object URL itself.
- At most 10 MB per file (and 10 MB for all attachments together), and the download must finish within 20 seconds.
- Any failure (a non-2xx status, a timeout, a file too large) rejects the whole send with a
validationerror whoseparamnames the attachment, for exampleattachments[1].url. Nothing is sent with a missing file.
Inline images
Give the image a content_id, set content_disposition to inline, and refer to it from the HTML body. The content_id is what makes it inline: an image with content_disposition: inline but no content_id is delivered as an ordinary attachment.
Drafts
Drafts take the same attachments as a send, base64 content or url. A url is fetched when the draft is sent (for a scheduled draft, at send_at), so its size is 0 until then and a URL that has stopped working makes the send fail.
Forwarding
A REST forward does not copy the original message’s attachments. To pass them on, download them and add them to the forward’s attachments.
Download
Get the attachment’s metadata and a download URL:
Example Code
curl https://api.machinemail.dev/v0/inboxes/support-agent@machinemail.dev/messages/msg_01m4fybrc20000ypd31v108d71/attachments/att_01m4fybrc10000qyygf5yv5thv \
-H "Authorization: Bearer $MACHINEMAIL_API_KEY"
Response
{
"attachment_id": "att_01m4fybrc10000qyygf5yv5thv",
"filename": "invoice.pdf",
"size": 13,
"content_type": "application/pdf",
"content_disposition": "attachment",
"download_url": "https://...",
"expires_at": "2026-10-09T10:02:01.949Z"
}
Check the risk first
Before an agent opens a file, read the matching entry in trust.attachments:
[
{
"attachment_id": "att_01m4fybrc10000qyygf5yv5thv",
"filename": "invoice.pdf",
"content_type": "application/pdf",
"risk": "low",
"reasons": []
}
]
Warning
Never execute or render a high risk attachment, and do not hand any attachment to a tool that can run code. If an agent needs a file’s contents, prefer extracted_text.
Attachments are fetched per message.
There is no thread-level attachment download endpoint; use the message that carries the file.