[data-reveal]{opacity:1!important;transform:none!important}
Sending Emails
Email

Sending Emails

The interface provides three sending functions: Send for single emails, SendTemplate for template-based emails, and SendBatch for multiple emails in one operation.

For a first message, start with EmailParams, then inspect the response. Add templates, batches, or optional fields only when the workflow needs them. Optional features depend on provider capabilities.

The EmailParams Object

The Send function accepts an EmailParams object. Only to and subject are required -- all other fields are optional.

import { Send } from "@antelopejs/interface-email";

const response = await Send({
  to: "[email protected]",
  subject: "Welcome aboard",
  html: "<h1>Welcome!</h1><p>Thanks for joining.</p>",
  text: "Welcome! Thanks for joining.",
});

if (!response.success) {
  throw new Error(
    response.error?.message ?? "The provider did not accept the email",
  );
}
console.log(response.status); // Acceptance is not proof of inbox delivery.

This example uses the configured default sender. Handle rejected promises at your application boundary as well as unsuccessful responses. See Email Response for optional fields and retry precautions.

Required Fields

FieldTypeDescription
toEmailAddress | EmailAddress[]One or more recipients
subjectstringThe email subject line

Content Fields

Provide at least one of text or html, even though the TypeScript type makes both optional. Including both gives readers a plain-text alternative to HTML.

FieldTypeDescription
textstringPlain text body
htmlstringHTML body

Address Fields

FieldTypeDescription
fromEmailAddressSender address (uses provider default if omitted)
replyToEmailAddress | EmailAddress[]Reply-to address(es)
ccEmailAddress | EmailAddress[]CC recipients
bccEmailAddress | EmailAddress[]BCC recipients

Threading Fields

Use these fields to place an email within an existing thread:

FieldTypeDescription
messageIdstringCustom Message-ID header
inReplyTostringMessage-ID of the email being replied to
referencesstring | string[]Message-IDs of the thread
import { Send } from "@antelopejs/interface-email";

await Send({
  to: "[email protected]",
  subject: "Re: Project update",
  text: "Thanks for the update!",
  inReplyTo: "<[email protected]>",
  references: ["<[email protected]>"],
});

Email Priority

The priority field requests a priority from providers that support it. It does not guarantee faster delivery. Check features.priority on the awaited provider capabilities before relying on this option.

ValueDescription
"high"Request high priority for an urgent message
"normal"Standard delivery priority (default)
"low"Non-urgent emails such as newsletters
import { Send } from "@antelopejs/interface-email";

await Send({
  to: "[email protected]",
  subject: "Server Alert",
  text: "CPU usage exceeded 95%.",
  priority: "high",
});

Tracking Options

The tracking field configures open and click tracking through the TrackingOptions interface.

FieldTypeDescription
opensbooleanTrack when the recipient opens the email
clicksbooleanTrack when the recipient clicks links
trackingDomainstringCustom domain for tracking URLs
disabledbooleanDisable all tracking
import { Send } from "@antelopejs/interface-email";

await Send({
  to: "[email protected]",
  subject: "Monthly Report",
  html: "<p>Your report is ready. <a href='https://app.example.com/reports'>View it here</a>.</p>",
  tracking: {
    opens: true,
    clicks: true,
    trackingDomain: "track.example.com",
  },
});

Schedule Options

The schedule field delays email delivery to a specified time using the ScheduleOptions interface.

FieldTypeDescription
sendAtDate | stringWhen to send the email (ISO 8601 string or Date object)
timezonestringIANA timezone identifier (e.g., "Europe/Paris")
import { Send } from "@antelopejs/interface-email";

await Send({
  to: "[email protected]",
  subject: "Monday Standup Reminder",
  text: "Standup in 15 minutes.",
  schedule: {
    sendAt: new Date("2027-04-05T06:45:00Z"),
    timezone: "Europe/Paris",
  },
});

Note: Check (await GetCapabilities()).features.scheduling before using scheduling to confirm the provider supports it.

Await GetCapabilities() before reading its flags. Use an explicit UTC offset when constructing a Date; the example is 08:45 in Paris on that date. A timezone field does not change how JavaScript parses a timezone-less date string.

Tags and Metadata

Tags and metadata help categorize and track emails for analytics.

FieldTypeDescription
tagsstring[]Labels for categorization and analytics
metadataRecord<string, string>Custom key-value pairs returned in webhook events
import { Send } from "@antelopejs/interface-email";

