Keep your SDK. Change the base URL and the key.
Compatible endpoints for the transactional APIs your code already speaks — all funneled through the same admission gate as native traffic.
Your existing code is the migration
The rewrite is the step you skip: SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code — attachment sends are the one exception, refused with a clear error in your provider's own format rather than silently dropped. That's what a SendGrid compatible API is for — with Mailgun and Postmark equivalents beside it. The switch is configuration: new base URL, new key, same code paths you've already debugged in production.
How it works
| Coming from | Endpoint | Auth and semantics |
|---|---|---|
| SendGrid | POST /v3/mail/send | Authorization: Bearer; SendGrid-shaped 202 with an X-Message-Id response header |
| Mailgun | POST /v3/:domain/messages | HTTP Basic api:key; urlencoded and multipart bodies both accepted |
| Postmark | POST /email and POST /email/batch | X-Postmark-Server-Token header; Postmark error semantics, including error code 406 for an inactive recipient |
Every compat request funnels into the same checkpoint as native traffic — every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. cc/bcc and provider scheduling fields (SendGrid send_at, Mailgun o:deliverytime) map to their native equivalents, and provider-specific settings knobs with no equivalent here are accepted without effect. And batch calls through the compat surface are transactionally atomic: a mid-batch failure rolls back every recipient in the call, so retries can't double-send.
The evidence
curl https://api.emailfast.dev/v3/mail/send \
-H "Authorization: Bearer $EMAILFAST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"personalizations":[{"to":[{"email":"ada@example.com"}]}],
"from":{"email":"you@yourdomain.com"},
"subject":"Hello",
"content":[{"type":"text/html","value":"<p>Hi.</p>"}]}'Response: 202 with X-Message-Id — exactly where your existing error handling expects to find them.
Honest limits
We mirror the send surfaces your code actually calls, not three vendors' entire product APIs — their campaign-management and statistics endpoints are not cloned. Outbound attachments aren't supported yet, platform-wide: a compat send carrying one is refused with a clear provider-shaped error, never silently stripped. Hosted template, contact, and suppression importers are planned; today compatibility moves your code, and your data moves by CSV and re-created templates. Where behavior differs, the per-provider guides say so plainly.
Where to go next
Each guide has the concrete path for its provider: SendGrid, Mailgun, Postmark, and EmailJS. Still deciding whether to switch at all? The comparison is the honest version, including where staying put is the right call.
Questions, answered plainly
Do I have to rewrite my integration?
No. Point your existing SDK at the new base URL with a new key. The request shapes, response shapes, and headers your code already handles stay the same — cc/bcc and provider scheduling fields included. The one exception is attachments: not supported yet, and refused with a clear error in your provider's own format rather than silently dropped.
Are error responses faithful to the original APIs?
Yes, where your code depends on them: SendGrid-shaped 202 responses with an X-Message-Id header, Mailgun accepting both urlencoded and multipart bodies over HTTP Basic api:key, and Postmark error semantics including error code 406 for an inactive recipient.
What happens if a batch fails halfway?
Compat batches are transactionally atomic: a mid-batch failure rolls back every recipient in the call, so a retry can't double-send the half that succeeded.
How do I move templates, contacts, and suppression lists?
Hosted importers for templates, contacts, and suppressions are planned. Today, contacts and suppression lists move by CSV through the dashboard, and templates re-create in the dashboard or over POST /v1/templates — give each the slug of your old provider template id and existing template sends resolve to it unchanged.
Do compat sends skip any checks?
No: every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip.