How to include file attachments when sending emails through the Mailgun API

I’m having trouble getting file attachments to work when sending emails via the Mailgun API. The email gets delivered successfully but the attached file never shows up. When I check the Mailgun dashboard logs, I can see that the attachments array is completely empty.

Here’s my current code setup:

$document_path = 'uploads/report.docx';
$email_data = array(
    'from' => 'Support Team <[email protected]>',
    'to' => '[email protected]',
    'subject' => 'Monthly Report',
    'text' => 'Please find the attached report.',
    'attachment[1]' => '@' . $document_path
);

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'https://api.mailgun.net/v3/mysite.com/messages');
curl_setopt($curl, CURLOPT_USERPWD, 'api:key-MySecretApiKey');
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data'));
curl_setopt($curl, CURLOPT_POSTFIELDS, $email_data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($curl);

The API response looks normal:

{
    "id": "<[email protected]>",
    "message": "Queued. Thank you."
}

But in the logs, no attachments are showing:

"message": {
    "headers": {
        "to": "[email protected]",
        "message-id": "[email protected]",
        "from": "Support Team <[email protected]>",
        "subject": "Monthly Report"
    },
    "attachments": [],
    "size": 349
}

The file exists and has proper read permissions. What am I missing here?

check if your file path is actually correct by doing a quick file_exists() check before the curl call. i had this same problem and it turned out the file wasnt where i thought it was. also try using the full server path instead of relative path - sometimes mailgun needs the complete file path to work properly.

I’ve encountered this attachment issue before and there’s another potential culprit beyond the Content-Type header. Your attachment syntax looks correct, but double-check that you’re using CURLFile objects instead of the @ prefix method. The @ syntax has been deprecated in newer PHP versions and doesn’t always work reliably with Mailgun. Try replacing your attachment line with: ‘attachment[1]’ => new CURLFile($document_path, mime_content_type($document_path), basename($document_path)). This approach gives you better control over the MIME type and filename. Also verify that your PHP version supports the file upload properly - I’ve seen cases where certain PHP configurations silently ignore the @ prefix without throwing errors. The fact that you’re getting a successful API response but empty attachments array strongly suggests it’s a file handling issue rather than authentication or endpoint problems.

I ran into this exact issue a few months back and it drove me crazy for days. The problem is with how you’re setting the Content-Type header manually. When you explicitly set Content-Type: multipart/form-data, cURL can’t properly handle the multipart boundary that’s needed for file uploads. Remove this line completely: curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data')); CURL will automatically set the correct Content-Type header with the proper boundary when it detects file uploads in your POST data. Also make sure your file path is absolute or properly relative to where your script is running from. I had cases where the path looked correct but was actually relative to a different directory than expected. After removing that header line, your attachments should start showing up in the Mailgun logs and actually get delivered with the emails.