> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mailofly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from Twilio SendGrid

> How to migrate from Twilio SendGrid to Mailofly: replacing complex v3 personalizations, SMTP relay cutover, and suppression migration.

# Migrating from Twilio SendGrid to Mailofly

SendGrid has long been an industry standard for email delivery, but developers frequently struggle with its deeply nested `personalizations` API structure, dated dashboard interface, slow customer support, and complex pricing tiers.

Mailofly offers an elegant, high-throughput modern replacement with transparent pricing, instant logs, clean APIs, and zero payload nesting.

***

## Why Developers Migrate from SendGrid

* **Simplified Payload Schema**: Say goodbye to `personalizations[0].to[0].email` and multiple `content` objects. Mailofly uses flat, intuitive JSON payloads.
* **Instant Delivery Logs**: Real-time log streaming with granular status codes, bounce categorization, and click tracking—no 15-minute dashboard delays.
* **Modern Developer Ergonomics**: Modern typed SDKs for TypeScript, Python, Go, PHP, and Dart, plus native Model Context Protocol (MCP) support for AI.
* **Clean Identity Routing**: Replace cumbersome SendGrid Subusers with lightweight Mailofly Sending Identities (`account_key`).

***

## 1. Payload Schema Comparison

SendGrid's v3 `/v3/mail/send` payload requires deep nesting. Here is how it maps directly to Mailofly:

| SendGrid v3 Field                           | Mailofly Field      | Notes                                     |
| :------------------------------------------ | :------------------ | :---------------------------------------- |
| `personalizations[0].to`                    | `to`                | Array of strings (`["user@example.com"]`) |
| `personalizations[0].subject`               | `subject`           | Top-level string                          |
| `from.email` + `from.name`                  | `from`              | Format: `"Acme <sales@acme.com>"`         |
| `content[].value` (type: text/html)         | `html`              | Direct HTML string                        |
| `content[].value` (type: text/plain)        | `text`              | Direct plaintext string                   |
| `personalizations[0].dynamic_template_data` | `variables`         | Plain key-value object                    |
| `custom_args`                               | `tags` or `headers` | Metadata dictionary                       |
| `attachments` (base64)                      | `attachments`       | Array of `{ filename, content }` objects  |

***

## 2. Code Migration Examples

### Node.js / TypeScript

<CodeGroup>
  ```ts Before: SendGrid (@sendgrid/mail) theme={null}
  import sgMail from "@sendgrid/mail";

  sgMail.setApiKey(process.env.SENDGRID_API_KEY!);

  const msg = {
    to: "alex@example.com",
    from: { email: "team@acme.com", name: "Acme Team" },
    subject: "Welcome to Acme",
    text: "Hello Alex, welcome to Acme!",
    html: "<strong>Hello Alex</strong>, welcome to Acme!",
    customArgs: { customerId: "12345" },
  };

  await sgMail.send(msg);
  ```

  ```ts After: Mailofly (@mailofly/node) theme={null}
  import { Mailofly } from "@mailofly/node";

  const mailofly = new Mailofly({
    apiKey: process.env.MAILOFLY_API_KEY!,
  });

  const { id } = await mailofly.emails.send({
    to: ["alex@example.com"],
    from: "Acme Team <team@acme.com>",
    subject: "Welcome to Acme",
    text: "Hello Alex, welcome to Acme!",
    html: "<strong>Hello Alex</strong>, welcome to Acme!",
    tags: [{ name: "customerId", value: "12345" }],
  });

  console.log("Sent with Mailofly:", id);
  ```
</CodeGroup>

***

### Python

<CodeGroup>
  ```python Before: SendGrid theme={null}
  from sendgrid import SendGridAPIClient
  from sendgrid.helpers.mail import Mail
  import os

  message = Mail(
      from_email="team@acme.com",
      to_emails="alex@example.com",
      subject="Welcome to Acme",
      html_content="<strong>Welcome to Acme!</strong>",
  )

  sg = SendGridAPIClient(os.environ.get("SENDGRID_API_KEY"))
  response = sg.send(message)
  ```

  ```python After: Mailofly theme={null}
  from mailofly import Mailofly
  import os

  client = Mailofly(api_key=os.environ["MAILOFLY_API_KEY"])

  response = client.emails.send(
      sender="Acme Team <team@acme.com>",
      to=["alex@example.com"],
      subject="Welcome to Acme",
      html="<strong>Welcome to Acme!</strong>",
  )

  print("Mailofly ID:", response.id)
  ```
</CodeGroup>

***

### Raw HTTP cURL

Notice the reduction in nesting and boilerplate:

<CodeGroup>
  ```bash Before: SendGrid v3 theme={null}
  curl -X POST https://api.sendgrid.com/v3/mail/send \
    -H "Authorization: Bearer SG.xxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "personalizations": [
        {
          "to": [{ "email": "alex@example.com" }]
        }
      ],
      "from": { "email": "team@acme.com", "name": "Acme Team" },
      "subject": "Your Order Confirmation",
      "content": [
        {
          "type": "text/html",
          "value": "<p>Thank you for your order!</p>"
        }
      ]
    }'
  ```

  ```bash After: Mailofly theme={null}
  curl -X POST https://api.mailofly.com/emails \
    -H "Authorization: Bearer mf_live_xxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "from": "Acme Team <team@acme.com>",
      "to": ["alex@example.com"],
      "subject": "Your Order Confirmation",
      "html": "<p>Thank you for your order!</p>"
    }'
  ```
</CodeGroup>

***

## 3. SMTP Drop-in Cutover

If you use SendGrid via SMTP (such as in Laravel, Supabase, Ghost, WordPress, or Postfix), migration requires **zero code changes**—simply update your environment variables:

| Setting      | SendGrid                   | Mailofly                            |
| :----------- | :------------------------- | :---------------------------------- |
| **Host**     | `smtp.sendgrid.net`        | `smtp.mailofly.com`                 |
| **Port**     | `587` (TLS) or `465` (SSL) | `587` (STARTTLS) or `465` (SSL/TLS) |
| **Username** | `apikey`                   | `mailofly` (or your API Key)        |
| **Password** | SendGrid API Key (`SG...`) | Mailofly API Key (`mf_live_...`)    |

***

## 4. Migrating Suppressions & Unsubscribes

<Important>
  Failing to import your SendGrid suppression lists before sending through Mailofly can cause high initial bounce rates, triggering automated rate limits.
</Important>

SendGrid maintains four suppression tables:

1. **Bounces**: Mailboxes that permanently rejected mail.
2. **Spam Reports**: Contacts who marked your mail as spam.
3. **Blocks**: IPs or domains temporarily blocked.
4. **Global Unsubscribes**: Contacts who opted out of all emails.

### Export from SendGrid

In the SendGrid Dashboard:

1. Go to **Settings → Suppressions** (or use the SendGrid Suppressions API: `/v3/suppression/bounces`, `/v3/suppression/spam_reports`, `/v3/suppression/unsubscribes`).
2. Export each list as a CSV.

### Import to Mailofly

1. In Mailofly, go to **[Audiences > Contacts](https://www.mailofly.com/user/audience)**.
2. Click **Import Contacts**.
3. Upload your CSV and toggle **Mark as Unsubscribed / Suppressed**.
4. Mailofly will automatically populate your global suppression blacklist, protecting your domain's sending reputation.

***

## 5. Webhook Migration

SendGrid dispatches webhooks as an unauthenticated or signature-checked **JSON array of events**. Mailofly sends structured, single-event payloads cryptographically signed with HMAC / Svix.

### Event Type Equivalents

| SendGrid Event | Mailofly Event           | Meaning                                  |
| :------------- | :----------------------- | :--------------------------------------- |
| `processed`    | `email.sent`             | Email accepted and queued by the engine  |
| `delivered`    | `email.delivered`        | Accepted by recipient's mail server      |
| `bounce`       | `email.bounced`          | Hard bounce (permanent rejection)        |
| `deferred`     | `email.delivery_delayed` | Soft bounce or temporary ISP throttle    |
| `spamreport`   | `email.complained`       | Recipient marked message as spam         |
| `open`         | `email.opened`           | Tracking pixel loaded by recipient       |
| `click`        | `email.clicked`          | Recipient clicked a tracked link         |
| `unsubscribe`  | `contact.unsubscribed`   | Recipient opted out via unsubscribe link |

***

## 6. Migration Checklist

* [ ] Add domain to Mailofly and add `mailofly._domainkey` DKIM records.
* [ ] Add `include:mailofly.com` to your existing SPF TXT record alongside SendGrid.
* [ ] Export SendGrid Bounces, Spam Reports, and Unsubscribes, and import into Mailofly Suppressions.
* [ ] Update SDK packages or environment variables (`MAILOFLY_API_KEY`, `smtp.mailofly.com`).
* [ ] Set up Mailofly Webhook endpoints in your application.
* [ ] Send test emails and verify click/open tracking on the Mailofly dashboard.
* [ ] Decommission the SendGrid API key once traffic has completely transitioned.


## Related topics

- [Migration Guide](/audience/migration-guide.md)
- [Migration Overview & Strategy](/guides/migrations/overview.md)
- [Introduction](/guides/introduction.md)
- [Managing Identities](/identities/managing-identities.md)
