automations.settings.
Entry tab
The Entry tab answers two questions in order:- Trigger: when does enrollment happen?
- Audience: among the contacts the trigger fires for, who is allowed in?
Triggers
Every series has an entry trigger stored insettings.entry_trigger. The four options in the dashboard map to these values:
Manual activation
The simplest trigger. When you click Activate:- Halo evaluates the audience filters
- Every matching contact is added to the queue with
scheduled_forbased on the first email’s send window + delay - Contacts who match later (e.g. via a segment update) are not added; manual is one-shot at activation
completed).
User signs up
Halo enrolls every newly-identified contact automatically. Useful for welcome sequences:- New signup → “Welcome” email
-
- 1 day → “Get started” email
-
- 3 days → “Have you tried [feature]?”
- A first SDK
identify()call - The chat-widget lead form captures a new email
- A teammate adds the contact from the dashboard
signed_up_at trait. You do not need to send signed_up_at for “User signs up” series to work, and the trigger fires once per new row regardless.
Welcome series with imported users. If you bulk-import users via CSV, the Series API, or an integration sync, every imported contact is “newly created” from Halo’s point of view. Either keep the welcome series in
paused until your import completes, or use an event trigger keyed on a real product signup event.Triggers an event
Enroll when a specific event type fires. Configure:- Event name (
settings.entry_event_name): theevent_typeto listen for, e.g.trial_started,integration_connected,plan_upgraded
- The SDK’s automatic activity tracking
- Manual
POST /api/sdk/eventscalls from your backend - Some integrations (e.g. Stripe webhook events can map to internal event types)
Matches the audience filter
Enroll when a contact newly matches the audience filter set in the Audience section below. Requires:- Audience source set to Filter by criteria (
audience_type: "segment") - At least one filter
free to pro → enroll in a “Welcome to Pro” series.
Segment matches are evaluated:
- On every SDK
identify()call (when traits or company traits change) - By periodic background re-evaluation
segment_match series, existing matches enroll immediately. After that, new matches enroll on their next SDK identify. The “On activate” card in the side panel shows the current match count for this reason.
The activate route returns 400 if entry_trigger is segment_match and audience source isn’t “Filter by criteria”.
Audience
The Audience section narrows down who the trigger applies to. Two dropdowns drive everything:Contact type (settings.audience_contact_type)
Filters the underlying end_users rows by contact_type:
This filter applies on every trigger path: the activate route honors it for manual sends, and the enrollment runtime honors it for
user_created / event / segment_match so a lead can’t slip into a “Users only” series.
Source (audience_type on the automation row)
Picks how the audience is defined:
Quick filters
When source is All contacts with email or Filter by criteria, the side panel shows one-click presets that seed the filter set:
Clicking a preset switches Source to Filter by criteria if it isn’t already, and appends the preset filter into the first AND group. Repeat clicks are idempotent.
Filter by criteria
When source is Filter by criteria, the audience builder is a list of AND/OR filter groups. Each filter is{ field, operator, value }. The full list of fields is defined in getAllAudienceFields() and combines:
- All user fields (identity, lifecycle, revenue, activity, renewal, Stripe)
- All company fields, prefixed with
company. - All lead fields, prefixed with
lead: - Any custom fields discovered on
end_users.custom_fieldsandcompanies.custom_fields
Date operators expect ISO 8601 (
YYYY-MM-DD is fine for date-only comparisons; full timestamps work too).
The recipient count under “On activate” recomputes whenever the filter set changes. Click View who will enroll / View who currently matches to inspect the actual contacts.
If the count stays at 0 but you expect matches, see Troubleshooting. A common case is filtering on Role without passing role in identify().
Options
Allow re-enrollment
When re-enrolling, prior
automation_recipients rows are deleted to satisfy the (step_id, end_user_id) unique index. Aggregate stats stay counter-based.
If a contact has an active pending or processing recipient row, new enrollment is skipped (no reset, no duplicate) regardless of reenroll_enabled.
Company priority (settings.company_priority)
When enabled, at most one contact per company is enrolled. If another seat from the same company already has an active recipient row, this contact is skipped. Contacts without a company are unaffected.
Use this to avoid emailing the same company multiple times from a series like “Renewal coming up” or “Trial ending soon”.
Goal tab
The Goal tab is purely for measurement. Toggling it on does not change who gets enrolled.
A recipient counts as “converted” when their traits, custom fields, Stripe data, or events start matching
goal_filters within goal_window_days of their enrollment timestamp.
Common goals:
- Subscription activated (
stripe:subscription_status is active) - Plan upgraded (
plan is_not free) - Ticket resolved (custom event filter)
goal_enabled = true without any goal filters is flagged in the side panel. No conversions will be tracked because there’s nothing to match.
Exit tab
Exit rules remove recipients from the series before they reach the final step. Multiple rules can be active at once; any one match is enough to exit. They live onsettings.exit_rules as an array of strings.
Exit rules are evaluated:
- At enrollment time (
filter_matchonly): if the contact already matches the exit filter, they aren’t enrolled at all - On every cron tick the recipient is processed: for
replied,event,segment_leave,filter_match - On the unsubscribe webhook: for
unsubscribed
exit_reason on automation_recipients. Recipients who reach the final step are stamped exit_reason: "completed" automatically.
Conditions (mid-series branches)
Conditions are different from exit rules. They live as steps inside the series (added in the Manual Builder canvas), not as series-level settings. They check something about the recipient’s history at the moment that step runs.user_property operators
Behavior on missing values:
=,>,<,>=,<=, andcontainsevaluate to false when the field is unset!=evaluates to true when the field is unset and the expected value is non-empty
event_fired scoping
event_fired matches events that fired at or after the recipient reached this condition step (we use the recipient row’s created_at as the lower bound). This means the pattern “trial_started → 3-day wait → paid?” only passes for users who paid during the wait, not anyone who paid in their account history.
Condition evaluation outcome
When a condition step is reached, Halo checks the condition:
There is no second branch for the false path. Conditions are gates, not splits; failed users exit the series at that step. The canvas only exposes a single output handle to make this behavior explicit. To create true branching, use multiple parallel series triggered by different segments.
Activation blockers
When you click Activate, the activate route runs validation and returns a 400 with a specific error message if anything is wrong. The side panel pre-flags these in the “Cannot activate yet” amber card so you can fix them up front. Order matches the activate route’s own validation.user_created / event / segment_match triggers are forward-only. Activating one of them with 0 currently-eligible contacts is allowed; the series sits ready to enroll new matches as they arrive. (segment_match is the exception: existing matches enroll immediately at activation time, then ongoing matches enroll on identify.)
Combining triggers and conditions
A common pattern:- Trigger:
eventwithentry_event_name = trial_started - Step 1: “Welcome to your trial” email
- Step 2: Wait 3 days
- Step 3: Condition
event_firedwithcondition_value = paid_subscription_started- True → next step (e.g. “Welcome to the Pro plan”)
- False → recipient exits with
condition_not_met
Where to go next
Series
Build the steps that triggers and conditions live within.
Subscription Lists
Consent management for the unsubscribed exit rule.
User Traits
Reference for the trait fields available in audience filters and conditions.
Troubleshooting
Why matched count is 0 and how to verify filters against real data.
Company Traits
Reference for the company-prefixed fields in audience filters.