AgentPost
Core Concepts

Attachments

Upload and download files attached to email messages

Attachments are files associated with email messages. AgentPost stores attachments in S3-compatible object storage (Cloudflare R2) and provides secure, time-limited access through presigned URLs.

Key properties

PropertyDescription
idUnique identifier (prefix att_)
filenameOriginal file name
mime_typeMIME content type (e.g., application/pdf)
sizeFile size in bytes
message_idThe message this attachment belongs to

Upload flow

Uploading an attachment is a two-step process:

  1. Request a presigned upload URL from the AgentPost API
  2. Upload the file directly to S3 using the presigned URL

This approach keeps large files off the API server and uploads them directly to object storage.

// Step 1: Get a presigned upload URL
const upload = await client.attachments.getUploadUrl({
  filename: 'quarterly-report.pdf',
  mime_type: 'application/pdf',
  size: 2_450_000, // bytes
});

// Step 2: Upload the file to S3
const fileBuffer = fs.readFileSync('./quarterly-report.pdf');
await fetch(upload.upload_url, {
  method: 'PUT',
  body: fileBuffer,
  headers: {
    'Content-Type': 'application/pdf',
  },
});

// Step 3: Use the attachment key when sending a message
const message = await client.messages.send('inb_abc123', {
  to: [{ email: 'cfo@acmeco.com', name: 'CFO' }],
  subject: 'Q1 2026 Report',
  text_body: 'Please find the quarterly report attached.',
  attachments: [{ key: upload.key, filename: 'quarterly-report.pdf', mime_type: 'application/pdf' }],
});
# Step 1: Get a presigned upload URL
upload = client.attachments.get_upload_url(
    filename="quarterly-report.pdf",
    mime_type="application/pdf",
    size=2_450_000,
)

# Step 2: Upload the file to S3
import requests

with open("./quarterly-report.pdf", "rb") as f:
    requests.put(
        upload.upload_url,
        data=f,
        headers={"Content-Type": "application/pdf"},
    )

# Step 3: Use the attachment key when sending
message = client.messages.send(
    inbox_id="inb_abc123",
    to=[{"email": "cfo@acmeco.com", "name": "CFO"}],
    subject="Q1 2026 Report",
    text_body="Please find the quarterly report attached.",
    attachments=[{"key": upload.key, "filename": "quarterly-report.pdf", "mime_type": "application/pdf"}],
)
# Step 1: Get a presigned upload URL
UPLOAD=$(curl -s -X POST https://api.agent-post.dev/api/v1/attachments/upload-url \
  -H "Authorization: Bearer $AGENTPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "quarterly-report.pdf", "mime_type": "application/pdf", "size": 2450000}')

UPLOAD_URL=$(echo $UPLOAD | jq -r '.upload_url')
KEY=$(echo $UPLOAD | jq -r '.key')

# Step 2: Upload the file
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @quarterly-report.pdf

# Step 3: Send with the attachment
curl -X POST https://api.agent-post.dev/api/v1/inboxes/inb_abc123/messages \
  -H "Authorization: Bearer $AGENTPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"to\": [{\"email\": \"cfo@acmeco.com\"}],
    \"subject\": \"Q1 2026 Report\",
    \"text_body\": \"Please find the quarterly report attached.\",
    \"attachments\": [{\"key\": \"$KEY\", \"filename\": \"quarterly-report.pdf\", \"mime_type\": \"application/pdf\"}]
  }"

Downloading attachments

Download an attachment from a received message:

const attachment = await client.attachments.get('att_xyz789');

// The response includes a presigned download URL
const response = await fetch(attachment.download_url);
const buffer = await response.arrayBuffer();
fs.writeFileSync(`./downloads/${attachment.filename}`, Buffer.from(buffer));
attachment = client.attachments.get("att_xyz789")

# Download from the presigned URL
import requests

response = requests.get(attachment.download_url)
with open(f"./downloads/{attachment.filename}", "wb") as f:
    f.write(response.content)
# Get attachment details including download URL
ATTACHMENT=$(curl -s "https://api.agent-post.dev/api/v1/attachments/att_xyz789" \
  -H "Authorization: Bearer $AGENTPOST_API_KEY")

DOWNLOAD_URL=$(echo $ATTACHMENT | jq -r '.download_url')

# Download the file
curl -o "downloaded-file.pdf" "$DOWNLOAD_URL"

Size limits

ConstraintLimit
Maximum file size10 MB per attachment
Maximum attachments per messageNo hard limit (subject to total message size)

Size validation

The size parameter in the upload URL request must match the actual file size. If the uploaded file exceeds the declared size, the upload will be rejected by S3.

Inbound attachments

When your inbox receives an email with attachments, AgentPost automatically:

  1. Extracts attachments from the email
  2. Stores them in S3
  3. Creates attachment records linked to the message
  4. Makes them available via the GET /api/v1/attachments/:id endpoint

The message's has_attachments field indicates whether an inbound message has files attached.

Relationships

  • Every attachment belongs to one message (message_id)
  • Attachments are verified for organization ownership through the message -> inbox chain
  • Drafts can reference attachments via attachments_meta

Tips

  • Always use presigned URLs for uploads -- do not try to send file contents through the API directly
  • Presigned URLs are time-limited (typically 15 minutes). Generate a new URL if the old one expires.
  • The mime_type you declare should match the actual file content for correct handling by email clients
  • For inbound messages with attachments, check has_attachments before making additional API calls
  • Attachment download URLs are also time-limited. Do not cache them for long-term use.

On this page