# Sequences API

> Build automated email sequences and drip campaigns with the Broadcast API: create sequences, add steps, schedule delays, and enroll subscribers from your code.

Source: https://sendbroadcast.net/docs/api-sequences

The Sequences API provides full CRUD (Create, Read, Update, Delete) operations for managing automated email sequences and their steps. You can create sequences, add steps (emails, delays, conditions), and manage subscriber enrollment programmatically.

## Required Permissions

All endpoints require authentication via an API token with appropriate permissions:

- **Read Permission**: Required for GET endpoints (list, show sequences and steps)
- **Write Permission**: Required for POST, PATCH, DELETE endpoints (create, update, delete sequences and steps)
- **Subscribers Read**: Required for listing subscribers in a sequence
- **Subscribers Write**: Required for adding/removing subscribers from sequences

## Sequence Object

The sequence object contains the following fields:

| Field | Description |
| --- | --- |
| `id` | Unique identifier of the sequence |
| `label` | The name of the sequence |
| `active` | Whether the sequence is active and processing subscribers |
| `track_opens` | Whether open tracking is enabled for emails in this sequence |
| `track_clicks` | Whether click tracking is enabled for emails in this sequence |
| `use_any_email_server` | Whether to use any available email server |
| `email_server_ids` | Array of specific email server IDs to use |
| `sender_identity_id` | ID of the sender identity (From address) to send as; omit to use the channel default |
| `init_segment_id` | Segment ID for automatic enrollment trigger |
| `init_tag` | Tag that triggers automatic enrollment |
| `excluded_segment_ids` | Segments whose members never get an email from this sequence: each email step is skipped for them, however they were enrolled, and the entry segment never enrolls them. Must be segments of this channel and not the `init_segment_id` (422 otherwise). Settable on create and update |
| `topic_id` | Topic for the email steps: only subscribers who receive it get them; the others skip them and continue. Unsubscribe links in sequence emails always unsubscribe from everything. Settable on create and update |
| `parent_sequence_id` | ID of the sequence this was duplicated from (null if not a duplicate) |
| `subscribers_count` | Number of subscribers currently in the sequence: active or paused enrollments. Completed, stopped and removed enrollments, and redacted subscribers, are not counted |
| `created_at` | When the sequence was created |
| `updated_at` | When the sequence was last updated |

(Requires Broadcast 2.50 or later)

The sequence object also has `reply_to`: the Reply-To address for the sequence's emails, or `null`. With `null`, replies go to the sender address. Settable on create and update; a value that is not an email address is a 422.

## List Sequences

```
GET /api/v1/sequences
```

Retrieve a list of all sequences for the authenticated broadcast channel.

### Query Parameters

- `limit` (optional): Maximum number of sequences to return, from 1 to 1000
- `offset` (optional): Number of sequences to skip, 0 or more

A `limit` or `offset` outside those ranges, or that is not a whole number, returns `422 Unprocessable Entity`.

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/sequences
```

### Response

```json
{
  "data": [
    {
      "id": 1,
      "label": "Welcome Series",
      "active": true,
      "track_opens": true,
      "track_clicks": true,
      "use_any_email_server": true,
      "email_server_ids": [],
      "init_segment_id": null,
      "init_tag": null,
      "parent_sequence_id": null,
      "subscribers_count": 150,
      "created_at": "2024-01-01T12:00:00Z",
      "updated_at": "2024-01-01T12:00:00Z"
    }
  ],
  "total": 1
}
```

## Get Sequence

```
GET /api/v1/sequences/:id
```

Retrieve details of a specific sequence.

### Query Parameters

- `include_steps` (optional): Set to `true` to include all steps in the response

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "http://your-domain.com/api/v1/sequences/123?include_steps=true"
```

### Response

```json
{
  "id": 123,
  "label": "Welcome Series",
  "active": true,
  "track_opens": true,
  "track_clicks": true,
  "use_any_email_server": true,
  "email_server_ids": [],
  "init_segment_id": null,
  "init_tag": "new-subscriber",
  "parent_sequence_id": null,
  "subscribers_count": 150,
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-01T12:00:00Z",
  "steps": [
    {
      "id": 1,
      "action": "entry_point",
      "label": "Entry Point",
      "parent_id": null
    },
    {
      "id": 2,
      "action": "send_email",
      "label": "Welcome Email",
      "parent_id": 1,
      "subject": "Welcome to our service!"
    }
  ]
}
```

## Create Sequence

```
POST /api/v1/sequences
```

Create a new sequence. An entry point step is automatically created with the sequence.

### Parameters

