Skip to main content

Transactional Emails

Transactional emails are triggered programmatically for individual users—order confirmations, password resets, account notifications, and other time-sensitive communications.

Transactional vs Marketing

TransactionalMarketing
Triggered by user actionSent to segments
One recipient at a timeMultiple recipients
Time-sensitiveCan be scheduled
Expected by userPromotional
Higher deliverabilityMay be filtered
Examples of transactional emails:
  • Order confirmation
  • Shipping notification
  • Password reset
  • Account verification
  • Payment receipt
  • Security alert

Sending Modes

Create reusable templates in the dashboard, send via API using the template slug.
curl -X POST "https://api.sequenzy.com/api/v1/transactional/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "customer@example.com",
    "slug": "order-confirmation",
    "variables": {
      "orderNumber": "ORD-12345",
      "total": "$99.99",
      "deliveryDate": "January 20, 2024"
    }
  }'
Benefits:
  • Non-developers can edit templates
  • Consistent branding
  • Version control in dashboard
  • Analytics per template
  • Template blocks can include conditional display rules based on recipient variables
  • Repeat blocks can render one child block set per item in an array variable

Dynamic Array Data

Transactional templates can repeat blocks over arrays passed in variables. For example, a repeat block with source items and item alias item renders its child blocks once per item. Inside those children, use scoped merge tags like {{item.title}}, {{item.description}}, and {{item.number}}.
curl -X POST "https://api.sequenzy.com/api/v1/transactional/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "customer@example.com",
    "slug": "order-confirmation",
    "variables": {
      "items": [
        { "title": "Pro plan", "description": "Monthly subscription" },
        { "title": "Priority support", "description": "Account add-on" }
      ]
    }
  }'

2. Direct Mode

Send email content directly without a pre-created template.
curl -X POST "https://api.sequenzy.com/api/v1/transactional/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "customer@example.com",
    "subject": "Your Order #{{orderNumber}}",
    "body": "<h1>Thank you for your order!</h1><p>Order: {{orderNumber}}</p>",
    "variables": {
      "orderNumber": "ORD-12345"
    }
  }'
Use cases:
  • Dynamic content generated by your app
  • One-off emails that don’t need templates
  • Testing and development
  • React Email templates (see below)

3. React Email Mode

Build type-safe, responsive email templates using React Email components and render them to HTML.
import { render } from "@react-email/render";
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Html,
  Preview,
  Text,
} from "@react-email/components";

// Create your email component
function OrderConfirmation({ orderNumber, total }: { orderNumber: string; total: string }) {
  return (
    <Html>
      <Head />
      <Preview>Your order #{orderNumber} is confirmed</Preview>
      <Body style={{ fontFamily: "sans-serif" }}>
        <Container>
          <Heading>Thank you for your order!</Heading>
          <Text>Order: #{orderNumber}</Text>
          <Text>Total: {total}</Text>
          <Button href="https://example.com/orders">View Order</Button>
        </Container>
      </Body>
    </Html>
  );
}

// Render and send
const html = await render(OrderConfirmation({ orderNumber: "ORD-12345", total: "$99.99" }));

await fetch("https://api.sequenzy.com/api/v1/transactional/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SEQUENZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "customer@example.com",
    subject: "Your Order #ORD-12345",
    body: html,
  }),
});
Benefits:
  • Type-safe templates with TypeScript
  • Reusable components
  • Responsive design built-in
  • Preview emails during development

4. SMTP Template Payloads

If another product can send through SMTP but lets you control the message body, you can point it at Sequenzy SMTP and send a JSON payload that references one of your existing transactional emails. This is useful for products such as Supabase Auth, where Supabase still triggers the email but Sequenzy renders and sends the final template. Requirements:
  • A Sequenzy API key for SMTP authentication
  • At least one sender profile on a verified sending domain
  • A transactional email with a sender profile assigned
{
  "slug": "supabase-magic-link",
  "dataVariables": {
    "confirmation_url": "https://app.example.com/auth/verify?token=123",
    "email": "user@example.com"
  }
}
You can also use transactionalId instead of slug. If Sequenzy receives a JSON body in this format over SMTP, it ignores the raw email subject/body and sends the saved transactional template instead. For the full Supabase setup, including variable mapping, see the Supabase integration guide.

Framework Quickstarts

See our quickstart guides for Bun, Next.js, and Express.

Creating Templates

In the Dashboard

  1. Go to Transactional in your dashboard
  2. Click Create Template
  3. Enter a slug (URL-friendly identifier)
  4. Design your email with the visual editor
  5. Add variable placeholders where needed
  6. Save and activate

Template Slugs

Slugs are unique identifiers for your templates:
order-confirmation
password-reset
welcome-email
invoice-reminder
shipping-update
Slugs are auto-generated from the template name but can be customized. They cannot be changed after creation.

Variables

Variables let you personalize transactional emails.

Syntax

<p>Hello {{FIRST_NAME}},</p>
<p>Your order {{orderNumber}} has shipped!</p>

With Defaults

Provide fallback values for missing variables:
<p>Hello {{FIRST_NAME|Customer}},</p>
<p>Hello {{ firstName | default: "Customer" }},</p>
If FIRST_NAME or firstName is empty, “Customer” is used.

Conditional HTML Sections

