Skip to main content

Documentation Index

Fetch the complete documentation index at: https://segmentflow.ai/docs/llms.txt

Use this file to discover all available pages before exploring further.

Use emails.send when your backend needs to send one expected email to one primary recipient, such as an order confirmation, password reset, account invite, receipt, or shipping update. emails.send creates one durable EmailSend. The REST route is POST /api/v1/emails; each accepted request snapshots one saved Email Template, resolves one primary Profile, freezes the effective envelope, enqueues delivery, and returns the EmailSend status handle. Use emails.batchSend when your backend already has several independent EmailSends to create at once. Each item has its own primary recipient, data, idempotency key, durable status record, and result. If you are deciding between one email, batch email, event-triggered Journeys, Broadcasts, or Newsletter Issues, start with Which API should I use?.
EmailSend always uses a saved Email Template. It does not accept raw html, text, react, inline template content, Template aliases, Template slugs, Template names, or locale-key resolution in v1.

Prerequisites

  • A saved Email Template whose purpose is Transactional.
  • A verified sending domain.
  • A SenderProfile on that verified sending domain.
  • A purpose-scoped API Key with emails:write to send and emails:read to retrieve status.
  • A caller-generated Idempotency-Key for every emails.send request, or an item-level idempotencyKey for every emails.batchSend item.
import Segmentflow from "@segmentflow/segmentflow-typescript";

const client = new Segmentflow({
  apiKey: process.env.SEGMENTFLOW_API_KEY,
});

Send one email

Use templateId to choose the saved Email Template. Use envelope fields such as from, to.email, subject, replyTo, cc, and bcc the same way you would in an email provider API, while still keeping the content template-backed.
const send = await client.v1.emails.send(
  {
    templateId: "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    from: "Acme Receipts <receipts@example.com>",
    to: {
      email: "alex@example.com",
    },
    subject: "Receipt for order ord_9421",
    replyTo: "Acme Support <support@example.com>",
    cc: "Billing <billing@example.com>",
    bcc: ["audit@example.com"],
    data: {
      orderId: "ord_9421",
      firstName: "Alex",
      orderTotal: "49.00",
      receiptUrl: "https://app.example.com/orders/ord_9421/receipt",
    },
  },
  {
    headers: { "Idempotency-Key": "order-confirmation:ord_9421" },
  },
);
The response is the durable EmailSend status handle. Store send.id in your system and use it for reconciliation.
{
  "id": "018f7a35-1111-7111-8111-111111111111",
  "status": "Queued",
  "templateId": "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
  "templateSnapshotId": "018f7a35-3333-7333-8333-333333333333",
  "senderProfileId": "018f7a35-4444-7444-8444-444444444444",
  "from": {
    "email": "receipts@example.com",
    "name": "Acme Receipts"
  },
  "replyTo": {
    "email": "support@example.com",
    "name": "Acme Support"
  },
  "subject": "Receipt for order ord_9421",
  "subscriptionGroupId": null,
  "profileId": "018f7a35-5555-7555-8555-555555555555",
  "to": {
    "email": "alex@example.com"
  },
  "cc": [
    {
      "email": "billing@example.com",
      "name": "Billing"
    }
  ],
  "messageId": null,
  "skippedReason": null,
  "failureReason": null,
  "failureMessage": null,
  "createdAt": "2026-05-29T00:00:00.000Z",
  "updatedAt": "2026-05-29T00:00:00.000Z"
}
Ordinary create and retrieve responses echo the safe effective envelope: from, replyTo, subject, to, cc, and subscriptionGroupId. They do not echo bcc.

Recipient resolution