- `label` (required): Name of the sequence
- `active` (optional): Whether the sequence is active (default: false)
- `track_opens` (optional): Enable open tracking. Left out, it uses the channel default (`tracking.track_opens` in the [Channel Settings API](https://sendbroadcast.net/docs/api-channel-settings)), which is `false` unless you changed it. An open condition step turns it on.
- `track_clicks` (optional): Enable click tracking. Left out, it uses the channel default `tracking.track_clicks` (`false` unless you changed it). A click condition step turns it on.
- `use_any_email_server` (optional): Use any available email server (default: true)
- `email_server_ids` (optional): Array of specific email server IDs
- `init_segment_id` (optional): Segment ID for automatic enrollment
- `init_tag` (optional): Tag that triggers automatic enrollment

(Requires Broadcast 2.50 or later)

- `reply_to` (optional): Reply-To address for the sequence's emails. Empty or `null` sends replies to the sender address.

### Request

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence": {
      "label": "Onboarding Series",
      "active": true,
      "track_opens": true,
      "track_clicks": true,
      "init_tag": "new-signup"
    }
  }' \
  http://your-domain.com/api/v1/sequences
```

### Response

```json
{
  "id": 123
}
```

## Update Sequence

```
PATCH /api/v1/sequences/:id
```

Update an existing sequence.

### Request

```bash
curl -X PATCH \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence": {
      "label": "Updated Onboarding Series",
      "active": false
    }
  }' \
  http://your-domain.com/api/v1/sequences/123
```

### Response

Returns the updated sequence object.

## Delete Sequence

```
DELETE /api/v1/sequences/:id
```

Delete a sequence and all its steps.

### Request

```bash
curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/sequences/123
```

### Response

```json
{
  "message": "Sequence deleted successfully"
}
```

---

# Sequence Steps API

Manage individual steps within a sequence. Steps define the actions taken as subscribers progress through the sequence.

## Step Object

| Field | Description |
| --- | --- |
| `id` | Unique identifier of the step |
| `action` | Type of action (see supported actions below) |
| `label` | Display name for the step |
| `parent_id` | ID of the parent step (null for entry_point) |
| `true_branch` | Whether this step is on the true branch of a condition |
| `subject` | Email subject (for send_email action) |
| `preheader` | Email preheader text (for send_email action) |
| `body` | Email body content (for send_email action) |
| `html_body` | Whether body is HTML (for send_email action) |
| `html_body_add_footer` | Whether the channel footer is appended to an HTML email. An email with its own unsubscribe link (a footer block that shows the link, or a `{{ unsubscribe_url }}` link that every subscriber sees) never gets the channel footer on top, whatever this value (2.46.0 or later). Without an own link, rich text and block emails always get the channel footer, and HTML emails follow this value. When the step uses a `template_id`, the template's own setting applies and is what the API returns. |
| `include_unsubscribe_link` | Whether the email carries Broadcast's unsubscribe link (default: true). When false, the email gets no channel footer and no `List-Unsubscribe` header, `{{ unsubscribe_url }}` renders blank, a footer block hides its unsubscribe link, and a hand-written link to `{{ unsubscribe_url }}` is removed with its text. A step that uses a `template_id` follows this value too. Accepted by the API in 2.46.0 or later. |
| `delay` | Delay in seconds (for delay action) |
| `delay_until_time` | Time to send (for delay_until_time action) |
| `delay_until_timezone` | Timezone for delay_until_time |
| `condition_setting` | Condition type (for condition action) |
| `target_sequence_id` | Target sequence (for move_to_sequence action) |
| `tags` | Tags to add/remove (for tag actions) |
| `has_true_branch` | Whether step has a true branch child |
| `has_false_branch` | Whether step has a false branch child |
| `ready` | Whether the step is ready for processing |
| `template_id` | ID of the template to use (for send_email action) |
| `content_type` | Content type for the step (e.g., "plain", "html", "blocks") |
| `blocks_json` | JSON representation of email blocks (for drag-and-drop editor) |
| `created_at` | When the step was created |
| `updated_at` | When the step was last updated |

(Requires Broadcast 2.50 or later)

A `send_email` step also has `reply_to`: the step's own Reply-To address, or `null`. With `null`, the email uses the sequence's `reply_to`, and the sender address when that is `null` too. The API returns the step's own value, not the resolved one.

## Supported Step Actions

| Action | Description | Required Fields |
| --- | --- | --- |
| `entry_point` | Starting point (auto-created) | - |
| `send_email` | Send an email to the subscriber | `subject`, `body` |
| `delay` | Wait for specified seconds | `delay` |
| `delay_until_time` | Wait until specific time of day | `delay_until_time`, `delay_until_timezone` |
| `condition` | Branch based on conditions | `condition_setting` |
| `move_to_sequence` | Move subscriber to another sequence | `target_sequence_id` |
| `add_tag_to_subscriber` | Add tags to subscriber | `taggify_list` |
| `remove_tag_from_subscriber` | Remove tags from subscriber | `taggify_list` |
| `deactivate_subscriber` | Deactivate the subscriber | - |
| `make_http_request` | Send HTTP webhook | - |

## Condition Settings

For `condition` steps, use one of these condition settings:

- `any_email_opened` - Subscriber opened any email in this sequence
- `previous_email_opened` - Subscriber opened the previous email
- `any_email_clicked` - Subscriber clicked any link in this sequence
- `previous_email_clicked` - Subscriber clicked a link in the previous email

## List Steps

```
GET /api/v1/sequences/:sequence_id/steps
```

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Response

```json
{
  "data": [
    {
      "id": 1,
      "action": "entry_point",
      "label": "Entry Point",
      "parent_id": null,
      "true_branch": true,
      "has_true_branch": true,
      "has_false_branch": false,
      "ready": true,
      "created_at": "2024-01-01T12:00:00Z",
      "updated_at": "2024-01-01T12:00:00Z"
    },
    {
      "id": 2,
      "action": "send_email",
      "label": "Welcome Email",
      "parent_id": 1,
      "subject": "Welcome!",
      "body": "Hello {{subscriber.first_name}}...",
      "ready": true
    }
  ],
  "total": 2
}
```

## Get Step

```
GET /api/v1/sequences/:sequence_id/steps/:id
```

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/sequences/123/steps/456
```

