How to Send an Email with Attachments via the API
Problem reported by yerim kim - 9/20/2025 at 11:22 AM
Submitted

Hello,

I would like to attach a file when sending mail through the SendMessage API. Which parameter should I use for this?

I could not find the answer in the API documentation or in the community.

Derek Curtis Replied
Employee Post
Hi, Yerim

We do have an AddAttachment API for composing messages. It's documented here (you'll need to substitute your mail server address):

https://mail.your-mail-domain.com/Documentation/api#/reference/SmarterMail.Web.Api.MailController/AddAttachment/post

This call is generally used for inline attachments, hence the need for a ContentID. If you leave that part off, it MAY add a file as a standard attachment, but we've not tested that. (We're in the process of it.) One thing to note is that any file that's uploaded needs to be done using form data. 
Derek Curtis COO SmarterTools Inc. www.smartertools.com
yerim kim Replied
Thank you, Derek
I checked the documentation, but I was unable to make the API function as expected.

Could you provide an example of the two different ways to attach an image? Should I use the AddAttachment API in both cases?

Specifically, I would like to understand the difference between embedding an image inline in the email body versus attaching it as a separate file.

This image is an example of attaching a file in the webmail interface.
Derek Curtis Replied
Employee Post
Samples are provided in the documentation for Python, PHP, and C#. 

To add in inline attachment you'd use the ContentID, and to add the file as an attachment, you'd eliminate this. So the call would look something like this:

Inline Attachment:

https://mail.your-mail-domain.com/api/v1/mail/attachment/blm44xiy4bguqw1h/cidgenerate
File added as attachment
https://mail.your-mail-domain.com/api/v1/mail/attachment/blm44xiy4bguqw1h
"blm44xiy4bguqw1h" is the Attachment GUID, and "cidgenerate" is the Content ID
Derek Curtis COO SmarterTools Inc. www.smartertools.com
Matt Petty Replied
Employee Post
In the html you refer to the image like this
<IMG SRC="cid:The-Cid-From-cidgenerate" alt="Logo">
Also make sure the Guid used for Attachment GUID remains consistent, from the cidgenerate calls, to the put-message call, which also takes in an attachmentguid. The same attachment guid needs to used for  all these calls for the data to be associated together.

Matt Petty Senior Software Developer SmarterTools Inc. www.smartertools.com
Matt Petty Replied
Employee Post
You may find it easier to use SMTP to send these emails. Below is a full example using both python and ps1 to do this, including the inline attachment with HTML.

AI Cooked this up:

Sending Emails with Inline Attachments - Python & PowerShell

Instead of wrestling with API complexity, here are two straightforward approaches to send emails with inline attachments.

Python Example


import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

def send_email_with_inline_attachment():
    # Email configuration
    smtp_server = "your-mail-server.com"
    smtp_port = 587  # or 465 for SSL
    username = "your-email@domain.com"
    password = "your-password"
    
    # Email details
    from_email = "your-email@domain.com"
    to_email = "recipient@domain.com"
    subject = "Email with Inline Image"
    
    # Create message container
    msg = MIMEMultipart('related')
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = to_email
    
    # Create the HTML body with embedded image
    html_body = """
    <html>
      <body>
        <h2>Hello from Python!</h2>
        <p>Here's an inline image:</p>
        <img src="cid:image1" alt="Embedded Image" width="300">
        <p>Pretty cool, right?</p>
      </body>
    </html>
    """
    
    # Attach HTML body
    msg.attach(MIMEText(html_body, 'html'))
    
    # Add the image as inline attachment
    with open('your-image.png', 'rb') as f:
        img = MIMEImage(f.read())
        img.add_header('Content-ID', '<image1>')  # This matches the cid: in HTML
        img.add_header('Content-Disposition', 'inline', filename='your-image.png')
        msg.attach(img)
    
    # Send the email
    try:
        server = smtplib.SMTP(smtp_server, smtp_port)
        server.starttls()  # Enable TLS encryption
        server.login(username, password)
        server.send_message(msg)
        server.quit()
        print("Email sent successfully!")
    except Exception as e:
        print(f"Error sending email: {e}")

