# SMTP relay

> Send through smtp.pidgely.com on ports 587 or 465 with user pidgely and your API key as password. Examples for Nodemailer, Django, Rails, Laravel and Spring.

Source: https://docs.pidgely.com/guides/smtp/

If your framework or tool only speaks SMTP, send through Pidgely's submission server. Messages go through the same pipeline as the API: verified From domain, suppression list, quotas, DKIM signing and webhooks.

| Setting | Value |
|---|---|
| Host | `smtp.pidgely.com` |
| Port | `587` or `2587` with STARTTLS · `465` or `2465` with implicit TLS |
| Username | `pidgely` |
| Password | Your API key, `pdg_…` |
| Authentication | `PLAIN` or `LOGIN`, TLS 1.2 or newer |

Any valid API key works, including `sending_access` keys; a [domain-restricted key](https://docs.pidgely.com/authentication/#domain-restricted-keys) can only send from its domain. Use ports `2587`/`2465` if your network blocks the standard ones.

## Framework examples

```js tab="Nodemailer"
import nodemailer from 'nodemailer';

const transporter = nodemailer.createTransport({
  host: 'smtp.pidgely.com',
  port: 465,
  secure: true,
  auth: { user: 'pidgely', pass: process.env.PIDGELY_API_KEY },
});

await transporter.sendMail({
  from: 'Acme <hello@acme.com>',
  to: 'steve@example.com',
  subject: 'Hello over SMTP',
  html: '<p>It works!</p>',
  headers: { 'Pidgely-Idempotency-Key': 'welcome-42', 'X-Pidgely-Tags': 'category=welcome' },
});
```

```python tab="Django"
# settings.py
import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.pidgely.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "pidgely"
EMAIL_HOST_PASSWORD = os.environ["PIDGELY_API_KEY"]
DEFAULT_FROM_EMAIL = "Acme <hello@acme.com>"
```

```ruby tab="Rails"
# config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "smtp.pidgely.com",
  port: 587,
  user_name: "pidgely",
  password: ENV.fetch("PIDGELY_API_KEY"),
  authentication: :plain,
  enable_starttls_auto: true
}
```

```env tab="Laravel"
# .env (STARTTLS is negotiated automatically on port 587)
MAIL_MAILER=smtp
MAIL_HOST=smtp.pidgely.com
MAIL_PORT=587
MAIL_USERNAME=pidgely
MAIL_PASSWORD=pdg_your_key_here
MAIL_FROM_ADDRESS=hello@acme.com
MAIL_FROM_NAME="Acme"
```

```properties tab="Spring"
# application.properties
spring.mail.host=smtp.pidgely.com
spring.mail.port=587
spring.mail.username=pidgely
spring.mail.password=${PIDGELY_API_KEY}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
```

## Special headers

| Header | Effect |
|---|---|
| `Pidgely-Idempotency-Key` | Deduplicates retries for 24 hours; a repeat returns the original message id. See [Idempotency](https://docs.pidgely.com/idempotency/). |
| `X-Pidgely-Tags: name=value,name2=value2` | Sets [tags](https://docs.pidgely.com/guides/sending/#tags) (`[A-Za-z0-9_-]{1,256}` each). |

Pidgely removes `Bcc`, `Return-Path`, `DKIM-Signature`, `Received`, `Delivered-To`, `Authentication-Results`, `ARC-*`, `Feedback-ID`, `List-Unsubscribe-Post` and the two headers above before sending, and adds `Message-ID` and `Date` when missing. Bcc recipients are taken from the SMTP envelope. Other headers (for example `X-Entity-Ref-ID`) pass through. SMTP mail uses the transactional stream.

## Limits

- Messages up to **40 MB**.
- Up to **50 recipients** per message (after de-duplication).
- Sending [quotas](https://docs.pidgely.com/rate-limits/#sending-quotas) apply as for the API.

## Error replies

| Reply | Meaning |
|---|---|
| `530 5.7.0` | Authenticate first |
| `535 5.7.8` | Wrong username or API key |
| `454 4.7.0` | Temporary authentication failure; retry |
| `550 5.6.0` | Invalid message, including a From domain that is not verified |
| `550 5.7.1` | Not allowed: key restricted to another domain, account paused or suspended |
| `452 4.5.3` | Sending quota reached |
| `451 4.7.0` | Rate limited; retry later |
| `451 4.3.0` | Temporary failure; retry |
