# SMTP

Use Make Sender from any SMTP client or library instead of the REST API.

## Overview

Messages sent over SMTP are queued and DKIM-signed exactly like REST API submissions. The size limit is 25 MB.

| Setting | Value |
| --- | --- |
| `Host` | smtp.makesender.no |
| `Port` | 2525 |
| `Encryption` | STARTTLS |
| `Authentication` | PLAIN |

- **SMTP credentials:** Create them in your account settings. They belong to the account and work for all its verified domains. Create separate ones for each application or environment.
- **A verified domain:** Verify the domain of the sender address you want to use.

## Sending via SMTP

Ruby · net/smtp:

```ruby
require 'net/smtp'

message = <<~MESSAGE
From: sender@example.com
To: recipient@example.com
Subject: Hello from SMTP

This is the message body.
MESSAGE

Net::SMTP.start('smtp.makesender.no', 2525, 'example.com',
                'your_username', 'your_password', :plain) do |smtp|
  smtp.send_message message, 'sender@example.com', 'recipient@example.com'
end
```

Rails · config/environments/production.rb:

```ruby
# config/environments/production.rb

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "smtp.makesender.no",
  port: 2525,
  domain: "your-domain.com",
  user_name: "your_smtp_username",
  password: "your_smtp_password",
  authentication: "plain",
  enable_starttls_auto: true
}
```

Swaks:

```bash
# Install
brew install swaks

# Send email
swaks --auth --tls \
      --server smtp.makesender.no:2525 \
      --au your_username \
      --ap your_password \
      --to recipient@example.com \
      --from sender@example.com \
      --h-Subject: "Hello from SMTP" \
      --body 'This is a test message'
```

curl · SMTP:

```bash
curl -v --ssl-reqd smtp://smtp.makesender.no:2525 \
  --mail-from "sender@example.com" \
  --mail-rcpt "recipient@example.com" \
  --user "your_username:your_password" \
  -T /path/to/message.txt
```

Node.js · Nodemailer:

```js
const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'smtp.makesender.no',
  port: 2525,
  secure: false,
  auth: {
    user: 'your_username',
    pass: 'your_password'
  },
  tls: {
    rejectUnauthorized: true
  }
});

await transporter.sendMail({
  from: 'sender@example.com',
  to: 'recipient@example.com',
  subject: 'Hello from SMTP',
  text: 'This is the message body'
});
```

Python · smtplib:

```python
import smtplib
from email.mime.text import MIMEText

msg = MIMEText('This is the message body')
msg['Subject'] = 'Hello from SMTP'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'

with smtplib.SMTP('smtp.makesender.no', 2525) as server:
    server.starttls()
    server.login('your_username', 'your_password')
    server.send_message(msg)
```

PHP · PHPMailer:

```php
<?php
// composer require phpmailer/phpmailer
use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.makesender.no';
$mail->Port = 2525;
$mail->SMTPAuth = true;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Username = 'your_username';
$mail->Password = 'your_password';

$mail->setFrom('sender@example.com');
$mail->addAddress('recipient@example.com');
$mail->Subject = 'Hello from SMTP';
$mail->Body = 'This is the message body';
$mail->send();
```

## Job ID

When a message is accepted, the reply to DATA includes the ID it was queued under. The same ID is set as the `X-Job-ID` header on the delivered e-mail and as `job-id` in webhook events, so you can match them to your own records. Most libraries expose this final reply: `Net::SMTP#send_message` returns it in Ruby, and Nodemailer has it as `info.response`.

DATA reply:

```text
250 2.0.0 Ok: queued as 3714
```

## Custom headers

Pass extra settings in `X-Make-*` headers. Make Sender reads them and removes them before the message is relayed.

| Header | Example |
| --- | --- |
| `X-Make-Template-Name` | welcome_email |
| `X-Make-Track` | true / false |
| `X-Make-Track-Opens` | true / false |
| `X-Make-Track-Clicks` | true / false |
| `X-Make-Variables` | {"order_id": "12345"} |
| `X-Make-Template-Variables` | {"name": "John"} |
| `X-Make-Recipient-Variables` | {"alice@example.com": {"name": "Alice"}} |

Per-recipient personalization:

```text
X-Make-Recipient-Variables: {"alice@example.com": {"name": "Alice"}, "bob@example.com": {"name": "Bob"}}
```

## Message-ID and header passthrough

Your `Message-ID` is kept as it is, so threading and deduplication keep working. If it is missing, one is generated (<…@makesender.no>). All recipients of a submission share it, and it is included as `message-id` in webhook events.

The message is rebuilt before it is relayed, so not every header is kept:

- **Kept:** From, To, Cc, Subject, your own `X-*` headers, `In-Reply-To`, References, List-* (including `List-Unsubscribe-Post` for RFC 8058 one-click unsubscribe), Precedence and Auto-Submitted.
- **Rewritten:** From keeps its display name and local part. With "rewrite From to top domain" turned on, only the domain part changes. The envelope sender is only used when From is missing or not on the authenticated domain.
- **Generated:** `Message-ID` if missing, Date, `MIME-Version`, Content-*, Received, `Return-Path` and `DKIM-Signature`. Bcc recipients come from the envelope and are never written to a header.

## Rate limits and errors

Limits apply to connections per IP, failed logins per IP per hour, and message size (25 MB).

| Code | Meaning |
| --- | --- |
| `421` | Rate limit exceeded: too many connections or failed logins. Retry with backoff. |
| `501` | Syntax error: an invalid address, or From or To is missing. |
| `535` | Authentication failed: wrong credentials, or the SMTP credential is inactive. |
| `552` | Message too large: over 25 MB. |
| `553` | A non-ASCII address in MAIL FROM or RCPT TO, which is not supported. |

## Character encodings

Send UTF-8. The server advertises 8BITMIME and SMTPUTF8, but addresses must be ASCII. Internationalised mailboxes (EAI) are not relayed. A non-ASCII address is refused with 553 5.6.7, or with 501 when it appears in the To, Cc or From header.

Header text is more forgiving. Display names and Subject should be UTF-8 or RFC 2047 encoded, and raw ISO-8859-1 or Windows-1252 bytes are converted rather than refused. Bodies are converted from the charset in Content-Type, and an 8-bit text part without a declared charset must be UTF-8.

## Troubleshooting and security

- **Connection refused:** Allow outbound connections to port 2525 and check the host name.
- **Authentication failures:** Check that the credential is active and that the username matches the SMTP credentials page.
- **TLS errors:** Use STARTTLS on port 2525, not SMTPS on 465. Your client must support TLS 1.2.
- **Keep credentials safe:** Never commit them to version control. Use environment variables or a secret store, and rotate them regularly.

## Limitations

- SMTP does not cover everything the REST API can do.
- Batch operations are more limited than in the REST API.
- Delivery status in real time requires webhooks.