Every EmailSend has exactly one primary recipient. That primary recipient resolves to one Profile and creates at most one Message. Use to.email when you know the recipient email address:
await client.v1.emails.send(
  {
    templateId: "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    to: { email: "alex@example.com" },
    data: { orderId: "ord_9421" },
  },
  {
    headers: { "Idempotency-Key": "order-confirmation:ord_9421" },
  },
);
Add to.externalId when you want Segmentflow to link or reconcile the Profile against your stable customer id:
await client.v1.emails.send(
  {
    templateId: "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    to: {
      email: "alex@example.com",
      externalId: "shopify:customer_123",
    },
    data: { orderId: "ord_9421" },
  },
  {
    headers: { "Idempotency-Key": "order-confirmation:ord_9421" },
  },
);
Use top-level profileId when you already know the Segmentflow Profile. If you omit to.email, Segmentflow uses the Profile’s canonical email for delivery.
await client.v1.emails.send(
  {
    templateId: "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    profileId: "018f7a35-5555-7555-8555-555555555555",
    data: { orderId: "ord_9421" },
  },
  {
    headers: { "Idempotency-Key": "order-confirmation:ord_9421" },
  },
);
profileId and to.email must refer to the same Profile. to.externalId alone must resolve to an existing Profile; include to.email when the send may need to create a new Profile. EmailSend does not update durable Profile properties. Use the Profiles API or events.track profile.properties when you need future segmentation or personalization state.

Template, sender, and subject

Pass the exact templateId of a saved Email Template. v1 does not resolve aliases, slugs, locale keys, or template names. If you edit the Template after a send is accepted, the existing EmailSend still uses the TemplateSnapshot created for that send. SenderProfile resolution is deterministic:
  1. Use request from or senderProfileId when present.
  2. If both from and senderProfileId are present, they must resolve to the same SenderProfile.
  3. Otherwise use the SenderProfile saved on the Template.
  4. If no valid transactional SenderProfile on a verified domain resolves, the send is rejected or skipped with InvalidSenderProfile.
from accepts a bare email or "Name <email@example.com>". The email must exactly match a verified SenderProfile email. A verified domain does not authorize arbitrary local-parts. replyTo accepts one bare email or "Name <email@example.com>". It may differ from from, but it must use a verified sender domain owned by your Organization. Request-level subject is final text. Segmentflow freezes it onto the EmailSend and does not interpolate it. If you omit subject, the saved Template subject is frozen instead.

Copied recipients

Use cc and bcc to copy operational recipients on the same provider envelope.
await client.v1.emails.send(
  {
    templateId: "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    to: { email: "alex@example.com" },
    cc: ["Billing <billing@example.com>", "ops@example.com"],
    bcc: "audit@example.com",
    data: { orderId: "ord_9421" },
  },
  {
    headers: { "Idempotency-Key": "order-confirmation:ord_9421" },
  },
);
cc and bcc accept a single friendly-address string or an array of friendly-address strings. Segmentflow validates and stores them for provider handoff. Copied recipients are envelope-only in v1:
  • They do not create Profiles.
  • They do not create Messages.
  • They do not own personalization.
  • They do not receive separate Delivery Events.
  • Primary Profile erasure, suppression, and subscription checks apply to the primary recipient, not copied recipients.
cc is returned in ordinary create and retrieve responses for operational visibility. bcc is stored for delivery and retry behavior, but ordinary API responses, Resource Events, and dashboard list rows do not expose it.

Batch independent sends

emails.batchSend accepts up to 100 independent items. Use shared defaults for fields that are identical across the batch, then override specific fields on each item.
const batch = await client.v1.emails.batchSend({
  defaults: {
    templateId: "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    from: "Acme Receipts <receipts@example.com>",
    subject: "Your receipt is ready",
    replyTo: "Acme Support <support@example.com>",
    tracking: { clicks: false },
  },
  items: [
    {
      idempotencyKey: "order-confirmation:ord_9421",
      to: {
        email: "alex@example.com",
        externalId: "shopify:customer_123",
      },
      data: {
        orderId: "ord_9421",
        orderTotal: "49.00",
      },
      cc: "Billing <billing@example.com>",
    },
    {
      idempotencyKey: "order-confirmation:ord_9422",
      to: { email: "sam@example.com" },
      subject: "Receipt for order ord_9422",
      data: {
        orderId: "ord_9422",
        orderTotal: "129.00",
      },
      bcc: "audit@example.com",
    },
  ],
});
Batch defaults can include templateId, from, senderProfileId, subject, replyTo, subscriptionGroupId, and tracking. Each item can override those defaults. Batch defaults cannot include profileId, to, data, cc, or bcc. Recipient identity, one-send data, and copied recipients must stay item-specific. Malformed batch envelopes fail the whole request. Item-specific validation, idempotency, sender, template, or recipient failures return Rejected results for those items without rolling back accepted items. emails.batchSend is not a campaign sender. Use Broadcasts or Newsletter Issues when the system should compute or manage an audience, scheduling, campaign review, compliance controls, or one shared message sent to a segment.

