Test (studies, claims)
Testing tools.
The Test group is where the bright line bites hardest: numbers come from fielding actual persona responses, never from a model guessing a percentage.
| Tool | What it tests |
|---|---|
test_concept | A product/feature concept. |
test_message / compare_messages | One message, or A/B/n across messages. |
test_claims | Specific claims for believability/appeal. |
test_pricing | Willingness-to-pay / price structures. |
test_positioning / test_naming | Positioning statements, names. |
test_creative / test_ad_placement / test_geography | Creative, placement, geo. |
run_study / run_grounded_study | A full multi-question study (grounded = evidence-anchored). |
field_with_variance | Field with realistic response variance. |
compute_stats / synthesize_insight | Statistics + insight synthesis over results. |
run_parity_check / get_parity_history / validate_with_humans | Calibrate synthetic vs. human ground truth. |
map_jtbd / map_objections / find_feature_gaps / reconcile_advocacy | Qual synthesis (jobs, objections, gaps). |
Calibration matters. run_parity_check and validate_with_humans let you measure how closely your synthetic audience tracks real respondents, so you know how much to trust a result. Always sanity-check a synthetic study against known ground truth before betting on it.
Large studies — run them async
A study with n ≥ 50 fields many live model calls in one go (~200s+), which is close to the request ceiling. For big runs, use the durable async queue: enqueue and poll instead of blocking.
# 1) enqueue — returns immediately with a jobId
POST /api/v1/studies/async
{ "audienceId": "aud_f1_1", "questions": [ ... ], "n": 400, "seed": 42 }
# -> { "jobId": "job_...", "status": "QUEUED", "poll": "/api/v1/studies/async?jobId=job_..." }
# 2) poll until DONE or FAILED
GET /api/v1/studies/async?jobId=job_...
# -> { "status": "RUNNING" } (keep polling)
# -> { "status": "DONE", "studyId": "...", "result": { aggregates, insight, confidence } }A background worker (cron-driven, plus an immediate kick on enqueue) claims the job, runs the full sample → field → compute → synthesize pipeline out-of-band, and writes the result back. Jobs are crash-safe (a stalled job is reclaimed) and retried up to 3 times. The resulting studyId feeds straight into compare_studies / detect_drift / synthesize_insight. Small studies (n < 50) can still use synchronous run_study.