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 sendAt unless you intend to send or schedule immediately
  • Save the returned data.id as draftId

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 sendAt to schedule or send
  • Include scheduled: true when 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

FieldRequiredDescription
_idYesAttachment ID
nameYesFile name
contentTypeYesMIME type
contentLengthYesFile size in bytes
sourceIdNoExternal 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: sendAt set before upload completed
    • Fix: Upload file before updating draft
  • 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