Use {{#if variable}}, {{else}}, and {{/if}} to render simple conditional sections in raw HTML. This is most useful with subscriber custom attributes:
{{#if subscriber.plan}}
<p>Your {{subscriber.plan}} workspace is ready.</p>
{{else}}
<p>Your workspace is ready.</p>
{{/if}}
{{#unless variable}}...{{/unless}} is also supported. Conditions check whether the resolved variable is present and truthy. Nested if/unless sections are supported, but helpers, comparisons, loops, and arbitrary Handlebars expressions are not.

System Variables

These variables are resolved at send time:
VariableSource
{{EMAIL}}Recipient email address
{{FIRST_NAME}}firstName, first_name, or FIRST_NAME from request variables
{{LAST_NAME}}lastName, last_name, or LAST_NAME from request variables
{{NAME}}name, NAME, fullName, or a full name built from FIRST_NAME and LAST_NAME variables
For REST API sends, pass name values in variables when you want to use them. Saved subscriber names are not automatically backfilled into FIRST_NAME, LAST_NAME, or NAME.

Custom Attributes

Any subscriber custom attributes are available:
<p>Your plan: {{plan}}</p>
<p>Account ID: {{userId}}</p>

Passed Variables

Variables passed in the API request:
{
  "variables": {
    "orderNumber": "ORD-123",
    "total": "$99.99"
  }
}
<p>Order: {{orderNumber}}</p>
<p>Total: {{total}}</p>

API Reference

Send Email

POST /api/v1/transactional/send
Template mode:
{
  "to": "user@example.com",
  "slug": "template-slug",
  "variables": {
    "key": "value"
  }
}
Direct mode:
{
  "to": "user@example.com",
  "subject": "Email Subject",
  "body": "<html>Email content</html>",
  "variables": {
    "key": "value"
  }
}
Response:
{
  "success": true,
  "data": {
    "jobId": "job_abc123",
    "email": "user@example.com"
  }
}

List Templates

GET /api/v1/transactional
Response:
{
  "success": true,
  "transactional": [
    {
      "id": "trans_abc123",
      "slug": "order-confirmation",
      "name": "Order Confirmation",
      "emailId": "email_abc123",
      "enabled": true
    }
  ]
}

Get Template Details

GET /api/v1/transactional/:idOrSlug
Response:
{
  "success": true,
  "transactional": {
    "id": "trans_abc123",
    "slug": "order-confirmation",
    "name": "Order Confirmation",
    "emailId": "email_abc123",
    "subject": "Your Order #{{orderNumber}}",
    "blocks": [],
    "enabled": true,
    "variables": ["orderNumber", "total", "items"]
  }
}

Auto-Creation

When you send to an email that doesn’t exist:
  1. A new subscriber is created automatically
  2. Status is set to active
  3. Custom attributes from variables are saved (if applicable)
This makes integration seamless—no need to create subscribers first.

Error Handling

Common Errors

ErrorCauseSolution
Template not foundInvalid slugCheck slug spelling
Template disabledTemplate is deactivatedEnable in dashboard
Missing required fieldsNo to, slug/subjectInclude required fields
Missing variablesTemplate has unfilled variablesPass all required variables
No sender configuredNo sender profileSet up sender in dashboard
Sending pausedAccount sending is pausedCheck sending status

Response Codes

CodeDescription
200Email queued successfully
400Validation error
401Invalid API key
404Template not found
500Server error

Best Practices

1. Use Templates

Templates are easier to maintain and update:
✓ Template: Update once, affects all future sends
✗ Direct: Must update every code path that sends email

2. Handle Variables Gracefully

Always provide defaults for optional variables, or guard larger optional sections with conditionals:
<!-- Compact default syntax -->
<p>Hi {{FIRST_NAME|there}},</p>

<!-- Liquid-style default filter syntax -->
<p>Hi {{ firstName | default: "there" }},</p>

<!-- Conditional section syntax -->
{{#if subscriber.plan}}
<p>Your {{subscriber.plan}} workspace is ready.</p>
{{else}}
<p>Your workspace is ready.</p>
{{/if}}
Conditional sections support if, unless, and else. They do not support comparisons or custom helpers.

3. Log Job IDs

Save the returned job ID for debugging:
const response = await sendTransactional({
  to: user.email,
  slug: "order-confirmation",
  variables: { orderNumber: order.id },
});

await saveToDatabase({
  orderId: order.id,
  emailJobId: response.data.jobId,
});

4. Use Meaningful Slugs

✓ "order-confirmation"
✓ "password-reset"
✓ "trial-expiring-reminder"

✗ "email1"
✗ "template_2024_01"

5. Test in Development

Use test emails before production:
const to =
  process.env.NODE_ENV === "development" ? "test@yourdomain.com" : user.email;

Integration Examples

Order Confirmation

async function sendOrderConfirmation(order) {
  await fetch("https://api.sequenzy.com/api/v1/transactional/send", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SEQUENZY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      to: order.customerEmail,
      slug: "order-confirmation",
      variables: {
        orderNumber: order.id,
        total: formatCurrency(order.total),
        items: order.items.map((i) => i.name).join(", "),
        shippingAddress: formatAddress(order.shippingAddress),
        estimatedDelivery: formatDate(order.estimatedDelivery),
      },
    }),
  });
}

Password Reset

async function sendPasswordReset(email, resetToken) {
  const resetUrl = `${APP_URL}/reset-password?token=${resetToken}`;

  await fetch("https://api.sequenzy.com/api/v1/transactional/send", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SEQUENZY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      to: email,
      slug: "password-reset",
      variables: {
        resetUrl,
        expiresIn: "24 hours",
      },
    }),
  });
}

Welcome Email

async function sendWelcome(user) {
  await fetch("https://api.sequenzy.com/api/v1/transactional/send", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SEQUENZY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      to: user.email,
      slug: "welcome",
      variables: {
        firstName: user.firstName,
        dashboardUrl: `${APP_URL}/dashboard`,
        docsUrl: `${APP_URL}/docs`,
      },
    }),
  });
}

API Reference

Full API documentation

Subscribers

Auto-creation and attributes

Campaigns

Broadcast marketing emails

Sequences

Automated email workflows