intermediate
segments
audiences
targeting

Audience Segments

Build audience segments against live fleet data - filter by rides, wallet, subscription, score, language, and more.

Levy Fleets TeamMay 18, 202612 min read

Audience Segments

A segment is a saved filter over your riders. Engage evaluates every segment against your live database, so the count you see is the count that will receive your next send.

Navigation

Access segments from Engage > Segments.

How Segmentation Works

Each segment definition is a tree of AND / OR / NOT groups containing rules. The Engage backend compiles that tree into a parameterized SELECT against customers joined to per-customer rollup views (engage_ride_agg, engage_wallet_agg, engage_subscription_agg, etc.) and runs it through the engage_run_segment_sql RPC.

The RPC enforces a SELECT-only whitelist and bans semicolons, so a segment definition can never run an arbitrary write. Service role gates the call.

Available Attributes

SourceAttributes
customerscreated_at, language, city, country, timezone, marketing_email_consent, marketing_sms_consent, marketing_push_consent, push_token, rider_score, rider_score_tier
rides (rollup)first_ride_at, last_ride_at, total_count, total_revenue, force_ended_count
wallet_transactions (rollup)current_balance, lifetime_topup, last_topup_at, auto_topup_enabled
subscriptions (rollup)active_plan, status, next_renewal_at, churned_at
violations (rollup)total_count, last_violation_at, severity_max
referrals, loyaltycounts, tier, points balance

The rollup views are evaluated at query time, not pre-aggregated. There is no staleness window.

Operators

OperatorWorks onExample
equalstext, number, booleanlanguage = "es"
not_equalstext, number, booleancountry != "US"
greater_thannumber, daterides.total_count > 5
less_thannumber, datewallet.current_balance < 3
within_lastdatelast_ride_at within last 30 days
more_than_agodatelast_ride_at more than 60 days ago
is_set / is_nullanypush_token is_set
intextlanguage in ("en", "es")

Boolean Composition

Group rules with AND, OR, NOT. Groups can nest up to 4 levels deep (a validation cap that catches most accidental builder loops without hampering real segments).

Example - Re-Engagement Candidates

AND
├── rides.last_at more than 60 days ago
├── rides.last_at within last 180 days
├── marketing_email_consent = true
└── NOT
    └── violations.severity_max &gt;= "high"

That segment finds riders who used to be active, are not yet stale, can be emailed, and have no serious safety issues.

Live Count

Every segment shows a live count that is cached for 60 seconds.

  1. Build your rules.
  2. Click Preview.
  3. The count appears with a sample of 10 matching rows for sanity-checking.

The 60-second cache means refreshing the page right after a save shows the same count - that is intentional, not stale data.

Saving and Reusing

Saved segments live on your subaccount and are RLS-scoped - no other operator can see them.

  1. Click Save.
  2. Name the segment (Active 30d, High-Value Lapsed, etc.).
  3. The segment now appears in the Audience dropdown of the campaign composer and journey builder.

You can edit a saved segment at any time. Editing does not affect campaigns already in flight - those use the audience that was compiled at send time.

CSV Export

To pull a segment out for external analysis, click Export CSV on the segment detail page. The CSV streams from the same compiled SQL.

Ad-Hoc Segments

You do not have to save every segment. The campaign composer accepts an inline filter for one-off sends:

  1. In the campaign composer, choose Audience > Custom Filter.
  2. Build the rules.
  3. Engage will use them for this send only.

This is the right move for promo blasts that target a unique cut.

Build these first - they cover 80% of common sends:

SegmentDefinition
Active 30dlast_ride_at within last 30 days
Lapsed 30-60dlast_ride_at between 30 and 60 days ago
Lapsed 60-90dlast_ride_at between 60 and 90 days ago
Deep Lapsed 90d+last_ride_at more than 90 days ago
Low Balance, No Auto Top-Upwallet.current_balance < 3 AND wallet.auto_topup_enabled = false
New Signups, No Ride Yetcreated_at within last 14 days AND rides.total_count = 0
Top 10% by Ride Countrides.total_count &gt;= 50 (adjust to match your top 10%)
Spanish Speakers Activelanguage = "es" AND last_ride_at within last 60 days
Subscription Renewal Next 7dsubscription.next_renewal_at within next 7 days AND subscription.status = "active"

Performance and Limits

  • Segment SQL is parameterized - no string interpolation, no injection surface.
  • Validation caps nesting depth at 4 to prevent runaway compiles.
  • Counts are cached 60 seconds per segment.
  • Large segments (>100k riders) compile fine, but sends are subject to rate limits.

Best Practices

  • Always preview before saving - misnamed columns and typos show up in the count.
  • Layer consent into every segment - if you are sending email, include marketing_email_consent = true. The dispatcher will still suppress non-consenting riders, but a clean segment count avoids confusion.
  • Use named segments for recurring sends. Ad-hoc filters are fine for one-off blasts but get unwieldy if you reuse them.
  • Watch your active rate. If a segment is too narrow, you will undercount the audience and underestimate impact. If it is too broad, you risk over-mailing.

Troubleshooting

Segment count is zero

  • Did you check both marketing_*_consent flags and the rollup view filters?
  • New customer columns (e.g., rider_score_tier) are nullable - a NOT NULL style rule on them will exclude everyone without a score.

Count looks too high

  • The rollup views ignore deleted customers via is_deleted = false. Check whether you are inadvertently including test or staff accounts.

Edit broke a live campaign

  • It did not. Campaigns snapshot their audience at compile time. Edits to the segment take effect on the next send.

Need Help?

For segmentation help, contact support@levyelectric.com.