Drip Sequences Reference
Follow-up sequences (drips) are timed email to leads, owned by a brokerage and triggered by a pipeline stage. This page lists the fields and the endpoints. For how to build one and what stops it, read set up follow-up sequences.
Sequence
Section titled “Sequence”| Field | Type | Notes |
|---|---|---|
id | integer | |
brokerage_id | integer | The owning brokerage. Non-admins are pinned to their own. |
name | string, up to 120 | Unique per brokerage. |
description | string or null | Free text for the list. |
trigger_state | NEW, CONTACTING, QUALIFIED, ARCHIVED | The stage whose entry enrolls a lead. |
pipeline_type | buyer, seller, or null | Null means both. Stored today; filtering on it arrives with the lead pipeline type. |
active | boolean | Off pauses enrollment and stops everyone currently on it (sequence_deactivated). |
stop_on_state_change | boolean, default true | Whether leaving the enrolled stage ends the enrollment. |
steps | list of steps | Ordered by position. |
active_enrollments | integer | Read only. |
total_enrollments | integer | Read only. Includes completed and stopped. |
| Field | Type | Notes |
|---|---|---|
position | integer from 1 | Assigned from list order on save. |
delay_days | integer, 0 to 365 | From enrollment for the first step, from the previous step otherwise. |
subject_template | string, up to 300 | Placeholders allowed. |
body_template | string, up to 20000 | Plain text with placeholders. The unsubscribe footer is appended at send time. |
Placeholders: {{first_name}}, {{last_name}}, {{agent_name}}, {{agent_phone}}, {{agent_email}}, {{brokerage_name}}, {{brokerage_phone}}. Unknown names render as empty text. GET /api/v1/admin/drips/meta returns the current list.
Enrollments point at a step by position, not by id, so editing or reordering steps does not orphan a lead mid-sequence: the next send picks up whichever step now holds that position. Deleting the step a lead is waiting on skips to the next one that exists, or completes.
Enrollment
Section titled “Enrollment”One row per lead per sequence.
| Field | Type | Notes |
|---|---|---|
lead_id, lead_name, lead_email | ||
enrolled_at | timestamp | |
enrolled_state | stage | The stage the lead was in when enrolled. Compared against the current stage before every send when stop_on_state_change is on. |
next_step_position | integer or null | Null once completed or stopped. |
next_send_at | timestamp or null | When the sweep will send the next step. |
status | active, completed, stopped | |
stopped_reason | see below | |
stopped_at | timestamp or null | |
last_sent_at | timestamp or null | |
send_failures | integer | Consecutive failures; reset to 0 on a successful send. |
Stop reasons
Section titled “Stop reasons”| Reason | Set when |
|---|---|
state_changed | The lead left the stage it was enrolled in and the sequence stops on stage change. |
unsubscribed | The lead has the Unsubscribed flag (link or staff). |
flagged | The lead has Do Not Contact or No Email. |
manual | Staff clicked Stop. |
sequence_deactivated | The sequence was turned off. |
send_failed | Three consecutive sends failed. |
no_email | The lead has no email address. |
Re-enrolling through the API or the lead page restarts a completed or stopped lead from step one with a fresh enrolled_at. An active enrollment is not restarted; the request answers 409.
When sending happens
Section titled “When sending happens”The lifecycle sweep that runs inside the API about once a minute also runs the drips. Each pass sends at most one step per enrollment. Before sending, it re-derives the lead’s stage, re-reads its flags, and checks for an email address, in that order, so nothing that was set since the last pass is missed. A failed send leaves next_send_at alone and is retried on the next pass; the third consecutive failure stops the enrollment.
Emails go out from the platform sender with the assigned agent’s address as Reply-To. Every body ends with an unsubscribe line. With DRIP_UNSUBSCRIBE_SECRET (or a non-default FAVORITES_SECRET) and PUBLIC_API_URL set, the line is a link to GET /api/drips/unsubscribe/{token}, where the token is the lead id signed with the secret; visiting it sets the Unsubscribed flag with reason unsubscribe link, stops the lead’s sequences, and shows a plain confirmation page. Without those settings the line asks the reader to reply, and the run report notes that links are disabled.
Admin API
Section titled “Admin API”All routes live under /api/v1/admin/drips and take the same bearer auth as the rest of the admin API. Brokers and owners see and edit their brokerage; agents may read; admins see every brokerage and may pass brokerage_id when listing or creating. Out-of-scope ids answer 404. Mutations by an agent answer 403.
| Method and path | Does |
|---|---|
GET /meta | Trigger stages, pipeline types, placeholders, whether unsubscribe links are enabled. |
GET /?brokerage_id= | List sequences with steps and enrollment counts. |
POST / | Create. Body: the sequence fields plus steps as an ordered list of {delay_days, subject_template, body_template}. 409 on a duplicate name. |
GET /{id} | One sequence. |
PUT /{id} | Partial update. When steps is present it replaces the whole list. Setting active: false stops current enrollments. |
DELETE /{id} | Deletes the sequence and its enrollments. |
GET /{id}/enrollments | Every enrollment with lead name, status, next send. Agents see only their own leads. |
POST /{id}/enrollments/{lead_id} | Enroll or restart from step one. 409 if already active or the sequence has no steps. |
DELETE /{id}/enrollments/{lead_id} | Stop with reason manual. 404 if nothing is active. |
POST /{id}/preview | Body {lead_id, position?}. Returns the rendered subject and body, To, Reply-To, and whether the footer is a link. Sends nothing. |
POST /run?dry_run= | Admins only. Runs the drip pass now and returns the actions taken (sent, retry, stopped, completed, or would_send in a dry run). |
The lead page uses GET /api/v1/admin/leads/{lead_id}/drips, which returns the lead’s enrollments, the brokerage’s active sequences it could be put on, and can_manage for the caller.