Moving in

Migrating

We answer Resend and SendGrid request shapes at the paths their clients already call. Change the base URL and the key; leave the rest.

From Resend

POST /emails takes their body and answers { id }, as they do. Their own client works unchanged:

Their SDK, our APIimport { Resend } from "resend";

const resend = new Resend(process.env.TRANSMIT_API_KEY, {
  baseUrl: "https://api.transmit.dev",   // the only line that changes
});

await resend.emails.send({
  from: "Acme <hello@yourdomain.com>",
  to: ["you@example.com"],
  subject: "It arrived.",
  html: "<p>Nobody noticed.</p>",
});
What we do with the fields that differ
FieldTypeNotes
tagstranslatedTheir [{ name, value }] becomes our name:value strings.
attachments[].content_typetranslatedBecomes our type.
attachments[].pathrefusedWe do not fetch attachments from a URL. Send base64 content.
scheduled_attranslatedBecomes scheduled_for. Must be an ISO 8601 instant — relative times like "in 1 min" are refused rather than silently sent now.
Idempotency-KeyoptionalNot required here, and generated per request when absent.

From SendGrid

POST /v3/mail/send takes their body and answers 202 with the id in X-Message-Id, as they do.

Their SDK, our APIimport sgMail from "@sendgrid/mail";

sgMail.setApiKey(process.env.TRANSMIT_API_KEY);
sgMail.client.setDefaultRequest("baseUrl", "https://api.transmit.dev");  // the only line that changes

await sgMail.send({
  to: "you@example.com",
  from: "hello@yourdomain.com",
  subject: "It arrived.",
  text: "Nobody noticed.",
});
What we do with the fields that differ
FieldTypeNotes
personalizationstranslatedEach becomes one message, with its own recipients and subject.
content[]translatedtext/plain becomes text, text/html becomes html.
categoriestranslatedBecomes tags.
send_attranslatedA unix timestamp becomes scheduled_for.
dynamic_template_datatranslatedBecomes variables.
template_idrefusedA d-… id names a template living in SendGrid. Recreate it here and send its id.

Where we cannot honour something, we refuse it loudly rather than dropping it. A scheduled message that silently sends immediately is worse than an error, so that case is a 422 naming the field.

What you still have to do

  • Verify your domain here. Sending domains do not travel; publish our DNS records before you cut over.
  • Recreate templates. A template in their system is not a template in ours.
  • Repoint webhooks. Our events and signature are our own — see webhooks.
  • Bring your suppression list. People who asked the old sender to stop have not changed their minds.

Once you are moved in, the TypeScript SDK reaches the rest of the API — templates, SMS, contacts, campaigns, analytics — which the compatibility shapes do not cover.