Tutorial: Send Attachments with drafts
Create, upload, and attach files to drafts or existing messages using the correct attachment workflow.
Create draft attachments first, upload file bytes via a pre-signed URL, then update or send the draft. This guide explains how to:
- Attach files to a customer draft before sending
- Upload file contents using pre-signed URLs
- Add attachments to an existing message
Sending attachments with a draft
Follow this sequence to ensure attachments are correctly associated before sending.
1. Create the draft
Create a draft without scheduling or sending it.
POST /v1/customers/{customerId}/drafts- Include required channel-specific fields
- Do not include
sendAtunless you intend to send or schedule immediately - Save the returned
data.idasdraftId
2. Create the draft attachment
Create an attachment resource for the draft.
POST /v1/drafts/{draftId}/attachments
Request body:
{
"name": "invoice.pdf",
"contentType": "application/pdf",
"contentLength": 245760
}
Response includes:
- Draft attachment resource
- Pre-signed upload URL
Example response:
{
"data": {
"id": "58c766aadd4b90110053bd81",
"type": "attachment",
"attributes": {
"name": "invoice.pdf",
"contentType": "application/pdf",
"contentLength": 245760
},
"relationships": {
"draft": {
"data": {
"type": "draft",
"id": "57323b90b7579a2a004f1316"
}
}
},
"links": {
"self": "/v1/drafts/57323b90b7579a2a004f1316/attachments/58c766aadd4b90110053bd81",
"related": "https://your-presigned-upload-url"
}
}
}
3. Upload the file contents
Upload the file using the pre-signed URL returned in the previous step.
- Use the provided URL exactly as returned
- Follow the upload method specified in the response
- Ensure the upload completes successfully before proceeding
Example:
curl --upload-file ./invoice.pdf
"https://your-presigned-upload-url"
4. Update or send the draft
Finalize or schedule the draft.
PUT /v1/drafts/{draftId}
Example:
{
"channel": "email",
"sendAt": "2026-05-01T15:00:00.000Z",
"scheduled": true
}
- Include
sendAtto schedule or send - Include
scheduled: truewhen scheduling for the future - Ensure all channel-specific fields match OpenAPI definitions
Add attachments to an existing message
Attach files to an already created message.
PUT /v1/messages/{messageId}
Request body:
{
"attachments": [
{
"_id": "507f1f77bcf86cd799439011",
"name": "receipt.png",
"contentType": "image/png",
"contentLength": 8911
}
]
}
Request fields
| Field | Required | Description |
|---|---|---|
_id | Yes | Attachment ID |
name | Yes | File name |
contentType | Yes | MIME type |
contentLength | Yes | File size in bytes |
sourceId | No | External mapping identifier |
Important notes and details
- Drafts do not include attachments until you explicitly create and upload them.
- File bytes are uploaded separately from metadata using a pre-signed URL.
- A draft can be safely updated or scheduled after attachments are uploaded.
- Messages accept attachments via an attachments array update.
Recommended action
- Use draft attachments (
POST /drafts/{draftId}/attachments) for all new draft workflows - Do not use standalone attachment creation for draft flows
- Always upload file contents before sending or scheduling drafts
- Validate channel-specific fields before updating drafts
Errors and troubleshooting
- Draft sends without attachment
- Cause:
sendAtset before upload completed - Fix: Upload file before updating draft
- Cause:
- Upload fails
- Cause: Incorrect use of pre-signed URL
- Fix: Use exact URL and method returned by API
- Attachment not visible on message
- Cause: Missing or incorrect
_id - Fix: Verify attachment ID from creation response
- Cause: Missing or incorrect
Updated about 2 hours ago