Skip to content
couryo

Developer guideMigration

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.

View as Markdown

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 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 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, 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.
  • 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): 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.

Examples#

Laravel (.env)
MAIL_MAILER=smtp
MAIL_HOST=smtp.couryo.com
MAIL_PORT=587
MAIL_USERNAME=couryo
MAIL_PASSWORD=ck_live_...
MAIL_ENCRYPTION=tls
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');
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.