Email SDK
Adapters

SendGrid

Send through the Twilio SendGrid Mail Send API with the fullest field support of any adapter.

Capabilities

Repeated headersIdempotencySchedulingPersonalized
NononeYesnative

These values come from the adapter's exported capabilities declaration. The field support matrix covers normalized message fields.

The SendGrid adapter calls the Twilio SendGrid Mail Send API with plain fetch. It supports every EmailMessage field: recipients land in a personalizations entry, metadata becomes custom_args, and tags become categories.

SendGrid logo
SendGrid@opencoredev/email-sdk/sendgrid
Provider docs

Configure

Create an API key with Mail Send permission in the SendGrid dashboard and verify the sender identity or domain you use in from.

lib/email.ts
import { createEmailClient } from "@opencoredev/email-sdk";
import { sendgrid } from "@opencoredev/email-sdk/sendgrid";

export const email = createEmailClient({
  adapters: [sendgrid({ apiKey: process.env.SENDGRID_API_KEY! })],
});

Prop

Type

Send

Everything maps: cc, bcc, headers, attachments, tags, metadata, and even multiple replyTo addresses (sent as reply_to_list). sendAt schedules the send natively as send_at (a Unix timestamp in seconds); SendGrid accepts at most 72 hours in the future.

const result = await email.send({
  from: "Acme <security@acme.com>",
  to: [{ email: "user@example.com", name: "Ada" }],
  subject: "Reset your password",
  html: "<p>Click the link below to reset your password.</p>",
  text: "Use the link below to reset your password.",
  metadata: { userId: "user_123", flow: "password-reset" },
  tags: [{ name: "type", value: "password-reset" }],
});

console.log(result.id); // SendGrid message id from the x-message-id header

SendGrid's send endpoint returns 202 Accepted with an empty body, so the adapter reads result.id from the x-message-id response header. metadata values arrive in SendGrid event webhooks as custom_args; tag values arrive as categories.

Personalized sends

SendGrid implements native sendPersonalized, so the adapter emits one personalizations entry per recipient and sends the complete set in one request.

await email.sendPersonalized({
  message: {
    from: "Acme <hello@acme.com>",
    subject: "Hi %recipient.name%",
    html: '<a href="https://acme.com/unsub?id=%recipient.id%">Unsubscribe</a>',
  },
  recipients: [
    { to: "ada@example.com", variables: { name: "Ada", id: "u_1" } },
    { to: "linus@example.com", variables: { name: "Linus", id: "u_2" } },
  ],
});

Verify from the CLI

SENDGRID_API_KEY="SG...." npx email-sdk doctor --adapter sendgrid
SENDGRID_API_KEY="SG...." npx email-sdk send \
  --adapter sendgrid \
  --from "Acme <hello@acme.com>" \
  --to user@example.com \
  --subject "SendGrid smoke test" \
  --text "It works" \
  --dry-run

Drop --dry-run for one real send. SendGrid account review and sender verification can block delivery even when the payload validates, and only a live send proves they are done.

Compare SendGrid with other providers

Side-by-side message-field support from the SDK capability matrix, with working code for both adapters:

On this page