await Send({
  to: "[email protected]",
  subject: "Order Confirmation",
  html: "<p>Your order #12345 has been confirmed.</p>",
  tags: ["transactional", "order-confirmation"],
  metadata: {
    orderId: "12345",
    userId: "usr_abc",
  },
});

Provider Options

The providerOptions field passes provider-specific configuration that falls outside the standard interface. Use it sparingly, as it reduces portability.

import { Send } from "@antelopejs/interface-email";

await Send({
  to: "[email protected]",
  subject: "Test",
  text: "Hello",
  providerOptions: {
    sendgridIpPoolName: "transactional",
  },
});

Template Emails

The SendTemplate function renders an email from a template before sending. Templates can be provider-hosted or defined inline.

Provider-hosted templates

Reference a template stored in the provider's system by its ID:

import { SendTemplate } from "@antelopejs/interface-email";

const response = await SendTemplate({
  to: "[email protected]",
  template: { id: "welcome-v2", version: "3" },
  variables: {
    name: "Alice",
    activationUrl: "https://app.example.com/activate?token=xyz",
  },
});

Inline templates

Define the template content directly with an optional engine hint:

import { SendTemplate } from "@antelopejs/interface-email";

const response = await SendTemplate({
  to: "[email protected]",
  template: {
    content: "<h1>Hello {{name}}</h1><p>Welcome to our platform.</p>",
    type: "html",
    engine: "handlebars",
  },
  variables: { name: "Alice" },
});

Per-recipient variables

When sending to multiple recipients, provide unique variable values for each one using recipientVariables:

import { SendTemplate } from "@antelopejs/interface-email";

await SendTemplate({
  to: ["[email protected]", "[email protected]"],
  template: { id: "weekly-digest" },
  variables: { week: "March 23-29" },
  recipientVariables: {
    "[email protected]": { name: "Alice", unreadCount: "5" },
    "[email protected]": { name: "Bob", unreadCount: "12" },
  },
});

Batch Sending

The SendBatch function sends multiple emails in a single operation. Each message in the batch follows the same EmailParams structure, with an optional batchId for matching responses.

import { SendBatch } from "@antelopejs/interface-email";

const result = await SendBatch({
  messages: [
    {
      to: "[email protected]",
      subject: "Your invoice",
      html: "<p>Your invoice #001 is ready in your account.</p>",
      batchId: "invoice-001",
    },
    {
      to: "[email protected]",
      subject: "Your invoice",
      html: "<p>Your invoice #002 is ready in your account.</p>",
      batchId: "invoice-002",
    },
  ],
  defaults: {
    from: { email: "[email protected]", name: "Billing" },
    tags: ["invoice"],
  },
  continueOnError: true,
});

Batch defaults

The defaults field applies shared values to all messages. Individual message fields override defaults when both are specified.

Error handling in batches

When continueOnError is true (the default), the batch continues sending even if individual messages fail. The response includes per-message results:

console.log(result.total); // Total messages in batch
console.log(result.successful); // Number sent successfully
console.log(result.failed); // Number that failed

for (const res of result.responses) {
  if (!res.success) {
    console.error(
      `Message ${res.batchId} to ${res.recipient} failed:`,
      res.error,
    );
  }
}

Email Response

The Send and SendTemplate functions return an EmailResponse object:

FieldTypeDescription
successbooleanWhether the email was accepted
statusEmailStatusCurrent delivery status
messageIdstringProvider-assigned ID for tracking
errorEmailErrorError details if the send failed
timestampDateWhen the email was accepted
providerstringName of the provider that handled the request

The EmailStatus type includes these values: "queued", "sent", "delivered", "failed", "rejected", "scheduled", and "unknown".

Only success and status are required on EmailResponse. Message IDs, errors, timestamps, and provider names may be absent. In a batch, each result also has an index into the original messages and a recipient; batchId is optional. The batch-level success is true only when every message succeeds.

Error handling

When success is false, inspect the optional error field for an EmailError object:

import { Send } from "@antelopejs/interface-email";

const response = await Send({
  to: "[email protected]",
  subject: "Hello",
  text: "World",
});

if (!response.success && response.error) {
  console.error(`Error [${response.error.code}]: ${response.error.message}`);

  if (response.error.retryable) {
    // A retry may succeed; apply your retry and duplicate-send policy.
  }
}

retryable is a hint, not an idempotency guarantee. Retrying can send another message; the interface does not define a deduplication key. EmailError requires code and message, with optional field and retryable. Also handle rejected promises from the configured implementation.

Next Steps

  • Attachments -- Learn how to attach files using buffers, base64, file paths, or URLs.