← Back to docs

Migrate from SendGrid

Move transactional email from SendGrid to EuroMail: API field mapping, code before/after, SMTP cutover, DNS, webhooks, and a zero-downtime checklist.

What changes, what doesn't

Both are REST APIs built around one call per email plus webhooks for delivery events, so the shape of your integration survives. SendGrid's structure is more elaborate (personalizations arrays, content type/value pairs) because it grew to support bulk personalization at scale; EuroMail's request body is a flatter JSON object for the common case. The honest side-by-side, including where SendGrid remains the better fit, is at euromail vs SendGrid.

Verify your domain on EuroMail while SendGrid still handles production traffic, then cut over. A config change, not a rewrite.

API mapping

SendGridEuroMail
EndpointPOST https://api.sendgrid.com/v3/mail/sendPOST https://api.euromail.dev/v1/emails
Auth headerAuthorization: Bearer SG.xxxX-EuroMail-Api-Key: em_live_...
Recipientspersonalizations[0].to (array of {email, name})to. String or array
Senderfrom: { email, name }from: "Name <email>" or bare address
Bodycontent: [{ type: "text/html", value: "..." }]html_body, text_body. Flat fields
Attachmentsattachments: [{ content, filename, type }] (base64)attachments: [{ filename, content, content_type }] (base64). Same shape, content_type required
Schedulingsend_at (Unix timestamp, max 72 h ahead)send_at (RFC 3339), no advance-window limit
Groupingcategories (max 10)tags (array of strings) + free-form metadata object
IdempotencyNoneidempotency_key body field. Same key always returns the original result
Success response202 Accepted, empty body202 Accepted with { "id", "status": "queued", ... }

The biggest structural change: SendGrid nests recipients inside personalizations to support per-recipient template substitution in one request. EuroMail's to is a flat field, for personalized batch sends, loop and call the endpoint once per recipient (or per intended batch), passing idempotency_key so retries never double-send. See Sending Emails.

Code before and after

curl:

# Before: SendGrid
curl -X POST https://api.sendgrid.com/v3/mail/send \
  -H "Authorization: Bearer SG.xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "personalizations": [{ "to": [{ "email": "[email protected]" }] }],
    "from": { "email": "[email protected]" },
    "subject": "Welcome aboard",
    "content": [{ "type": "text/html", "value": "<h1>Welcome!</h1>" }]
  }'

# After: EuroMail
curl -X POST https://api.euromail.dev/v1/emails \
  -H "X-EuroMail-Api-Key: em_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "[email protected]",
    "to": "[email protected]",
    "subject": "Welcome aboard",
    "html_body": "<h1>Welcome!</h1>"
  }'

TypeScript:

// Before: SendGrid
import sgMail from "@sendgrid/mail";
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);

await sgMail.send({
  to: "[email protected]",
  from: "[email protected]",
  subject: "Welcome aboard",
  html: "<h1>Welcome!</h1>",
});

// After: EuroMail
import { EuroMail } from "@euromail/sdk";
const client = new EuroMail({ apiKey: process.env.EUROMAIL_API_KEY! });

await client.emails.send({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Welcome aboard",
  html_body: "<h1>Welcome!</h1>",
});

Python, Rust, and Go SDKs use the same field names as the REST API.

SMTP cutover

SettingSendGridEuroMail
Hostsmtp.sendgrid.netsmtp.euromail.dev
Port587587 (STARTTLS) or 465 (implicit TLS)
Usernameapikey (literal)apikey (literal). Same convention
Passwordyour SendGrid API keyyour EuroMail API key (em_live_...)

Same literal username, so most SMTP-relay plugin presets need only a host and password change. Full walkthroughs (WordPress, framework mailers) in Send Email via SMTP.

DNS: verify your domain before switching

  1. Add your domain in the EuroMail dashboard and publish its DKIM, SPF, and DMARC records. See Domain Verification.
  2. Keep SendGrid's records in place during the dual-run. DKIM lives under a different selector and SPF include:s stack, so both providers verify simultaneously.
  3. Remove SendGrid's records on your own schedule after cutover.

Webhook event mapping

SendGrid eventEuroMail event
delivereddelivered
bouncebounced
blocked, deferreddeferred
spamreportcomplained
openopened
clickclicked
processed, droppedNo equivalent. The 202 API response already confirms acceptance or immediate rejection
unsubscribe, group_unsubscribeHandled via suppression lists, not a webhook event
account_status_changeN/A. See your account status in the dashboard instead

Signature verification changes: SendGrid signs with ECDSA over X-Twilio-Email-Event-Webhook-Signature / -Timestamp headers, which needs their helper library to verify correctly. EuroMail signs every payload with plain HMAC-SHA256 in the X-EuroMail-Signature header. A five-line function in any language, no library required. Swap your handler for the one in Webhooks.

Bring your suppression list

Export suppressions (bounces, spam reports, unsubscribes) from the SendGrid dashboard, then replay them:

curl -X POST https://api.euromail.dev/v1/suppressions \
  -H "X-EuroMail-Api-Key: em_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "email_address": "[email protected]",
    "reason": "migrated from SendGrid: hard bounce"
  }'

Details in Suppression Lists.

Honest gaps

  • Dynamic templates with Handlebars-style logic don't port directly. SendGrid's template_id + dynamic_template_data system supports conditionals and loops in templates; move that logic into your application code, or rebuild the template with EuroMail's template engine.
  • No categories cap. EuroMail's tags array has no fixed limit, unlike SendGrid's 10-category ceiling. Nothing to adapt, just noting the difference.
  • IP pools and warm-up. If you use SendGrid's dedicated IP pools, EuroMail handles IP reputation and warm-up automatically per account. See Deliverability Insights.
  • Full comparison at euromail vs SendGrid.

Cutover checklist

  1. Create a EuroMail account and an API key with the emails:send scope (API Keys).
  2. Verify your sending domain; confirm DKIM/SPF/DMARC show green.
  3. Register your webhook endpoint and swap the signature verification.
  4. Import suppressions from your SendGrid export.
  5. Switch the endpoint, auth header, and flatten personalizations/content into to/html_body/text_body (or the two SMTP settings), accepting 202 as success.
  6. Send a staging email end-to-end; confirm the delivered webhook arrives.
  7. Cut production traffic over; watch the first hour in Analytics and Deliverability Insights.
  8. Keep SendGrid read-only for a rollback window, then retire it.

Send your first email in about 90 seconds

The free tier includes 1,000 emails a month. All data stays in Finland | GDPR compliance without the paperwork.

Create free account See live delivery data