Skip to main content
The matched count on a series trigger node and in the Series Rules panel is a live preview of how many contacts currently pass your audience filters (after contact type narrowing and, where applicable, subscription consent). It is not the same as enrolled, which only grows after you activate the series and recipients are queued. If you see 0 matched but you know sign-ups happened today, work through the checklist below. The first two examples are real production patterns.

Example: sign-ups in your app, but missing in Halo until first login

What we saw

New accounts showed up in the customer’s database and analytics on signup day, but Contacts in Halo stayed empty until those users opened the authenticated app days later (email verification pending, invite not accepted yet, etc.). A Created Account series with audience filters on Signed Up showed 0 matched for today’s sign-ups even though the product had created users that morning.

Root cause

The customer followed the widget quickstart: load the SDK and call identify() when the user is known on the dashboard. They did not call POST /api/sdk/users/identify from their signup API. Halo only creates a contact after identify (SDK or REST), Stripe sync, or an integration import. A row in the customer’s Postgres (or similar) does not sync by itself.

Fix

  1. In your signup handler: Call server-side users/identify (and companies/identify when applicable) in the same request that creates the user and team. Pass signed_up_at from your product’s users.created_at.
  2. Keep widget identify on login so traits refresh and sessions link correctly.
  3. Backfill accounts created during the gap before deploy.
  4. Re-check the series matched count after identifies land.
See Identify Users → Authenticated apps with delayed first login.

Example: sign-ups today, but 0 matched (missing role on identify)

What we saw

A Created Account series used:
SettingValue
TriggerMatches the audience filter (segment_match)
Contact typeUsers only
AudienceFilter by criteria
FiltersRole is owner AND Signed Up on or after 2026-05-27
The builder showed 0 matched. The team expected new account creators from that day to appear.

What the data showed

Halo did have new users for that org on May 27, 2026:
Contactsigned_up_atcustom_fields.role
[email protected]Set (same day)(empty)
[email protected]Set (same day)(empty)
[email protected]Set (same day)(empty)
[email protected](empty)(empty)
So four new rows were created that day. Three satisfied the date filter. Zero satisfied Role is owner, because the product’s identify() call was not sending role at signup. Older contacts in the same org did have role: owner (from a later backfill or manual sync), but none of those also had signed_up_at on or after the filter date. The AND combination correctly evaluated to 0 matched.

Root cause

The Role audience field maps to custom:role, which reads end_users.custom_fields->>'role'. It is not Intercom’s intercom_role (user / lead). It is only populated when your app passes role in traits on identify:
ha.identify(userId, {
  email: user.email,
  name: user.name,
  signed_up_at: new Date().toISOString(),
  role: "owner", // required for "Role is owner" to match
});
Sign-ups were flowing in with signed_up_at, but without role, so the second filter excluded everyone.

Fix

  1. In your app: Pass role (and keep passing signed_up_at) on every identify at account creation, using the same string your filter expects (owner, admin, etc.).
  2. In Halo: Re-open the series rules panel and confirm the matched count updates (usually within a few seconds after the preview API runs).
  3. For contacts who already signed up without role: Backfill the trait with the Update / Backfill API or CSV import, both of which leave activity data untouched. Do not loop identify() to backfill, it bumps every user’s last_seen to today. See Importing & Backfilling Data. Temporarily removing the Role filter is an option if you need to include today’s sign-ups before the backfill runs.

How to verify before you activate

  1. Open Contacts and filter or search for a user who should match.
  2. Open their profile and check Role (under custom traits) and Signed Up.
  3. In the series builder, confirm the trigger node matched count matches your expectation.
  4. Click View who currently matches (or the equivalent audience preview) and spot-check a few emails.

Other common reasons for 0 matched

Contact does not exist yet (signup without server identify)

If you only identify in the browser after login, users who signed up but have not opened the app yet do not exist in Halo. They cannot match any audience filter and will not enroll in series until their first identify(). Fix with server-side identify at signup (see example above).

signed_up_at is empty

Audience filters on Signed Up read the signed_up_at column only. There is no fallback to created_at. Contacts created today via identify still won’t match a date filter if you never sent signed_up_at. See User Traits → How automations and segments read signed_up_at.

Wrong field for “role”

You filter onWhere the value must live
Role (custom:role)identify({ role: "..." })custom_fields.role
Intercom-style rolecustom:intercom_role (from Intercom sync; values like user, lead)
Filtering Role is owner will not match Intercom user / lead values.

Contact type mismatch

Users only excludes leads. If sign-ups are captured as leads first (widget form, no verified identify), they won’t appear in the matched count until promoted to contact_type = user.

Subscription list or suppression

With Subscription list as the audience source, opt-in lists with no subscribers return 0. The preview also subtracts bounced and unsubscribed addresses from the headline count.

Stripe or company filters resolve to nobody

Virtual fields such as stripe:subscription_status pre-resolve to user ID sets. If that set is empty, the whole audience can resolve to zero matches.

Series is still in draft

0 matched is about filters, not activation. But remember: no one enrolls until the series is active (except that you can still use the preview while drafting).

segment_match vs unsaved trigger

The matched count uses the saved audience filters on the automation row. If you changed the trigger in the UI but have not saved, the preview may not reflect what you see in the panel until you save.

Matched vs enrolled

TermMeaning
MatchedContacts who pass audience filters right now
EnrolledContacts who have been queued into the series at least once (lifetime, after activation)
A contact can be matched but not yet enrolled if the series is draft, or if enrollment gates block them (already active in the series, company priority, exit filter at enroll time, missing email for a required subscription, etc.).

Where to go next

Triggers & Conditions

Entry triggers, audience filters, and activation rules.

User Traits

role, signed_up_at, and what identify must send.