# Migrate from any provider over SMTP

> Guide to replace any SMTP provider (Gmail, Microsoft 365, your own server, hosting, another sending service) with Couryo without changing your code. Credentials, headers, idempotency, reply codes and the DNS change in the safe order.

Source: https://couryo.com/en/docs/migrate-smtp

If your system sends over SMTP, moving to Couryo means changing four values: server, port, username and password. Each message takes the same path as the API: verified domain, suppression, limits, timeline and webhooks. Follow the [safe migration order](https://couryo.com/en/docs/migration.md) and use this guide for the details.

## The credentials

| Field | Value |
|---|---|
| Server | `smtp.couryo.com` |
| Port | `587` (STARTTLS), `465` (direct TLS) or `2525` (STARTTLS, when 587 is blocked) |
| Username | `couryo` |
| Password | the API key (`ck_live_...`, or `ck_test_...` to test without delivering) |
| Encryption | required (TLS 1.2 or newer) |

The sender (`From`) must be on a [verified domain](https://couryo.com/en/docs/domains.md) at Couryo. Start with the `ck_test_` key in staging: the email is captured, shows up under **Emails** with the content and the [review link](https://couryo.com/en/docs/test-mode.md#review-link), and nobody gets it.

## What changes compared to other SMTPs

- **One id per message:** the answer to `DATA` is `250 2.0.0 Ok: queued as msg_...`. Keep the id to look up the timeline with `GET /v1/emails/{id}`.
- **No duplicate email on a retry:** the message `Message-ID` (or the `Idempotency-Key` header) is the idempotency key for 24 hours.
- **Limits explained:** near a limit, the answer is `452 4.7.0` with the reason and the numbers, instead of a refusal with no explanation. The codes are in [SMTP](https://couryo.com/en/docs/smtp.md#reply-codes).
- **Options by header:** `X-Couryo-Stream: marketing` adds one-click unsubscribe and keeps the suppression apart from transactional; `X-Couryo-Tags: type=order` becomes tags to filter in the dashboard and in webhooks.
- **Up to 50 recipients per message.** To send the same message to many people, prefer one per recipient (or a [campaign](https://couryo.com/en/docs/campaigns.md)): delivery and metrics stay per person.
- **What SMTP does not do:** saved templates and the batch of 100 in one call belong to the [REST API](https://couryo.com/en/docs/sending.md).

## Examples

```ini title="Laravel (.env)"
MAIL_MAILER=smtp
MAIL_HOST=smtp.couryo.com
MAIL_PORT=587
MAIL_USERNAME=couryo
MAIL_PASSWORD=ck_live_...
MAIL_ENCRYPTION=tls
```

```php title="PHPMailer"
$mail->isSMTP();
$mail->Host = 'smtp.couryo.com';
$mail->Port = 587;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->SMTPAuth = true;
$mail->Username = 'couryo';
$mail->Password = getenv('COURYO_API_KEY');
```

```python title="Django (settings.py)"
EMAIL_HOST = "smtp.couryo.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "couryo"
EMAIL_HOST_PASSWORD = os.environ["COURYO_API_KEY"]
```

Tools with a "custom SMTP" field (Supabase Auth, WordPress, n8n, ERPs) take the same four values.

## DNS

1. **Do not touch the MX** of your mailboxes or the root domain SPF. Couryo passes SPF through the `bounces` subdomain, by CNAME, without using any of the 10 DNS lookups of your SPF.
2. **Publish Couryo's records:** `couryo1._domainkey`, `couryo2._domainkey` and `bounces` (CNAME) and, if you do not have one yet, the suggested DMARC. They do not conflict with your current provider's.
3. **If the old provider already uses the `bounces` subdomain**, the domain page shows that record as different, and the domain is only verified once it points to Couryo. In that case, changing that record is the switch-over moment: do it at your lowest-volume hour, with the code already set for Couryo.
4. Wait for the domain to be verified, move the sending little by little and, 7 days after the end, delete the old provider's DKIM key and SPF `include:`, if nothing else uses it.
