/mail/{id}/attachment/{n}
GETDownload a specific attachment file from an email.
Description
This endpoint downloads a specific attachment file from an email. It returns the binary file content with appropriate HTTP headers including Content-Type, Content-Disposition, X-Attachment-ID, and X-Email-ID. The response Content-Type is automatically determined based on the file extension.
Binary File Response
This endpoint returns raw binary file data, not JSON. The Content-Disposition header includes the original filename to enable proper file downloads. Custom headers provide additional metadata about the attachment and email.
Interactive API Tester
Try it out
Path Parameters
The request requires two path parameters to identify the email and specific attachment.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | The unique identifier of the email. Must be a 32-character hexadecimal string (e.g., "a1b2c3d4e5f6789012345678abcdef90"). |
n |
integer | Yes | The attachment ID. Must be a positive integer (e.g., 1, 2, 3). This value corresponds to the attachment_id from the attachments list endpoint. |
Response
Upon success, the endpoint returns a 200 OK response with the binary file content and appropriate headers.
Response Headers
Response Body
Example Response Headers
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="invoice.pdf"
Content-Length: 245760
X-Attachment-ID: 1
X-Email-ID: a1b2c3d4e5f6789012345678abcdef90
[Binary file data]
Common Content-Types by Extension
- PDF: application/pdf
- Images (JPEG, PNG, GIF): image/jpeg, image/png, image/gif
- Documents (DOCX, XLSX): application/vnd.openxmlformats-officedocument.*
- Text files: text/plain
- ZIP archives: application/zip
- Unknown/Other: application/octet-stream
Examples
JavaScript Example
const apiHost = 'http://localhost:8080';
const emailId = 'a1b2c3d4e5f6789012345678abcdef90';
const attachmentId = 1;
async function downloadAttachment(emailId, attachmentId) {
try {
const response = await fetch(
`${apiHost}/api/mail/${emailId}/attachment/${attachmentId}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Get filename from Content-Disposition header
const contentDisposition = response.headers.get('Content-Disposition');
const filenameMatch = contentDisposition.match(/filename="(.+)"/);
const filename = filenameMatch ? filenameMatch[1] : `attachment_${attachmentId}`;
// Get metadata from custom headers
const attachmentIdHeader = response.headers.get('X-Attachment-ID');
const emailIdHeader = response.headers.get('X-Email-ID');
console.log(`Downloading: ${filename}`);
console.log(`Email ID: ${emailIdHeader}`);
console.log(`Attachment ID: ${attachmentIdHeader}`);
// Get the file blob
const blob = await response.blob();
// Create download link
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
// Cleanup
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
console.log('Download initiated successfully');
} catch (error) {
console.error('Error downloading attachment:', error);
}
}
downloadAttachment(emailId, attachmentId);
cURL Example
# Download attachment and save with original filename
curl -X GET "http://localhost:8080/api/mail/a1b2c3d4e5f6789012345678abcdef90/attachment/1" \
-J -O
# Download and view headers
curl -X GET "http://localhost:8080/api/mail/a1b2c3d4e5f6789012345678abcdef90/attachment/1" \
-i -o attachment.bin
# Download to specific filename
curl -X GET "http://localhost:8080/api/mail/a1b2c3d4e5f6789012345678abcdef90/attachment/1" \
-o "downloaded_invoice.pdf"
Go Example
package main
import (
"fmt"
"io"
"net/http"
"os"
"regexp"
)
const apiHost = "http://localhost:8080"
func downloadAttachment(emailID string, attachmentID int) error {
// Build URL
url := fmt.Sprintf("%s/api/mail/%s/attachment/%d", apiHost, emailID, attachmentID)
// Make GET request
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status: %s", resp.Status)
}
// Extract filename from Content-Disposition header
contentDisposition := resp.Header.Get("Content-Disposition")
re := regexp.MustCompile(`filename="(.+)"`)
matches := re.FindStringSubmatch(contentDisposition)
filename := fmt.Sprintf("attachment_%d", attachmentID)
if len(matches) > 1 {
filename = matches[1]
}
// Get metadata from custom headers
attachmentIDHeader := resp.Header.Get("X-Attachment-ID")
emailIDHeader := resp.Header.Get("X-Email-ID")
fmt.Printf("Downloading: %s\n", filename)
fmt.Printf("Email ID: %s\n", emailIDHeader)
fmt.Printf("Attachment ID: %s\n", attachmentIDHeader)
// Create output file
outFile, err := os.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer outFile.Close()
// Write response body to file
bytesWritten, err := io.Copy(outFile, resp.Body)
if err != nil {
return fmt.Errorf("failed to write file: %w", err)
}
fmt.Printf("Downloaded %d bytes to %s\n", bytesWritten, filename)
return nil
}
func main() {
emailID := "a1b2c3d4e5f6789012345678abcdef90"
attachmentID := 1
if err := downloadAttachment(emailID, attachmentID); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
}
Related Endpoints
/mail/{id}/attachments
Get a list of all attachments for a specific email with metadata including IDs, names, and sizes.