## Create Step

```
POST /api/v1/sequences/:sequence_id/steps
```

Create a new step in the sequence. The step will be added as a child of the specified parent.

### Common Parameters

- `action` (required): The type of step action
- `label` (required): Display name for the step
- `parent_id` (required): ID of the parent step to attach to

### Additional Parameters for `send_email` Action

- `subject`: Email subject line
- `preheader`: Email preview text
- `body`: Email body content
- `html_body`: Whether body is HTML (default: false)
- `html_body_add_footer`: Whether the channel footer is appended to an HTML email. An email with its own unsubscribe link (a footer block that shows the link, or a `{{ unsubscribe_url }}` link that every subscriber sees) never gets the channel footer on top, whatever this value (2.46.0 or later). Without an own link, rich text and block emails always get the channel footer, and HTML emails follow this value. A step that uses a `template_id` follows the template's setting instead, and that is the value the API returns. A value that disagrees with the document or the template is replaced, and the response carries a `parameter_overridden` warning (see [API Response Warnings](https://sendbroadcast.net/docs/api-response-warnings#the-footer-of-a-block-email)).
- `include_unsubscribe_link`: Whether the email carries Broadcast's unsubscribe link (default: true). When false, the email gets no channel footer and no `List-Unsubscribe` header, `{{ unsubscribe_url }}` renders blank, a footer block hides its unsubscribe link, and a hand-written link to `{{ unsubscribe_url }}` is removed with its text. A step that uses a `template_id` follows this value too. Accepted by the API in 2.46.0 or later.
- `template_id`: Use an existing template instead of custom content
- `content_type`: Content type ("plain", "html", or "blocks")
- `blocks_json`: JSON for drag-and-drop email builder blocks

(Requires Broadcast 2.50 or later)

- `reply_to`: Reply-To address for this email; it overrides the sequence's `reply_to`. Empty or `null` uses the sequence's. A value that is not an email address is a 422.

### Send Email Step

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "action": "send_email",
      "label": "Welcome Email",
      "parent_id": 1,
      "subject": "Welcome to our service!",
      "preheader": "Thanks for joining",
      "body": "Hello {{ subscriber.first_name }}, welcome aboard!",
      "html_body": false,
      "html_body_add_footer": true,
      "include_unsubscribe_link": true
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Delay Step

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "action": "delay",
      "label": "Wait 1 day",
      "parent_id": 2,
      "delay": 86400
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Delay Until Time Step

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "action": "delay_until_time",
      "label": "Send at 9 AM",
      "parent_id": 3,
      "delay_until_time": "09:00:00",
      "delay_until_timezone": "America/New_York"
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Condition Step

