> ## Documentation Index
> Fetch the complete documentation index at: https://inbound.new/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Send with SMTP

> Send email through Inbound from any SMTP client using a managed credential

Any SMTP client or library can send mail through Inbound with a managed **Mailbox + SMTP** or **SMTP only** credential. Messages submitted over SMTP go through the same sending pipeline as the [send email API](https://inbound.new/docs/api-reference/emails/send-an-email.md), so the same account sending limits and domain checks apply.

If you don't have a credential yet, follow [Create a mailbox credential](https://inbound.new/docs/mailboxes/connect-imap.md#create-a-mailbox-credential). Choose **SMTP only** if the application never needs to read mail. A **Mailbox + SMTP** credential must have **Read & write** access to send; **Read only** credentials cannot authenticate to SMTP.

## Connection settings

| Setting | Value |
| - | - |
| Host | `smtp.inboundemail.com` |
| Port `465` | Implicit TLS (TLS from the first byte) |
| Port `587` | STARTTLS (upgrade to TLS before authenticating) |
| Username | The credential's **Login email** |
| Password | The credential's generated password |
| Authentication | `PLAIN` or `LOGIN` (normal password) |

Both ports require TLS 1.2 or later. On port `587`, the server only offers `AUTH` after `STARTTLS` has completed, and authentication attempted before TLS is rejected with `538`. Keep certificate verification enabled.

Ordinary account API keys and your dashboard password cannot authenticate to SMTP.

## Allowed senders

Each credential has a sender policy. The gateway checks both the envelope sender (`MAIL FROM`) and the address in the message's `From` header against it:

| Sender policy | Allowed `MAIL FROM` and `From` addresses |
| - | - |
| **Exact identity** (`identity`) | Only the configured sending address |
| **Any scoped domain** (`scoped_domains`) | Any address on an exact domain in the credential's scopes. Subdomains are not included. |

An empty envelope sender (`MAIL FROM:<>`) is accepted, but the `From` header must still be allowed. If the message has no `From` header, the envelope sender is used as the From address. A disallowed sender is rejected with `553`.

The display name comes from the `From` header you send. The dashboard's **Display name** field does not change outgoing messages. See [SMTP sender policies](https://inbound.new/docs/mailboxes/scopes-and-permissions.md#smtp-sender-policies) for how scopes and sender policies interact.

## Recipients and Bcc

The envelope recipients (`RCPT TO`) decide who receives the message:

* Addresses in the `To` and `Cc` headers are delivered and shown only if they are also envelope recipients. Header addresses that are not envelope recipients are removed.
* Envelope recipients that don't appear in `To` or `Cc` are delivered as Bcc and are not visible to other recipients.
* A `Bcc` header in the message is ignored.

Nodemailer and Python's `send_message` build the envelope from `To`, `Cc`, and `Bcc` automatically, so Bcc works as expected with both.

## How messages are rebuilt

Inbound parses each submitted message and sends it again through the email API; it doesn't relay the raw bytes. The delivered message keeps:

* `From`, `To`, `Cc`, `Reply-To`, and `Subject`
* The plain-text and HTML bodies
* Attachments, including inline images referenced by `Content-ID`
* `In-Reply-To`, `References`, and custom `X-` headers (except `X-SES-*`)

Other headers, such as `Date`, `Sender` or `List-Unsubscribe`, are not kept, and the delivered message gets a new `Message-ID`. A valid `Message-ID` you set is kept on the copy saved to `Sent`, so a client that also saves its own copy doesn't create a duplicate. Line breaks and control characters in header values are replaced with spaces.

S/MIME signed messages (`multipart/signed`) are the exception: they are relayed unchanged so the signature stays valid, including their `Date`, `Message-ID`, and `To` and `Cc` headers.

## Limits

| Limit | Value |
| - | - |
| Maximum message size | 3 MiB (`3,145,728` bytes), advertised as `SIZE 3145728` |
| Recipients per message | 50 distinct envelope recipients |
| Simultaneous connections per client IP address | 10 |
| Time to authenticate after connecting | 30 seconds |
| Idle connection timeout | 60 seconds |
| Maximum session length | 10 minutes |
| Failed logins | 10 per login address and IP address, and 50 per IP address, in a 15-minute window |

The size limit applies to the complete MIME message. Base64 encoding makes attachments about a third larger than the original files. If the client sends a `SIZE` parameter above the limit, the message is rejected before `DATA`.

Connections over the per-IP limit are closed without a reply. When a login is throttled, further attempts from that address fail with `421` until the 15-minute window ends, even with the correct password. Authentication requests are also rate-limited by the API.

SMTP sends count toward your account's sending limits, the credential's [daily send limit](https://inbound.new/docs/mailboxes/access-and-limits.md#daily-send-limit) and the [API rate limits](https://inbound.new/docs/api-reference/rate-limits.md).

Messages sent with a **Mailbox + SMTP** credential are saved to its IMAP `Sent` folder automatically, a few seconds after they are accepted. **SMTP only** credentials have no mailbox and save no copy. See [Sent mail](https://inbound.new/docs/mailboxes/imap-behavior.md#sent-mail).

## Supported extensions

The server advertises `PIPELINING`, `8BITMIME`, `SIZE`, `STARTTLS` (port `587`, before TLS), and `AUTH PLAIN LOGIN` (after TLS).

`SMTPUTF8`, `DSN`, and `ENHANCEDSTATUSCODES` are not supported:

* Envelope addresses (`MAIL FROM` and `RCPT TO`) must have an ASCII local part; a non-ASCII local part is rejected with `553`. Internationalized domain names are accepted. Non-ASCII display names, subjects, and bodies work normally when MIME-encoded.
* `MAIL FROM` accepts only the `SIZE`, `BODY`, and `AUTH` parameters, and `RCPT TO` accepts none. Others, such as the DSN parameters `RET`, `ENVID`, `NOTIFY`, and `ORCPT`, are rejected with `555`. In Nodemailer, leave the `dsn` option unset.

After `STARTTLS`, send `EHLO` again before `AUTH`. SMTP libraries do this automatically.

## Reply codes

Most replies include an enhanced status code in their text, such as `5.7.8`, even though `ENHANCEDSTATUSCODES` is not advertised. Some protocol replies, such as `530`, `538` and `250 ... Queued as`, don't.

| Code | Meaning | What to do |
| - | - | - |
| `250` | Accepted. The reply text includes `Queued as` and the Inbound email ID. | Nothing |
| `421` | Too many failed logins, an authentication or session timeout, or the server is closing the connection | Wait out the 15-minute login window, authenticate promptly, and reconnect with backoff |
| `451` | Temporary failure: rate limited, the credential's [daily send limit](https://inbound.new/docs/mailboxes/access-and-limits.md#daily-send-limit) reached (reply text: `This mailbox has reached its daily sending limit`), the same message is already being processed, too many messages in flight for this credential or address, the gateway is busy, or the email API is unavailable | Retry later with backoff. For the daily limit, retry after 00:00 UTC. |
| `452` | More than 50 recipients | Send the remaining recipients in another message |
| `454` | The authentication service is unavailable or rate limited | Retry later with backoff |
| `530` | `MAIL FROM` was sent before authenticating | Authenticate first |
| `535` | Invalid login email or password, disabled credential, a **Read only** credential, SMTP turned off in the credential's `protocols`, a client address outside its `allowedIps`, or the login, scope or sender domain is no longer verified | Check the credential, its [access settings](https://inbound.new/docs/mailboxes/access-and-limits.md), and give it **Read & write** access to send. Repeated failures lead to `421`. |
| `538` | `AUTH` was attempted before TLS | Use port `465`, or run `STARTTLS` on port `587` first |
| `550` | The email API rejected the message (for example, not authorized or invalid content), or the message has no sender or recipients. A credential that was disabled, rotated or restricted after you logged in gets `550 5.7.1 Not authorized` on its next message. | Read the reply text and fix the message, account or credential. Don't retry unchanged. |
| `552` | The message is larger than 3 MiB | Reduce the size or remove attachments |
| `553` | The sender isn't allowed by the credential's sender policy, or an address contains non-ASCII characters | Use an allowed From and `MAIL FROM` address |
| `554` | The message couldn't be parsed, for example its header block is too large or it has more than 500 MIME parts | Simplify the message |
| `555` | Unsupported `MAIL FROM` or `RCPT TO` parameter, such as DSN or `SMTPUTF8` | Remove the parameter |

Inbound derives an idempotency key from the credential, envelope sender, recipients, and exact message bytes. If a client resubmits an identical message that was already sent, for example after a dropped connection, Inbound returns the original result instead of sending it twice. Libraries that generate a new `Message-ID` or `Date` for each attempt produce a different message, so this only covers retries of the same bytes.

## Examples

Store the credential as `INBOUND_MAILBOX_LOGIN` and `INBOUND_MAILBOX_PASSWORD`. Replace `support@example.com` with an address your sender policy allows. The TypeScript example uses Nodemailer (`bun add nodemailer`); the Python example uses only the standard library.

```typescript Nodemailer theme={null}
import nodemailer from "nodemailer";

const transport = nodemailer.createTransport({
  host: "smtp.inboundemail.com",
  port: 465,
  secure: true, // use port: 587, secure: false, requireTLS: true for STARTTLS
  auth: {
    user: process.env.INBOUND_MAILBOX_LOGIN,
    pass: process.env.INBOUND_MAILBOX_PASSWORD,
  },
});

const info = await transport.sendMail({
  from: "Support <support@example.com>",
  to: "recipient@example.com",
  subject: "Hello from Inbound",
  text: "Sent over SMTP.",
});

console.log(info.response);
```

```python Python theme={null}
import os
import smtplib
import ssl
from email.message import EmailMessage

message = EmailMessage()
message["From"] = "Support <support@example.com>"
message["To"] = "recipient@example.com"
message["Subject"] = "Hello from Inbound"
message.set_content("Sent over SMTP.")

context = ssl.create_default_context()

with smtplib.SMTP("smtp.inboundemail.com", 587) as smtp:
    smtp.starttls(context=context)
    smtp.login(
        os.environ["INBOUND_MAILBOX_LOGIN"],
        os.environ["INBOUND_MAILBOX_PASSWORD"],
    )
    smtp.send_message(message)
```

For port `465` in Python, use `smtplib.SMTP_SSL("smtp.inboundemail.com", 465, context=context)` and skip `starttls()`.

### Mail client settings

| Field | Value |
| - | - |
| Outgoing server | `smtp.inboundemail.com` |
| Port and security | `465` with SSL/TLS, or `587` with STARTTLS |
| Authentication | Normal password |
| Username | Your credential's login email |
| Password | Your credential's generated password |

Set the account's email address to an address your sender policy allows. For step-by-step setup in Apple Mail, iPhone and iPad, Thunderbird and Outlook, including the IMAP settings, see [Set up an email client](https://inbound.new/docs/mailboxes/connect-imap.md#set-up-an-email-client).

## Test the connection

Check TLS and the advertised extensions without sending credentials:

```bash theme={null}
openssl s_client -connect smtp.inboundemail.com:587 \
  -starttls smtp \
  -servername smtp.inboundemail.com \
  -verify_return_error
```

After the handshake, type `EHLO example.com`. The reply should list `AUTH PLAIN LOGIN` and `SIZE 3145728`. Type `QUIT` to close the connection.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.