# Migrate from another provider (/migrate)



Sendly speaks the transactional-send dialect of several popular email providers. If you
already send through Resend, SendGrid, Postmark, Mailgun, or Plunk, you don't have to
rewrite your integration or adopt a new SDK to try Sendly — keep the vendor SDK you
already run and change **two things**:

1. the **base URL** (or host) the SDK points at, and
2. the **API key** (use a Sendly secret key — see [API Keys](/guides/api-keys)).

Everything else — templates, domains, suppression, tracking — you configure in the
Sendly UI.

<Cards>
  <Card title="Resend" href="/migrate/resend">
    Point the <code>resend</code> SDK (JS or Python) at Sendly.
  </Card>

  <Card title="SendGrid" href="/migrate/sendgrid">
    Point <code>@sendgrid/mail</code> (JS) or <code>sendgrid</code> (Python) at Sendly.
  </Card>

  <Card title="Postmark" href="/migrate/postmark">
    Point the <code>postmark</code> SDK at Sendly with a request-host override.
  </Card>

  <Card title="Mailgun" href="/migrate/mailgun">
    Point <code>mailgun.js</code> at Sendly with a URL override.
  </Card>

  <Card title="Plunk" href="/migrate/plunk">
    Sendly's ancestral API — a near drop-in for existing Plunk senders.
  </Card>
</Cards>

## How the compatibility layer works [#how-the-compatibility-layer-works]

Each provider gets a dedicated **compat endpoint** under
`https://api.sendly.now/api/compat/<vendor>`. These endpoints are thin translators: they parse the
vendor's request shape, map it onto Sendly's native send, and run it through the **same
pipeline** your native API calls use. That means every Sendly gate still applies —
verified-domain checks, sending limits, suppression, and billing — regardless of which
dialect you send in.

<Callout type="info">
  The compat endpoints cover the **transactional send path** only. For the full Sendly
  surface — contacts, campaigns, workflows, templates, webhooks — use the official
  [Sendly SDKs](/sdks) or the [API](/api-reference/overview) directly.
</Callout>

## What happens to features Sendly can't map [#what-happens-to-features-sendly-cant-map]

A dialect can express things Sendly doesn't support in this phase (scheduled sends,
dynamic templates, URL-fetched attachments, and so on). Rather than silently dropping
them, the compat endpoint returns a **clean error in that vendor's own error shape** —
so your existing SDK's error handling fires exactly as it would against the original
provider. Each provider page below lists precisely what is supported and what returns an
error.

## Getting a Sendly key [#getting-a-sendly-key]

Every request authenticates with a Sendly **secret key** (`sk_...`) for a project that
has at least one [verified sending domain](/guides/verifying-domains). Create one in
your project settings; it slots into the same place the vendor's own key went.

A key with full permissions works across every compat endpoint. If you ticked specific
permissions when you created the key, the compat endpoints enforce them exactly as the
native API does: sending needs `emails:send`, and reading a message back — Resend's
`emails.get()`, the one compat call that is not a send — needs `emails:read`. Reading
returns the message's subject, body and recipient, so it follows the same permission rule
here as it does natively. A send-only key gets a **403 in that vendor's own error shape**,
with the message naming the permission it is missing.