# Run it
if __name__ == "__main__":
    send_email_with_inline_attachment()

PowerShell Example


# Email configuration
$SmtpServer = "your-mail-server.com"
$SmtpPort = 587
$Username = "your-email@domain.com"
$Password = "your-password"

# Email details
$From = "your-email@domain.com"
$To = "recipient@domain.com"
$Subject = "Email with Inline Image from PowerShell"

# Create secure credentials
$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword)

# Create the email message
$Message = New-Object System.Net.Mail.MailMessage
$Message.From = $From
$Message.To.Add($To)
$Message.Subject = $Subject
$Message.IsBodyHtml = $true

# HTML body with inline image reference
$HtmlBody = @"
<html>
  <body>
    <h2>Hello from PowerShell!</h2>
    <p>Here's an inline image:</p>
    <img src="cid:image1" alt="Embedded Image" width="300">
    <p>Sent with PowerShell magic! ✨</p>
  </body>
</html>
"@

$Message.Body = $HtmlBody

# Add inline attachment
$ImagePath = "C:\path\to\your-image.png"
if (Test-Path $ImagePath) {
    $Attachment = New-Object System.Net.Mail.Attachment($ImagePath)
    $Attachment.ContentId = "image1"  # This matches the cid: in HTML
    $Attachment.ContentDisposition.Inline = $true
    $Message.Attachments.Add($Attachment)
}

# Create SMTP client and send
try {
    $SmtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $SmtpPort)
    $SmtpClient.EnableSsl = $true
    $SmtpClient.Credentials = $Credential
    
    $SmtpClient.Send($Message)
    Write-Host "Email sent successfully!" -ForegroundColor Green
}
catch {
    Write-Host "Error sending email: $($_.Exception.Message)" -ForegroundColor Red
}
finally {
    # Clean up
    $Message.Dispose()
    if ($SmtpClient) { $SmtpClient.Dispose() }
}

Key Points for Inline Attachments

The Magic of Content-ID (CID)

  • Python: Use img.add_header('Content-ID', '<image1>')
  • PowerShell: Set $Attachment.ContentId = "image1"
  • HTML: Reference with <img src="cid:image1">
The Content-ID creates the link between your HTML reference and the actual attachment.

Regular vs Inline Attachments

  • Inline: Shows up embedded in the email body
  • Regular: Appears as downloadable files at the bottom/top
  • Key difference: The Content-Disposition: inline header and the CID reference
Matt Petty Senior Software Developer SmarterTools Inc. www.smartertools.com
yerim kim Replied

Derek, Matt — thank you for your answers.
I wasn’t able to reply earlier due to other work, sorry about that.

I tried using the AddAttachment API.
Since I am already familiar with SMTP, I am focusing on implementing this through the API instead.

This is my API written in C#:

var uri = MyDomain + "api/v1/mail/attachment";
var token = HttpContext.Current.Session["Token"].ToString();
var serializer = new JavaScriptSerializer();

var input_attachmentID = "attach1";
var input_CID = "cid1";

//uri = uri + "/" + input_attachmentID;
uri = uri + "/" + input_attachmentID + "/" + input_CID;

