Creating an audience
Ten pathways from whatever you have to an interviewable audience.
Pick your pathway
Sorted by what you have in hand. Every pathway returns the same versioned, health-scorable Audience object.
| You have… | Use | Where the numbers come from |
|---|---|---|
| Anything / not sure | build_audience_auto | Routes to the best pathway, then health-checks the result |
| A plain-language description | build_audience_from_description | Model priors, normalized & validated in code |
| Raw pasted data (messy CSV, survey export, notes) | build_audience_from_raw | Frontier model parses rows; distributions computed in code over them |
| A past study or conversation | build_audience_from_study | Model reads the transcript, proposes the audience it implies |
| Connected Sources (Stripe, HubSpot, uploads) | build_audience_from_data | 100% code-computed distributions from real rows |
| A product idea | build_icp | Code-computed when anchored to customers; category-modeled otherwise |
| A best-customer cohort | build_lookalike | Code-computed similarity expansion |
| Data worth clustering | discover_segments | Code-computed clustering; LLM names the clusters (labels only) |
| A rare/executive segment | build_hard_to_reach | Category-modeled with calibration caveats built in |
| An exact spec | build_audience_manual | Your shares, validated in code |
The three NL pathways, in one minute each
1. From a description — fastest idea → interviews
{
"description": "US dog owners considering a smart collar: anxious first-time pet parents, multi-dog veterans, suburban families, and rural working-dog owners. Skew millennial, mix of pet-tech adopters and subscription skeptics.",
"category": "pet-tech",
"mustIncludeDimensions": ["monthly pet spend"]
}
# → { "ok": true, "data": {
# "audience": "aud_f1_9",
# "proposed": { "name": "US smart-collar consideration set", "dimensionCount": 5, ... },
# "validation": { "distributionsSumToOne": true, "warnings": [] },
# "honesty": { "sharesAre": "model-priors-normalized-in-code", "groundingStrength": "category-modeled" }
# } }The model proposes opinion-driving dimensions with stated share priors; code renormalizes, validates, and always includes an in-market status dimension.
2. From raw data — paste anything, skip ETL
{
"raw": "name,age,dog,spend\nJess, 34, golden doodle, ~$200/mo\nMike | 51 | two labs | $80\n- Sarah (28) beagle owner, premium food only, $310 monthly",
"hint": "customer interview screener notes, one person per line"
}
# → "parse": { "rowsParsed": 3, "columnsInferred": ["age", "dog", "spend"], ... },
# "honesty": { "sharesAre": "code-computed-over-model-parsed-rows" }Three different formats in one paste — the model normalizes them into rows, then the same code path as connected-data builds computes every distribution. No column mapping, no cleaning scripts.
3. From a study — follow-ups stop being one-offs
{
"studyId": "conv_abc123",
"perspective": "market" // or "participants" to reconstruct who was in the room
}Or just: auto
{
"description": "dog owners weighing a $99 GPS collar",
"raw": "…optional pasted data…"
}
# → "routing": { "pathway": "build_audience_from_raw",
# "reason": "raw data present — parsed rows give code-computed distributions" },
# "health": { "score": 0.78, "summary": "balanced composition; consider a region dimension" }Auto routes by evidence strength (raw > study > description), builds, then self-checks with audience_health and tells you if the result is weak.
The bright line, restated for audiences. Models may PROPOSE structure (which dimensions matter), STATE priors (flagged model-priors-normalized-in-code), and PARSE text into rows. Every share over real rows is computed in code; every proposal is validated in code; the response's honesty.sharesAre field tells you which regime you're in. Grounding strength never exceeds partially-grounded for model-parsed rows.
After you build: the quality loop for audiences
Health-check it
audience_health scores composition balance, dimension coverage, grounding strength, and freshness — with concrete actions.
Interview it
Run a quick focus group with gradeRealism: true. The realism judge tells you if the panel reads human.
Refine it
refine_audience adds/removes/reweights dimensions as a NEW immutable version — lineage preserved, nothing overwritten.
Versioning & provenance
Audiences are immutable: refining produces version N+1 with lineage.derivedFrom and parent ids intact. Every persona drawn from an audience carries the audience id + version + seed in its own id — any transcript can be traced back to the exact audience version that produced it.