Three ways to materialize an audience at send time
Building an audience is one thing; getting it into a sendable shape is another. Orbit ships three materialization paths that sit between “I have an audience in mind” and “the campaign has a target,” and they solve different problems:- LLM-suggested segment — you don’t yet know which audiences are worth building. The suggester reads your recent CDP signal and proposes high-value segments as validated filter definitions.
- Click-cohort retarget — you know exactly whom you want: the contacts who clicked a specific link in a prior campaign. The cohort resolver turns that click set into a static, retargetable segment.
- On-the-fly CSV export — you have a filter definition but don’t want to persist it as a segment. The export resolves the audience live and hands you the matched contacts as a CSV download.
Section 1 — LLM-suggested segment
POST /api/v1/segments/auto-suggest proposes segments you have not
created yet. It takes no audience description — instead it reads the
org’s last-30-day CDP event volume, the top event names, the top contact
tags, and the predictive segment-label histogram, and asks the LLM to
propose up to five high-value segment definitions given that signal. An
optional focus_hint (up to 500 characters, e.g. “focus on retention”)
steers the suggestions.
Every suggestion is returned as four parts: a name, a one-sentence
description framed at the marketer, a rationale explaining why the
segment is high value for this org’s event mix, and a filters filter
AST. Before a suggestion reaches you it is dry-run against the
segmentation engine on your tenant’s data: suggestions that reference an
unknown field, use a mismatched operator, or blow up in the engine are
dropped, and the survivors arrive with a preview_match_count. The
response is therefore “trust everything we returned” — the dashboard’s
empty-state on the segments page calls this endpoint to render “Try one of
these” cards.
Suggestions are not persisted. Clicking one pre-fills the
create-segment dialog; the segment row exists only after you confirm the
creation. When the org has no useful signal (zero contacts, or zero events
in the last 30 days — including a brand-new tenant whose CDP tables have
not finished provisioning), the endpoint returns an empty suggestion list
with a note instead of an error.
How this differs from the autopilot endpoint
There are two LLM segment endpoints and they are easy to confuse:POST /api/v1/contacts/segments/autopilottakes a user-supplied natural-language description (“everyone who clicked the pricing page but didn’t book a demo”) and translates it into a segment definition plus a match-count preview on one round-trip. You bring the idea; the LLM compiles it.POST /api/v1/segments/auto-suggesttakes no description at all. The org’s own event mix supplies the raw material; the LLM supplies the ideas. Use it for discovery — the “what should I even build?” question — and use autopilot when you already know the audience you want.
Section 2 — Click-cohort resolution
POST /api/v1/segments/from-clicks closes the click→contact loop. Orbit
already shortens links, records clicks, and reports aggregate click
counts — but “how many clicked” never answered “which contacts clicked.”
This endpoint resolves that.
Scope the cohort one of two ways:
link_id— every recipient who clicked that one tracked short link.campaign_id— recipients who clicked any tracked link in the campaign.window_days(optional, 1–365) — only count clicks within the last N days, for recency-scoped re-engagement. Omit it to consider all-time clicks.
422 EMPTY_COHORT and explains that no cohort
exists to materialize.
The result is a static contact segment (no rules, auto_refresh = false) materialized through the same path any other segment uses — so
campaigns, exports, and audience activation all key on its segment_id
with no special handling. Click engagement is an event, not a live trait:
the cohort is a point-in-time snapshot that stays stable for the
re-engagement blast instead of draining on the next refresh tick. To
capture a later window, generate a fresh cohort. Soft-deleted contacts are
excluded, membership is hard-capped so a click-heavy tenant cannot
materialize an unbounded audience, and the creation is audit-logged with
the scope (link or campaign), the window, and the member count. Naming is
deterministic when you don’t supply one — Clicked link <id> (last 30d) —
so the same scope always yields the same label.
Section 3 — On-the-fly CSV export
POST /api/v1/segments/export.csv exports an unsaved audience. It
takes the same filter AST the segment builder and the auto-suggest
endpoint speak, resolves it live against your contacts, and returns the
matched rows as an RFC-4180 CSV attachment — no segment row, no membership
materialization, no waiting on a refresh tick. (The saved-segment
equivalent, for audiences you did persist, is
GET /api/v1/contacts/segments/{id}/export.csv; the
CDP segments guide covers the saved flow.)
The request body is three fields: filters (the audience definition), an
optional limit below the hard cap for a quick sample download, and an
optional filename stem — sanitized so it can never break the download
headers. The response is the CSV plus, when the audience exceeded the cap,
an X-Export-Truncated: true header.
Guardrails on this path:
- Hard row cap of 50,000. The resolve is synchronous — the reply is one CSV string — so the cap also bounds worker memory. An “all contacts” filter can never resolve more than the ceiling; when the result is truncated, the header tells you and the truncation is audited. For larger audiences, persist the segment and use the streaming saved-export path.
- Role and scope gate. Exporting an audience is a bulk PII pull, so it
requires the owner, admin, or developer role and the
contacts:writescope — a viewer or acontacts:readkey cannot pull full contact rows. - PII visibility. Every row passes through the same masking rules the dashboard applies: phone and email honour the caller’s role and reveal context exactly as the contact list renders them for that caller. An export never shows more than the dashboard would.
- Rate limit. 5 exports per minute per tenant.
contact_id,
first_name, last_name, email, phone, country_code,
lifecycle_stage, tags, created_at.
Section 4 — Contrast and decision
Route the choice by what you already know:
- Don’t know which audiences are worth building? Start with auto-suggest and let your own event mix propose them.
- Know the audience in words? Use the autopilot endpoint
(
/contacts/segments/autopilot) to compile the description. - Know the audience as “everyone who clicked that link”? Build a click cohort — attribution is resolved for you.
- Need the audience outside Orbit, once, without saving anything? Export the CSV.
Section 5 — Egress gating and the audit trail
The CSV path is the only one of the three where contact data leaves the product, so it carries the strictest posture. The gate has three layers:- Actor gate. Owner, admin, or developer role with the
contacts:writescope. Lower-privileged actors are refused before any contact row is read. - Content gate. PII visibility is resolved per request and applied to every row, so the file can never contain more phone/email detail than the caller could see in the dashboard.
- Volume gate. The 50,000-row hard cap and the 5-per-minute rate limit bound how much a single actor can pull.
Section 6 — See also
- CDP identity resolution — how a tracked event becomes a contact the cohort resolver can attribute.
- CDP segment recompute model — the refresh lifecycle of the segments these flows optionally create.
- CDP clean-room model — the governed path for sharing audience data without raw egress.
- CDP segments guide — the how-to for building and saving the segments this page materializes.
- Consent and suppression model — the privacy governance layer above all three paths.