daimon.email
Api referenceMessages

Get Attachment

Download a message attachment via presigned URL

Endpoints

  • GET /v1/inboxes/{id}/messages/{msgId}/attachments/{attachId} - Download via message path
  • GET /v1/inboxes/{id}/threads/{threadId}/messages/{msgId}/attachments/{attachId} - Download via thread path

Both endpoints return the same result - use whichever is more convenient for your agent's context.

Authentication

Requires the inbox's API key in the Authorization header.

Path Parameters

idstringpathrequired

Inbox ID (e.g., inb_abc123)

msgIdstringpathrequired

Message ID (e.g., msg_xyz789)

attachIdstringpathrequired

Attachment ID (e.g., att_ghi012)

threadIdstringpath

Thread ID (only for thread path variant)

Request

# Get presigned URL
curl -X GET "https://api.daimon.email/v1/inboxes/inb_abc123/messages/msg_xyz789/attachments/att_ghi012" \
  -H "Authorization: Bearer dm_free_7d8a9b0c1d2e3f4g5h6i7j8k9l0m1n2o"

# Then download the file
# curl -X GET "<presigned_url>" -o downloaded_file.pdf
const client = new DaimonClient({ apiKey: 'dm_free_...' });

// Get presigned download URL
const attachment = await client.inboxes.messages.attachments.get(
  'inb_abc123',
  'msg_xyz789',
  'att_ghi012'
);

console.log(`Download URL: ${attachment.url}`);
console.log(`Expires at: ${attachment.expires_at}`);
console.log(`Filename: ${attachment.filename}`);
console.log(`Size: ${attachment.size} bytes`);

// Download the file
const response = await fetch(attachment.url);
const blob = await response.blob();
const buffer = Buffer.from(await blob.arrayBuffer());

// Save to disk
import fs from 'fs';
fs.writeFileSync(attachment.filename, buffer);
console.log(`Downloaded: ${attachment.filename}`);
client = DaimonClient(api_key='dm_free_...')

# Get presigned download URL
attachment = client.inboxes.messages.attachments.get(
    'inb_abc123',
    'msg_xyz789',
    'att_ghi012'
)

print(f"Download URL: {attachment.url}")
print(f"Expires at: {attachment.expires_at}")
print(f"Filename: {attachment.filename}")
print(f"Size: {attachment.size} bytes")

# Download the file
import requests

response = requests.get(attachment.url)
with open(attachment.filename, 'wb') as f:
    f.write(response.content)

print(f"Downloaded: {attachment.filename}")

Response

Response

{
  "result": {
    "url": "https://r2.cloudflarestorage.com/daimon-email/attachments/inb_abc123/msg_xyz789/att_ghi012.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
    "expires_at": "2024-03-11T15:35:20Z",
    "filename": "welcome.pdf",
    "content_type": "application/pdf",
    "size": 45678
  },
  "next_steps": [
    "Download the file using the presigned URL",
    "URL expires in 1 hour - request a new URL if needed",
    "Use content_type to determine how to process the file"
  ]
}
{
  "error": "ATTACHMENT_NOT_FOUND",
  "message": "Attachment att_ghi012 not found in message msg_xyz789",
  "next_steps": [
    "Verify the attachment ID is correct",
    "Check GET /v1/inboxes/{id}/messages/{msgId} for available attachments"
  ]
}

Response Fields

result.urlstring

Presigned R2 download URL - valid for 1 hour

result.expires_atstring

ISO 8601 timestamp of when the presigned URL expires

result.filenamestring

Original filename from the email attachment

result.content_typestring

MIME type (e.g., application/pdf, image/png, text/csv)

result.sizeinteger

File size in bytes

next_stepsarray

Agent guidance for next actions

Presigned URL Behavior

Request presigned URL

Call this endpoint to get a temporary download URL

URL valid for 1 hour

The presigned URL expires after 1 hour for security

Download directly from R2

Use the URL to download the file directly from Cloudflare R2 - no API key needed

Request new URL if expired

If the URL expires, call this endpoint again to get a fresh presigned URL

Info

No authentication required for presigned URLs - the signature is embedded in the URL itself. This allows agents to download files without exposing their API keys.

Use Cases

Download all attachments from a message
const message = await client.inboxes.messages.get('inb_abc123', 'msg_xyz789');

console.log(`Message has ${message.attachments.length} attachments`);

for (const att of message.attachments) {
  const download = await client.inboxes.messages.attachments.get(
    'inb_abc123',
    message.id,
    att.id
  );

  // Download file
  const response = await fetch(download.url);
  const buffer = Buffer.from(await response.arrayBuffer());

  // Save to disk
  fs.writeFileSync(`downloads/${download.filename}`, buffer);
  console.log(`Downloaded: ${download.filename} (${download.size} bytes)`);
}
Filter by file type
const message = await client.inboxes.messages.get('inb_abc123', 'msg_xyz789');

// Only download PDF attachments
const pdfAttachments = message.attachments.filter(
  att => att.content_type === 'application/pdf'
);

for (const att of pdfAttachments) {
  const download = await client.inboxes.messages.attachments.get(
    'inb_abc123',
    message.id,
    att.id
  );

  // Process PDF...
  console.log(`PDF: ${download.filename}`);
}
Process image attachments with vision AI
const message = await client.inboxes.messages.get('inb_abc123', 'msg_xyz789');

const imageAttachments = message.attachments.filter(
  att => att.content_type.startsWith('image/')
);

for (const att of imageAttachments) {
  const download = await client.inboxes.messages.attachments.get(
    'inb_abc123',
    message.id,
    att.id
  );

  // Download image
  const response = await fetch(download.url);
  const buffer = Buffer.from(await response.arrayBuffer());

  // Process with vision AI
  const analysis = await visionAI.analyze(buffer);
  console.log(`Image analysis: ${analysis.description}`);
}
Check attachment size before downloading
const message = await client.inboxes.messages.get('inb_abc123', 'msg_xyz789');

const MAX_SIZE = 10 * 1024 * 1024; // 10MB

for (const att of message.attachments) {
  if (att.size > MAX_SIZE) {
    console.log(`Skipping large attachment: ${att.filename} (${att.size} bytes)`);
    continue;
  }

  // Download small attachments only
  const download = await client.inboxes.messages.attachments.get(
    'inb_abc123',
    message.id,
    att.id
  );

  // Process...
}
Handle expired presigned URLs
async function downloadWithRetry(inboxId, messageId, attachmentId) {
  let download = await client.inboxes.messages.attachments.get(
    inboxId,
    messageId,
    attachmentId
  );

  // Try download
  let response = await fetch(download.url);

  // If expired, get new URL
  if (response.status === 403) {
    console.log('URL expired, requesting new presigned URL...');
    download = await client.inboxes.messages.attachments.get(
      inboxId,
      messageId,
      attachmentId
    );
    response = await fetch(download.url);
  }

  return response;
}

const response = await downloadWithRetry('inb_abc123', 'msg_xyz789', 'att_ghi012');
const buffer = Buffer.from(await response.arrayBuffer());

Common MIME Types

Documents
  • application/pdf - PDF files
  • application/msword - Word (.doc)
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document - Word (.docx)
  • application/vnd.ms-excel - Excel (.xls)
  • application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - Excel (.xlsx)
  • text/plain - Plain text
  • text/csv - CSV files
Images
  • image/png - PNG
  • image/jpeg - JPEG/JPG
  • image/gif - GIF
  • image/webp - WebP
  • image/svg+xml - SVG
Archives
  • application/zip - ZIP
  • application/x-tar - TAR
  • application/gzip - GZIP
  • application/x-7z-compressed - 7Z