try
{
    var input_post = new
    {
        
    };

    var json = JsonConvert.SerializeObject(input_post);
    byte[] result = Encoding.UTF8.GetBytes(json.ToString());
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.Method = "POST";
    request.ContentType = "application/json;charset=UTF-8";
    request.ContentLength = result.Length;
    request.Headers["Authorization"] = "Bearer " + token;

    Stream postDataStream = request.GetRequestStream();
    postDataStream.Write(result, 0, result.Length);
    postDataStream.Close();

    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    using (var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
    {
        return reader.ReadToEnd();
    }
}

The request is returning a 500 Error, which makes me think that my URL might be incorrect. However, other APIs on the same domain are working fine, so I don’t believe it’s a domain or server issue.

I assumed that the attachmentID could be set arbitrarily, but perhaps my approach is incorrect?

If I want to retrieve a file from the server or from my local machine, should I pass that file as a parameter in the request?”

Matt Petty Replied
Employee Post
var uri = MyDomain + "api/v1/attachment-put";
var token = HttpContext.Current.Session["Token"].ToString();

try
{
    // For file uploads, use multipart/form-data instead of JSON
    var request = (HttpWebRequest)WebRequest.Create(uri);
    request.Method = "POST";
    request.Headers["Authorization"] = "Bearer " + token;
    
    // Create multipart form data
    string boundary = "----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x");
    request.ContentType = "multipart/form-data; boundary=" + boundary;
    
    using (var requestStream = request.GetRequestStream())
    {
        // Add file content here
        byte[] fileBytes = File.ReadAllBytes("path/to/your/file.pdf"); // Replace with actual file
        string fileName = "your-file.pdf";
        
        string header = $"--{boundary}\r\n" +
                       $"Content-Disposition: form-data; name=\"file\"; filename=\"{fileName}\"\r\n" +
                       $"Content-Type: application/octet-stream\r\n\r\n";
        
        byte[] headerBytes = Encoding.UTF8.GetBytes(header);
        requestStream.Write(headerBytes, 0, headerBytes.Length);
        requestStream.Write(fileBytes, 0, fileBytes.Length);
        
        string footer = $"\r\n--{boundary}--\r\n";
        byte[] footerBytes = Encoding.UTF8.GetBytes(footer);
        requestStream.Write(footerBytes, 0, footerBytes.Length);
    }

    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    using (var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
    {
        return reader.ReadToEnd();
    }
}
catch (Exception ex)
{
    // Handle the exception
    throw new Exception($"Attachment upload failed: {ex.Message}");
}
I ran your code through some AI against our codebase to correct it. I did not test this myself but I double checked the code and it should correct your issue. 

The primary issues was the 
-endpoint is just POST /api/v1/attachment-put    (no params)
-Need to send the request as "form-data"


Another pro-tip for using the API is to do the same thing in the browser while you have the network inspect window open in chrome and you can see how the request looks, the URL, the type, headers, post and return data.
Matt Petty Senior Software Developer SmarterTools Inc. www.smartertools.com
yerim kim Replied
Thanks to your example, I was able to successfully communicate with the API.

The issue was with the data format I was sending, and the endpoint required the [MyDomain/api/v1/mail/attachment/attachID] format. Thank you very much!

Here is the final example I used.
var uri = MyDomain + "api/v1/mail/attachment";
var token = HttpContext.Current.Session["Token"].ToString();
var serializer = new JavaScriptSerializer();

var input_attachmentID = "attach1";

uri = uri + "/" + input_attachmentID;

var file = File.ReadAllBytes(@"path/my/file.png");

try
{
	HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
	request.Method = "POST";
	request.Headers["Authorization"] = "Bearer " + token;
	
	string boundary = "----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x");
	request.ContentType = "multipart/form-data; boundary=" + boundary;

	using (var requestStream = request.GetRequestStream())
	{
		// Add file content here
		string fileName = "file.png";

		string header = $"--{boundary}\r\n" +
				$"Content-Disposition: form-data; name=\"file\"; filename=\"{fileName}\"\r\n" +
				$"Content-Type: application/octet-stream\r\n\r\n";

		byte[] headerBytes = Encoding.UTF8.GetBytes(header);
		requestStream.Write(headerBytes, 0, headerBytes.Length);
		requestStream.Write(file, 0, file.Length);

		string footer = $"\r\n--{boundary}--\r\n";
		byte[] footerBytes = Encoding.UTF8.GetBytes(footer);
		requestStream.Write(footerBytes, 0, footerBytes.Length);
	}

	HttpWebResponse response = (HttpWebResponse)request.GetResponse();
	using (var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
	{
		return reader.ReadToEnd();
	}
}
catch (Exception ex)
{
	var errorResult = new { success = false, message = "Error: " + ex.Message };
	return serializer.Serialize(errorResult);
}

Reply to Thread

Enter the verification text