Tracking

await client.v1.emails.send(
  {
    templateId: "d0df2e4d-8e44-4792-b2d5-8d3f2ea3d54b",
    to: { email: "sam@example.com" },
    data: {
      resetUrl: "https://app.example.com/reset?token=secret-token",
      expiresInMinutes: 30,
    },
    tracking: {
      clicks: false,
    },
  },
  {
    headers: { "Idempotency-Key": "password-reset:user_123:2026-05-29T12:00Z" },
  },
);
Click tracking is enabled by default. Set tracking.clicks: false for sensitive links that should not pass through a redirect. Open tracking is disabled for email in v1 and cannot be enabled per request.

Subscription groups

Pass subscriptionGroupId when this email should respect a specific SubscriptionGroup. If the primary recipient is not subscribed according to that group’s OptIn or OptOut policy, Segmentflow creates a durable skipped EmailSend with skippedReason: "ProfileUnsubscribed". Omit subscriptionGroupId for strictly service-requested transactional emails such as password resets or receipts.

Retrieve status

Store the returned id in your system. Use it as the durable handle for reconciliation when Resource Events are delayed or unavailable.
const current = await client.v1.emails.retrieve(send.id);
Retrieve responses follow the same envelope echo rules as create responses: safe fields such as from, replyTo, subject, to, and cc are returned, while bcc is not.

Idempotency

Every emails.send request requires an Idempotency-Key header. Every emails.batchSend item requires an idempotencyKey field. Choose a stable key from your business event, such as order-confirmation:ord_9421 or password-reset:user_123:<reset-request-id>. Idempotency is scoped to your Organization and the full effective request. The effective request includes the resolved recipient identity, templateId, sender, subject, replyTo, copied recipients, tracking settings, subscription gate, and one-send data.
  • Reusing the same key with the same effective request returns the existing EmailSend with its current status.
  • Reusing the same key with a different effective request returns 409 Conflict for emails.send or a Rejected item result for emails.batchSend.
  • Keys remain reserved for the lifetime of the EmailSend record.
  • Idempotent replay does not re-enqueue, re-render, recreate a Message, or send a duplicate email.
Moving a batch field between defaults and an item does not change idempotency when the merged effective request is the same.

Status lifecycle

StatusMeaning
QueuedThe request was accepted, persisted, and enqueued for worker processing.
ProcessingA worker is resolving, rendering, or sending the email.
SentSegmentflow’s delivery adapter accepted the email. This is not an inbox-delivery guarantee.
FailedThe send was accepted for processing, but rendering, provider submission, or worker execution failed.
SkippedSegmentflow intentionally did not attempt provider delivery because of policy, suppression, subscription, admission, or rate limit.
Delivery, open, click, bounce, and complaint outcomes are separate Delivery Records or Message events. They do not mutate the EmailSend lifecycle status.

Raw HTTP

curl https://api.segmentflow.ai/api/v1/emails \
  -H "x-api-key: $SEGMENTFLOW_API_KEY" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: order-confirmation:ord_9421" \
  -d '{
    "templateId": "2c2f9e13-7d0a-4b10-9f4b-35f6d96d7a70",
    "from": "Acme Receipts <receipts@example.com>",
    "to": {
      "email": "alex@example.com",
      "externalId": "shopify:customer_123"
    },
    "subject": "Receipt for order ord_9421",
    "replyTo": "Acme Support <support@example.com>",
    "cc": "Billing <billing@example.com>",
    "bcc": ["audit@example.com"],
    "data": {
      "orderId": "ord_9421",
      "orderTotal": "49.00"
    }
  }'
See the API Reference for the complete request and response schema. See Migrate from Resend when you are moving provider-style send calls into Segmentflow EmailSend.