Skip to main content
Migrate your transactional email from another provider to Ark. This guide covers API mapping, domain migration, suppression list import, and webhook reconfiguration for the four most common providers.

Migration Overview

1

Map your API calls

Update your sending code to use Ark’s SDK or API. See the provider-specific mapping tables below.
2

Configure your domain on Ark

Add your sending domain and configure , , and return path DNS records. See Domain Setup.
3

Import your suppression list

Export bounced/unsubscribed addresses from your old provider and import them into Ark.
4

Update webhooks

Reconfigure webhook endpoints to receive Ark’s event format.
5

Switch traffic gradually

Route a percentage of traffic through Ark, monitor deliverability, then increase to 100%.

Sending Email — API Mapping

SendGrid → Ark

SendGridArkNotes
sgMail.send(msg)client.emails.send(...)Direct replacement
msg.toto: [...]Ark uses array format
msg.fromfrom_: "..."Python uses from_ (reserved word)
msg.subjectsubject: "..."Same
msg.htmlhtml: "..."Same
msg.texttext: "..."Same
msg.categoriestag: "..."Ark uses single tag per email
msg.customArgsmetadata: {...}Key-value pairs, max 10 keys
msg.trackingSettings.openTrackingtrack_opens: boolPer-email control
msg.trackingSettings.clickTrackingtrack_clicks: boolPer-email control
Before (SendGrid):
import sendgrid
from sendgrid.helpers.mail import Mail

sg = sendgrid.SendGridAPIClient(api_key="SG.xxx")

message = Mail(
    from_email="hello@yourdomain.com",
    to_emails="user@example.com",
    subject="Order Shipped",
    html_content="<p>Your order is on its way!</p>"
)
message.custom_args = {"order_id": "ord_123"}

sg.send(message)
After (Ark):
from ark import Ark

client = Ark()

client.emails.send(
    from_="hello@yourdomain.com",
    to=["user@example.com"],
    subject="Order Shipped",
    html="<p>Your order is on its way!</p>",
    metadata={"order_id": "ord_123"}
)

Domain Migration

Keep Your Existing Domain

You can use the same sending domain with Ark. The key DNS changes:
RecordWhat Changes
Replace old provider’s include: with include:spf.arkhq.io
Add Ark’s DKIM record (different selector: ark._domainkey)
Return PathUpdate CNAME to point to rp.arkhq.io
No changes needed if already configured
Update SPF — don’t duplicate. You can only have one SPF TXT record per domain. Replace your old provider’s include with Ark’s, or add both includes to the same record if running in parallel during migration.Example: v=spf1 include:spf.arkhq.io include:sendgrid.net ~all (during migration, then remove the old provider after cutover).
See the Domain Setup guide for complete DNS configuration instructions.

Import Suppression List

Export your suppression/bounce list from your old provider and import it into Ark to avoid sending to known-bad addresses:
from ark import Ark
import csv

client = Ark()

def import_suppressions(csv_file: str):
    """Import suppression list from CSV (email,reason columns)."""
    suppressions = []

    with open(csv_file) as f:
        reader = csv.DictReader(f)
        for row in reader:
            suppressions.append({
                "email": row["email"],
                "reason": row.get("reason", "imported")
            })

    # Bulk import in batches of 100
    for i in range(0, len(suppressions), 100):
        batch = suppressions[i:i+100]
        client.suppressions.bulk_create(
            suppressions=batch
        )
        print(f"Imported {min(i+100, len(suppressions))}/{len(suppressions)}")

# Export from your old provider, then:
import_suppressions("sendgrid_suppressions.csv")

Export Guides by Provider

  1. Go to Suppressions in the SendGrid dashboard
  2. Export each list: Bounces, Blocks, Invalid Emails, Spam Reports
  3. Combine into a single CSV with email and reason columns
  4. Import using the script above
  1. Use the Mailgun API: GET /v3/{domain}/bounces
  2. Also export: GET /v3/{domain}/complaints and GET /v3/{domain}/unsubscribes
  3. Combine results into a CSV
  4. Import using the script above
  1. Go to Suppressions in the Postmark dashboard
  2. Click Export to download the suppression list
  3. The CSV format is compatible — just rename columns if needed
  4. Import using the script above
  1. SES doesn’t have a bulk export. Use the API: ListSuppressedDestinations
  2. Page through all results and save to CSV
  3. Import using the script above
# Export from SES
import boto3
import csv

ses = boto3.client("sesv2")
suppressions = []
next_token = None

while True:
    params = {"Reasons": ["BOUNCE", "COMPLAINT"]}
    if next_token:
        params["NextToken"] = next_token

    response = ses.list_suppressed_destinations(**params)
    suppressions.extend(response["SuppressedDestinationSummaries"])
    next_token = response.get("NextToken")
    if not next_token:
        break

with open("ses_suppressions.csv", "w") as f:
    writer = csv.DictWriter(f, fieldnames=["email", "reason"])
    writer.writeheader()
    for s in suppressions:
        writer.writerow({
            "email": s["EmailAddress"],
            "reason": s["Reason"].lower()
        })

Webhook Migration

Ark’s webhook events map to other providers’ events:
Ark EventSendGridMailgunPostmarkSES
MessageSentdelivereddeliveredDeliveryDelivery
MessageDelayeddeferredtemporary-failure
MessageDeliveryFailedbouncepermanent-failureBounceBounce
MessageBouncedbouncepermanent-failureBounceBounce
MessageLoadedopenopenedOpenOpen
MessageLinkClickedclickclickedClickClick
See Webhook Overview for full payload documentation and multi-language handler examples.

Gradual Traffic Migration

Don’t switch 100% of traffic at once. Migrate gradually to catch issues early and warm up your Ark sending reputation.
DayArk TrafficOld ProviderWhat to Monitor
1-310%90%Delivery rates, bounce rates
4-725%75%Spam folder placement, open rates
8-1450%50%All metrics stable
15-2175%25%Confirm no regressions
22+100%0%Decommission old provider

Implementation Pattern

import random
from ark import Ark

ark_client = Ark()
ARK_TRAFFIC_PERCENTAGE = 25  # Increase gradually

def send_email(from_addr, to, subject, html, **kwargs):
    """Route traffic between providers during migration."""
    if random.randint(1, 100) <= ARK_TRAFFIC_PERCENTAGE:
        # Send via Ark
        return ark_client.emails.send(
            from_=from_addr,
            to=to,
            subject=subject,
            html=html,
            **kwargs
        )
    else:
        # Send via old provider
        return old_provider_send(from_addr, to, subject, html, **kwargs)

Migration Checklist

Use this checklist to track your migration progress:
  • API Integration — Update sending code to use Ark SDK
  • Domain DNS — Configure SPF, DKIM, return path for your domain(s)
  • Domain Verification — All DNS records verified in Ark dashboard
  • Suppression Import — Bounced/complained addresses imported
  • Webhook Setup — Endpoints configured and receiving events
  • Webhook Handlers — Updated to parse Ark’s payload format
  • Test Emails — Verified delivery to Gmail, Outlook, Yahoo
  • Gradual Rollout — Started at 10%, monitoring metrics
  • Full Cutover — 100% traffic through Ark
  • Old Provider Cleanup — DNS records removed, account decommissioned

Next Steps