# Migrate from Resend (/migrate/resend)





Sendly implements Resend's `POST /emails`, `POST /emails/batch`, and
`GET /emails/:id` endpoints. Keep the official `resend` SDK — change the **base URL**
and the **API key**.

* **Base URL:** `https://api.sendly.now/api/compat/resend`
* **Auth:** `Authorization: Bearer <sk_...>` (the SDK sets this for you)

## The two-variable swap [#the-two-variable-swap]

<Tabs items="[&#x22;JavaScript&#x22;, &#x22;Python&#x22;]">
  <Tab value="JavaScript">
    ```js title="Before (Resend)"
    import { Resend } from "resend";

    const resend = new Resend("re_your_resend_key");

    await resend.emails.send({
      from: "you@yourdomain.com",
      to: "customer@example.com",
      subject: "Hello",
      html: "<p>Welcome aboard.</p>",
    });
    ```

    ```js title="After (Sendly)"
    import { Resend } from "resend";

    const resend = new Resend("sk_your_sendly_key", {
      baseUrl: "https://api.sendly.now/api/compat/resend",
    });

    await resend.emails.send({
      from: "you@yourdomain.com",
      to: "customer@example.com",
      subject: "Hello",
      html: "<p>Welcome aboard.</p>",
    });
    ```

    The SDK also honors the `RESEND_BASE_URL` environment variable, so you can point it at
    Sendly without touching code:

    ```bash
    RESEND_BASE_URL="https://api.sendly.now/api/compat/resend"
    ```
  </Tab>

  <Tab value="Python">
    ```python title="Before (Resend)"
    import resend

    resend.api_key = "re_your_resend_key"

    resend.Emails.send({
        "from": "you@yourdomain.com",
        "to": "customer@example.com",
        "subject": "Hello",
        "html": "<p>Welcome aboard.</p>",
    })
    ```

    ```python title="After (Sendly)"
    import resend

    resend.api_key = "sk_your_sendly_key"
    resend.api_url = "https://api.sendly.now/api/compat/resend"

    resend.Emails.send({
        "from": "you@yourdomain.com",
        "to": "customer@example.com",
        "subject": "Hello",
        "html": "<p>Welcome aboard.</p>",
    })
    ```

    The Python SDK also reads the `RESEND_API_URL` environment variable.
  </Tab>
</Tabs>

## Supported fields [#supported-fields]

| Field            | Support | Notes                                                                                                   |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------- |
| `from`           | Full    | Plain address or `"Name <you@domain.com>"` display-name form.                                           |
| `to`             | Full    | String or array.                                                                                        |
| `cc` / `bcc`     | Full    | String or array.                                                                                        |
| `subject`        | Full    |                                                                                                         |
| `html` / `text`  | Full    | `html` is preferred when both are present. At least one is required.                                    |
| `reply_to`       | Partial | The first address is used.                                                                              |
| `headers`        | Full    |                                                                                                         |
| `attachments`    | Partial | Inline base64 `content` only (see below).                                                               |
| `tags`           | Partial | Best-effort: `name`/`value` are combined and sanitized, max 10.                                         |
| `emails.get(id)` | Full    | Returns the Resend email object with a mapped `last_event`. Needs a key with `emails:read` — see below. |
| Batch send       | Full    | Order is preserved in the response.                                                                     |

## Not supported (returns a clean Resend error) [#not-supported-returns-a-clean-resend-error]

| Field                | Result                                                                                          |
| -------------------- | ----------------------------------------------------------------------------------------------- |
| `scheduled_at`       | `422 validation_error` — scheduled sends aren't supported in this phase.                        |
| `attachments[].path` | `422` — Sendly does not fetch attachments from a URL; send the file inline as base64 `content`. |
| `template`           | `422` — Resend template IDs can't be mapped to Sendly templates.                                |
| `topic_id`           | `422` — broadcast topics aren't supported.                                                      |

<Callout type="info">
  Each unsupported field returns Resend's own `{ statusCode, message, name }` error body,
  so your existing `resend` error handling (`{ data, error }` in JS, a raised
  `ResendError` in Python) behaves exactly as it does today.
</Callout>

## Key permissions [#key-permissions]

Sending works with any key that may send. `emails.get(id)` is the one Resend call Sendly
maps to something other than a send, and it is gated on `emails:read` instead — reading
returns the message's subject, body and recipient, so it follows the same permission rule
here as it does on the native API.

A key with full permissions covers both and needs nothing changed. A key created with only
sending ticked keeps sending normally and gets this on the read:

```json
{
  "statusCode": 403,
  "message": "This API key is missing the required scope: emails:read",
  "name": "restricted_api_key"
}
```

Grant the key `emails:read` in your project's API key settings, or use a full-permission
key.