Creating a condition step automatically creates true/false branch steps:

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "action": "condition",
      "label": "Check if opened email",
      "parent_id": 4,
      "condition_setting": "previous_email_opened"
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Move to Sequence Step

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "action": "move_to_sequence",
      "label": "Move to Sales Sequence",
      "parent_id": 5,
      "target_sequence_id": 10
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Add/Remove Tag Step

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "action": "add_tag_to_subscriber",
      "label": "Add engaged tag",
      "parent_id": 6,
      "taggify_list": "engaged,active"
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps
```

### Response

```json
{
  "id": 456
}
```

## Update Step

```
PATCH /api/v1/sequences/:sequence_id/steps/:id
```

Update an existing step. The `action` type cannot be changed after creation. The parameters are the same as for Create Step, `include_unsubscribe_link` included.

### Request

```bash
curl -X PATCH \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sequence_step": {
      "label": "Updated Welcome Email",
      "subject": "Welcome to our amazing service!"
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/steps/456
```

### Response

Returns the updated step object.

## Delete Step

```
DELETE /api/v1/sequences/:sequence_id/steps/:id
```

Delete a step. Entry point steps cannot be deleted. When a parent step is deleted, its children are reassigned to the grandparent.

### Request

```bash
curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/sequences/123/steps/456
```

### Response

```json
{
  "message": "Step deleted successfully"
}
```

## Move Step

```
POST /api/v1/sequences/:sequence_id/steps/:id/move
```

Move a step to be under a different parent step.

### Movable Step Types

- `send_email`
- `delay`
- `delay_until_time`
- `move_to_sequence`
- `deactivate_subscriber`
- `add_tag_to_subscriber`
- `remove_tag_from_subscriber`
- `make_http_request`

### Non-Movable Step Types

- `entry_point`
- `condition`
- `condition_branch`

### Request

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "under_id": 5
  }' \
  http://your-domain.com/api/v1/sequences/123/steps/456/move
```

### Response

Returns the updated step object with new `parent_id`.

---

# Subscriber Enrollment

Manage subscriber enrollment in sequences.

## Subscriber Sequence Object

| Field | Description |
| --- | --- |
| `id` | Unique identifier of the subscriber sequence |
| `status` | Current status of the sequence subscription |
| `started_at` | When the subscriber was added to the sequence |
| `completed_at` | When the subscriber completed or was removed from the sequence |
| `next_trigger_at` | When the next email in the sequence will be sent |
| `subscriber` | Information about the subscriber |
| `subscriber.id` | Unique identifier of the subscriber |
| `subscriber.email` | Email address of the subscriber |

## List Subscribers in a Sequence

```
GET /api/v1/sequences/:id/list_subscribers
```

Returns a paginated list of subscribers in the specified sequence. Redacted (GDPR-erased) subscribers are left out.

### Query Parameters

- `page` (optional): The page number to return (default is 1)

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "http://your-domain.com/api/v1/sequences/123/list_subscribers?page=1"
```

### Response

```json
{
  "subscriber_sequences": [
    {
      "id": "123",
      "status": "active",
      "started_at": "2024-03-20T10:00:00Z",
      "completed_at": null,
      "next_trigger_at": "2024-03-21T10:00:00Z",
      "subscriber": {
        "id": "456",
        "email": "user@example.com"
      }
    }
  ],
  "pagination": {
    "prev": null,
    "next": 2,
    "count": 95,
    "pages": 10,
    "page": 1
  }
}
```

## Add Subscriber to Sequence

```
POST /api/v1/sequences/:id/add_subscriber
```

Adds a subscriber to the specified sequence. If the subscriber email does not exist in the system, the subscriber will be created.

### Parameters

- `email` (required): The email address of the subscriber
- `first_name` (optional): The first name of the subscriber
- `last_name` (optional): The last name of the subscriber

### Request

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "subscriber": {
      "email": "user@example.com",
      "first_name": "John",
      "last_name": "Doe"
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/add_subscriber
```

### Response

Returns `201 Created` on success or `422 Unprocessable Entity` if the request fails. A redacted (GDPR-erased) subscriber cannot be enrolled and returns `404 Not Found`.

## Remove Subscriber from Sequence

```
DELETE /api/v1/sequences/:id/remove_subscriber
```

Removes a subscriber from the specified sequence.

### Parameters

- `email` (required): The email address of the subscriber

### Request

```bash
curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "subscriber": {
      "email": "user@example.com"
    }
  }' \
  http://your-domain.com/api/v1/sequences/123/remove_subscriber
```

### Response

Returns `200 OK` on success or `422 Unprocessable Entity` if the request fails.

---

## Error Responses

### 401 Unauthorized

```json
{
  "error": "Unauthorized"
}
```

### 404 Not Found

```json
{
  "error": "Sequence not found"
}
```

```json
{
  "error": "Step not found"
}
```

### 422 Unprocessable Entity

```json
{
  "error": "Label can't be blank"
}
```

```json
{
  "error": "Cannot delete entry_point step"
}
```

```json
{
  "error": "Cannot move entry_point step"
}
```
