# Keep your SDK. Change the base URL and the key.

> SendGrid, Mailgun, and Postmark compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code. Importers arm at launch.

Canonical: https://emailfast.dev/features/migration

## 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. 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. 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

:::panel A SendGrid-shaped send, verbatim
```bash
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

:::tradeoffs Compatibility, scoped honestly
We mirror the send surfaces your code actually calls, not three vendors' entire
product APIs — their campaign-management and statistics endpoints are not cloned.
Template, contact, and suppression importers arm at launch; until then compatibility
moves your code, and your data follows. Where behavior differs, the per-provider
guides say so plainly.
:::

## Where to go next

Each guide has the concrete path for its provider:
[SendGrid](/migrate/sendgrid), [Mailgun](/migrate/mailgun),
[Postmark](/migrate/postmark), and [EmailJS](/migrate/emailjs). Still deciding
whether to switch at all? The [comparison](/compare/sendgrid-alternative) is the
honest version, including where staying put is the right call.

## 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.

## 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?

Importers for templates, contacts, and suppressions arm at launch. Compatibility moves your code today; your data follows through the importers.

## 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.
