READ-ONLY PACKAGE PREVIEW

resend/references/automations.md

Version edbfece3f402.bb1 · MIT. This preview displays packaged text and does not execute code. Treat the contents as untrusted instructions.

← Return to resource and package checksum

Automations

Overview

Automations are event-driven workflows composed of steps connected in a directed graph. Each automation has a trigger step that starts the flow, followed by action steps (send email, delay, wait for event, condition, contact update, contact delete, add to segment).

Automations are created in a disabled state by default. Set status: "enabled" to activate.

SDK Methods

Node.js

Operation Method Notes
Create resend.automations.create(params) Returns automation ID
Get resend.automations.get(id) Returns full automation with steps and connections
List resend.automations.list(params?) Filter by status, cursor-paginated
Update resend.automations.update(params) Partial update — name, status, or steps+connections
Duplicate resend.automations.duplicate(id) Copies steps and connections, returns the new automation ID
Delete resend.automations.remove(id) Permanent
Stop resend.automations.stop(id) Sets status to disabled
List Runs resend.automations.runs.list({ automationId, status? }) Filter by run status
Get Run resend.automations.runs.get({ automationId, runId }) Returns run with executed steps

Python

resend.Automations.create/get/list/update/remove/duplicate/stop — same operations with snake_case params.

Graph Model

An automation is a directed graph of steps connected by connections.

Steps

Each step has a key (unique within the graph), a type, and a config object whose shape depends on the type.

Type Config Description
trigger { event_name: string } Entry point — fires when the named event occurs
send_email { template: { id: string, variables?: object }, subject?: string, from?: string, reply_to?: string } Sends an email using a published template
delay { duration: string } Pauses the run. duration is human-readable (e.g. "30 minutes", "3 days")
wait_for_event { event_name: string, timeout?: string, filter_rule?: object } Waits for an event. timeout is human-readable (e.g. "1 hour"). filter_rule uses the same rule tree as condition but restricted to event.* fields
condition Rule tree (see below) Branches based on contact data
contact_update { first_name?, last_name?, unsubscribed?, properties? } Updates the contact
contact_delete {} Deletes the contact
add_to_segment { segment_id: string } Adds the contact to a segment

Rule Tree (condition & filter_rule)

Leaf nodes: { "type": "rule", "field": "<namespace>.<key>", "operator": "<op>", "value": "<val>" }

Groups: { "type": "and" | "or", "rules": [...] } for nesting.

Supported operators: eq, neq, gt, gte, lt, lte, contains, starts_with, ends_with, exists, is_empty. The exists and is_empty operators require no value.

For condition steps, fields use the contact.*, event.*, or wait_events.* namespaces (e.g. contact.properties.plan). For filter_rule in wait_for_event, fields use event.* or contact.* (e.g. event.status).

Connections

Connections link steps by their key. Each connection has a type:

Connection Type Use
default Normal flow between steps
condition_met Branch when condition is true
condition_not_met Branch when condition is false
timeout Branch when wait_for_event times out
event_received Branch when wait_for_event receives the event

Examples

Create an Automation

