# Migrate from SendGrid (/migrate/sendgrid)





Sendly implements SendGrid's `POST /v3/mail/send`. Keep the official `@sendgrid/mail`
(JavaScript) or `sendgrid` (Python) SDK — change the **base URL / host** and the
**API key**.

* **Base URL / host:** `https://api.sendly.now/api/compat/sendgrid`
* **Auth:** `Authorization: Bearer <sk_...>` (the SDK sets this for you)
* **Success response:** `202` with an empty body and an `X-Message-Id` header — the exact
  semantic `@sendgrid/mail` expects.

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

<Tabs items="[&#x22;JavaScript&#x22;, &#x22;Python&#x22;]">
  <Tab value="JavaScript">
    The transport's base URL is set on `@sendgrid/client`, which `@sendgrid/mail` then uses.
    `setApiKey` resets the base URL, so set the base URL **after** it.

    ```js title="Before (SendGrid)"
    import sgMail from "@sendgrid/mail";

    sgMail.setApiKey("SG.your_sendgrid_key");

    await sgMail.send({
      to: "customer@example.com",
      from: "you@yourdomain.com",
      subject: "Hello",
      html: "<p>Welcome aboard.</p>",
    });
    ```

    ```js title="After (Sendly)"
    import sgMail from "@sendgrid/mail";
    import sgClient from "@sendgrid/client";

    sgClient.setApiKey("sk_your_sendly_key");
    sgClient.setDefaultRequest("baseUrl", "https://api.sendly.now/api/compat/sendgrid");
    sgMail.setClient(sgClient);

    await sgMail.send({
      to: "customer@example.com",
      from: "you@yourdomain.com",
      subject: "Hello",
      html: "<p>Welcome aboard.</p>",
    });
    ```
  </Tab>

  <Tab value="Python">
    ```python title="Before (SendGrid)"
    from sendgrid import SendGridAPIClient
    from sendgrid.helpers.mail import Mail

    message = Mail(
        from_email="you@yourdomain.com",
        to_emails="customer@example.com",
        subject="Hello",
        html_content="<p>Welcome aboard.</p>",
    )

    SendGridAPIClient("SG.your_sendgrid_key").send(message)
    ```

    ```python title="After (Sendly)"
    from sendgrid import SendGridAPIClient
    from sendgrid.helpers.mail import Mail

    message = Mail(
        from_email="you@yourdomain.com",
        to_emails="customer@example.com",
        subject="Hello",
        html_content="<p>Welcome aboard.</p>",
    )

    client = SendGridAPIClient(
        api_key="sk_your_sendly_key",
        host="https://api.sendly.now/api/compat/sendgrid",
    )
    client.send(message)
    ```
  </Tab>
</Tabs>

## Supported fields [#supported-fields]

| Field                | Support | Notes                                                                            |
| -------------------- | ------- | -------------------------------------------------------------------------------- |
| `personalizations[]` | Full    | Each personalization fans out into its own send (its `to`/`cc`/`bcc` + subject). |
| `content[]`          | Full    | The `text/html` entry is preferred as the body, else `text/plain`.               |
| `from` / `reply_to`  | Full    | `{ email, name }` objects.                                                       |
| `subject`            | Full    | Top-level or per-personalization.                                                |
| `attachments`        | Full    | `type` maps to content type, `content_id` to the inline content ID.              |
| `categories`         | Partial | Mapped to Sendly tags (sanitized).                                               |
| `headers`            | Full    |                                                                                  |

## Not supported (returns a clean SendGrid error) [#not-supported-returns-a-clean-sendgrid-error]

| Field                                     | Result                                                             |
| ----------------------------------------- | ------------------------------------------------------------------ |
| `template_id` (`d-...` dynamic templates) | `400` — SendGrid template IDs can't be mapped to Sendly templates. |
| `send_at`                                 | `400` — scheduled sends aren't supported in this phase.            |

<Callout type="info">
  Errors come back in SendGrid's `{ "errors": [{ "message", "field", "help" }] }` shape
  with the matching status, so the SDK's own error handling — a thrown `ResponseError` in
  JavaScript, an `HTTPError` in Python — fires exactly as it does today.
</Callout>