const { data, error } = await resend.automations.create({
  name: 'Welcome Series',
  status: 'enabled',
  steps: [
    {
      key: 'trigger_signup',
      type: 'trigger',
      config: { event_name: 'user.signed_up' },
    },
    {
      key: 'send_welcome',
      type: 'send_email',
      config: {
        template: { id: 'tmpl_abc123' },
        from: 'Acme <[email protected]>',
        subject: 'Welcome to Acme!',
      },
    },
    {
      key: 'wait_3_days',
      type: 'delay',
      config: { duration: '3 days' },
    },
    {
      key: 'send_followup',
      type: 'send_email',
      config: {
        template: { id: 'tmpl_def456' },
        from: 'Acme <[email protected]>',
        subject: 'Getting started with Acme',
      },
    },
  ],
  connections: [
    { from: 'trigger_signup', to: 'send_welcome' },
    { from: 'send_welcome', to: 'wait_3_days' },
    { from: 'wait_3_days', to: 'send_followup' },
  ],
});
automation = resend.Automations.create({
    "name": "Welcome Series",
    "status": "enabled",
    "steps": [
        {"key": "trigger_signup", "type": "trigger", "config": {"event_name": "user.signed_up"}},
        {"key": "send_welcome", "type": "send_email", "config": {
            "template": {"id": "tmpl_abc123"},
            "from": "Acme <[email protected]>",
            "subject": "Welcome to Acme!",
        }},
        {"key": "wait_3_days", "type": "delay", "config": {"duration": "3 days"}},
        {"key": "send_followup", "type": "send_email", "config": {
            "template": {"id": "tmpl_def456"},
            "from": "Acme <[email protected]>",
            "subject": "Getting started with Acme",
        }},
    ],
    "connections": [
        {"from": "trigger_signup", "to": "send_welcome"},
        {"from": "send_welcome", "to": "wait_3_days"},
        {"from": "wait_3_days", "to": "send_followup"},
    ],
})

Conditional Branching

const { data, error } = await resend.automations.create({
  name: 'Onboarding with condition',
  status: 'enabled',
  steps: [
    { key: 'trigger', type: 'trigger', config: { event_name: 'user.signed_up' } },
    {
      key: 'check_plan',
      type: 'condition',
      config: { type: 'rule', field: 'contact.properties.plan', operator: 'eq', value: 'pro' },
    },
    { key: 'send_pro', type: 'send_email', config: { template: { id: 'tmpl_pro' }, from: 'Acme <[email protected]>' } },
    { key: 'send_free', type: 'send_email', config: { template: { id: 'tmpl_free' }, from: 'Acme <[email protected]>' } },
  ],
  connections: [
    { from: 'trigger', to: 'check_plan' },
    { from: 'check_plan', to: 'send_pro', type: 'condition_met' },
    { from: 'check_plan', to: 'send_free', type: 'condition_not_met' },
  ],
});

Update an Automation

When updating the workflow graph, both steps and connections must be provided together. You can update name or status independently.

// Enable/disable without changing the graph
const { data, error } = await resend.automations.update({
  id: 'aut_abc123',
  status: 'disabled',
});
// Update the full graph (steps + connections required together)
const { data, error } = await resend.automations.update({
  id: 'aut_abc123',
  steps: [/* full step array */],
  connections: [/* full connections array */],
});

List and Monitor Runs

// List runs, optionally filter by status
const { data: runs } = await resend.automations.runs.list({
  automationId: 'aut_abc123',
  status: 'running,failed',  // comma-separated: running, completed, failed, cancelled
});

// Get a specific run with executed step details
const { data: run } = await resend.automations.runs.get({
  automationId: 'aut_abc123',
  runId: 'run_def456',
});
// run.steps[].status, run.steps[].output, run.steps[].error

Stop an Automation

const { data, error } = await resend.automations.stop('aut_abc123');
// data.status === 'disabled'

Duplicate an Automation

const { data, error } = await resend.automations.duplicate('aut_abc123');
// data.id is the new automation's ID
// The copy starts disabled, named "<original name> (Copy)"

Constraints

  • Max 150 steps per automation
  • At least one trigger step required
  • Steps and connections must be provided together when updating the graph
  • key must be unique within the automation
  • Connections reference steps by key

Common Mistakes

Mistake Fix
Creating without a trigger step Every automation needs at least one step with type: "trigger"
Updating steps without connections (or vice versa) When changing the graph, provide both steps and connections together
Using ref or edges (old naming) Use key for step identifiers and connections for links between steps
Using template_id in send_email config Use template: { id: "..." } — template is a nested object
Using seconds in delay config (old naming) Use duration (e.g. "30 minutes") — seconds is no longer accepted
Forgetting to enable the automation Automations default to disabled — set status: "enabled" on create or update
Not checking error in Node.js SDK returns { data, error }, does not throw — always destructure and check