Gather Launch takes a brief for a new brand test (like schol or Pinch) and runs it end to end: domain, concept pages, waitlist and Meta sign-up tracking, ads, campaigns, 2-hourly reports, auto-pause and the end-of-test report.
It does about 80% of the work itself. The rest needs a person (creating the Facebook Page, approving the budget, ticking Meta's AI info box), so it queues those tasks for Delbert and carries on when he marks them done.
You send a brief. The service checks it before anything is built. If something is missing or unclear you get questions back; answer them and it checks again. Once the brief is clear it is locked and queued. Nothing is bought, published or spent until Delbert approves the launch.
Get an API key
Sign in at https://gather-launch.vercel.app/keys with your @gatherat.ai or @rockstar-automations.com Google account.
Create a key with a label saying who it is for (e.g. emily, or emily-claude for a Claude session).
Copy it straight away. It is shown once and stored only as a hash.
Put it in a gitignored .env.local as GATHER_LAUNCH_KEY. Never commit it or paste it into a chat.
Send it on every request as Authorization: Bearer glk_....
The reply is always 202 with the launch id and one of three statuses: needs_clarification (with questions), ready (the brief is locked and queued), or checking (the checker is retrying; poll the launch in a minute).
The brief
Field
Type
Rules
brand
string
The brand name used on pages, ads and the Facebook Page.
domain_ideas
string[]
1 to 6 domains like getschol.com. The worker buys the first available one, after Delbert approves.
market
string
Two-letter code. Supported: IN (India), US (United States).
concepts
object[]
1 to 4 concepts. Each one gets its own page, ads and ad set, so they can be compared.
concepts[].slug
string
Lower-case letters, digits and dashes. Becomes the page path, e.g. /housing.
concepts[].problem
string
Who has what problem, in what situation. One sentence.
concepts[].promise
string
What the product does for them, concretely. No guarantees of outcomes or deadlines.
concepts[].proof
{text, source}
One real stat or quote, and where it comes from (a link, or a named survey with size and date).
concepts[].category
string, optional
Regulated area: none, housing, finance, immigration, health, employment. If you leave it out, the checker guesses and asks you to confirm.
budgetperday_usd
number
Total daily ad budget, 5 to 200. Split evenly across concepts and set as a hard Meta spend cap.
test_days
integer
3 to 21. Every campaign is paused at the end.
reviewers
string[]
Who signs off the copy before launch, not counting the owner (e.g. Abhi, Emily).
owner
string
Work email of the person who owns the test and gets the report.
notes
string, optional
Anything else the worker or Delbert should know.
site
object, optional
Who builds the pages. Leave it out and we build them on the shared concept site. { "mode": "own", "urls": { "<concept-slug>": "https://..." } } if the teammate builds their own (see Bring your own site). With your own site, domain_ideas can be left out.
A full example (this brief passes every check as written):
{
"brand": "schol",
"domain_ideas": ["getschol.com", "schol.app"],
"market": "IN",
"concepts": [
{
"slug": "early-number",
"problem": "Indian students flying to the US for a master's can't get a US phone number until they land, so banks and landlords can't reach them.",
"promise": "A US number that works in India before you fly, set up in two minutes.",
"proof": { "text": "61% of students we surveyed missed a call from a US bank or landlord in their first week.", "source": "Schol survey of 212 Indian students, October 2026" },
"category": "none"
},
{
"slug": "housing",
"problem": "Indian students arriving in the US have no credit history or SSN, so landlords turn down their rental applications.",
"promise": "We help you put together a complete US rental application before you land.",
"proof": { "text": "58% of students we surveyed were asked for a US credit score they did not have.", "source": "Schol survey of 212 Indian students, October 2026" },
"category": "housing"
}
],
"budget_per_day_usd": 40,
"test_days": 7,
"reviewers": ["Abhi", "Emily"],
"owner": "delbert@gatherat.ai",
"notes": "Same audience as the October schol test."
}
A brief that can't be read at all (wrong types, no concepts, more than four, bad or duplicate slugs) gets a 400 with an issues list instead of questions. Fix those and send it again.
What "clear" means
Every brief goes through three layers before the Mac worker can see it.
Rule checks (free, instant): required fields, 1 to 4 concepts, budget and test length within limits, supported market, reviewers named, owner on a team domain, the proof has a source, no guarantee words ("guaranteed", "within 15 days", "100%"), and at least one domain idea is available.
Judge (Jev, a fast calibrated classifier): for each concept it asks whether the problem is specific, whether the promise is concrete, whether the proof is a real sourced stat or quote, whether the copy makes a guarantee, whether it names a real company, and whether it is in a regulated area (housing, finance, immigration, health, employment). Jev sits on top of the rules and is never the only gate. If it is unsure, that counts as unclear, never as passed.
Questions: each failed or unclear check becomes a pre-written question. The judge never writes copy for you.
The brief is clear when every check passes or you have answered "keep it" to the ones that allow it. Rewriting a field clears any "keep it" on that field, so the new text is checked again.
The clarification loop
A needs_clarification reply looks like this:
{
"id": "jd78e9360v8vt4k1kmrnyqyakn8gagqm",
"status": "needs_clarification",
"questions": [
{
"id": "housing.promise.guarantee",
"field": "concepts[1].promise",
"question": "Concept \"housing\": this promise makes a guarantee (\"within 15 days\"): \"We get your lease approved, completed within 15 days.\". Rewrite it without promising an outcome or a deadline.",
"why": "We can't keep guarantees in a fake-door test, and Meta rejects ads that over-promise."
},
{
"id": "housing.category",
"field": "concepts[1].category",
"question": "Concept \"housing\": which regulated area is this in, if any? Our best guess is \"housing\".",
"options": ["none", "housing", "finance", "immigration", "health", "employment"],
"why": "Housing, finance and employment need a Meta special ad category and their own campaign; immigration and health need extra care in the copy."
}
]
}
id is stable. Use it as the key in your answer.
field is the part of the brief the answer replaces.
options, when present, lists the accepted answers. Questions with a single option such as "Keep it as written" or "It's fictional, keep it" accept either that option or a rewrite.
Questions without options take free text, which replaces the field. Lists (domain_ideas, reviewers) are comma separated.
Answer any or all of them:
curl -s -X POST https://gather-launch.vercel.app/api/launches/<id>/answers \
-H "Authorization: Bearer $GATHER_LAUNCH_KEY" \
-H "Content-Type: application/json" \
--data '{ "answers": { "housing.promise.guarantee": "We help you put together a complete US rental application before you land.", "housing.category": "housing" } }'
The reply has the same shape: more questions, or ready. If an answer couldn't be used it is listed in answer_errors (unknown id, not one of the options, not a number) and the question is asked again. When two questions point at the same field, the first rewrite wins and the new text is checked again.
Returns status, the brief, open questions, the worker's steps, waiting_on_delbert (open human tasks), results (domain, pixel, campaign ids, report) and recent history. People can see the same thing at https://gather-launch.vercel.app/launches/<id>. Both work when Delbert's Mac is off.
Status
Meaning
checking
The brief is being checked, or the judge was unreachable and is being retried (30 s, 2 min, 10 min).
needs_clarification
Questions are waiting for you.
ready
All clear. The brief is locked and can't change.
queued
Waiting for the Mac worker. A brief sent while the Mac sleeps waits here and is never lost.
running
The worker is building: domain, pages, tracking, creative, paused campaigns.
waitingondelbert
Blocked on a task only a person can do. See waiting_on_delbert.
awaitinglaunchapproval
Everything is built and paused. Delbert approves the budget and copy before anything goes live.
live
Ads are running. Reports every 2 hours; auto-pause at the budget cap and at the end of the test.
done
The test ended and the report names the winning concept.
failed
Stopped. fail_reason says why.
cancelled
Cancelled from the status page.
What happens after ready
Domain: bought and pointed at the shared concept site (after approval).
Concept pages: one page per concept on the shared multi-brand site, with the waitlist sheet.
Tracking: Meta pixel created through the ad account, server-side Lead events, and a real end-to-end test sign-up that never counts as a Lead (then deleted).
Creative: static and video ads rendered from the concept copy, using fictional names only.
Campaigns: created PAUSED, one ad set per concept, Instagram identity on every ad. Regulated concepts (housing, finance, employment) get their own special-category campaign.
Copy sign-off by the reviewers before launch, because ad edits after launch go back through Meta review.
Launch approval by Delbert, then live, 2-hourly reports to the Gather Dashboard, auto-pause, and the end-of-test report. Concepts are judged on cost per lead using our own sign-up count, not CTR.
Tasks only a person can do are queued for Delbert at /tasks, with a notification in Claude Code: create the Facebook Page and Instagram account, confirm Meta domain verification, tick AI info on each ad, CAPTCHAs, a card on a new ad account, and the judgment calls (copy sign-off, budget, regulated areas, picking the winner).
Bring your own site
Teammates can build the concept pages themselves, on any stack. gather-launch still does the counting and the Meta Leads, so nobody handles a Meta token and the numbers match every other launch.
In the brief, set site.mode to own and give one https URL per concept in site.urls, all on one domain. Pages can be unpublished when you send the brief.
Add the script tag below to every concept page, with data-brand set to the brand slug (the brand name in lower case with dashes, e.g. Rentwise becomes rentwise). The status page shows the exact tag once the brief is ready.
Mark the sign-up form with data-gather-lead, or call GatherLaunch.lead({ email, phone }) after your own submit succeeds. Email, phone or both works, so phone-only forms are fine. Mark buttons that open the sign-up with data-gather-cta.
Don't add your own Meta Pixel or Conversions API code; the script loads the brand's Pixel and the server sends the Lead.
Before launch the worker opens every URL in a real browser, checks the script and the concept, and submits a test sign-up through your form. If something is wrong, the launch's steps say exactly what ("the gather-launch script isn't on the page") and it re-checks every 10 minutes.
Meta domain verification uses a DNS TXT record. If your domain isn't in our Vercel account you'll be asked to add it.
<!-- In <head> of every concept page -->
<script src="https://gather-launch.vercel.app/t.js" data-brand="<brand-slug>" defer></script>
<!-- The sign-up form: mark it and keep normal email/phone inputs -->
<form data-gather-lead>
<input type="email" name="email" required>
<input type="tel" name="phone"> <!-- optional -->
<button type="submit">Join the waitlist</button>
</form>
<button data-gather-cta="hero">Join the waitlist</button>
<!-- Phone-only forms work too: just the type="tel" input -->
<!-- Or from your own code, after your submit succeeds -->
<script>
await GatherLaunch.lead({ email, phone }); // resolves {ok, eventId}
</script>
Guidance and playbooks
Every response carries a guidance block: the rules behind each open question, and once the brief is ready the playbooks for what comes next (copy rules, and for your own site the site and tracking rules). Each rule says why, from what we learned on Pinch and schol.
The full playbooks need no key: GET https://gather-launch.vercel.app/api/playbooks lists them, GET https://gather-launch.vercel.app/api/playbooks/<topic> returns one, /api/playbooks/all returns everything. Read them before building pages or creative.
Writing the brief (brief). A brand test compares 1 to 4 concepts. Each concept is one problem, one promise and one piece of real proof.
Name who has the problem and the situation they're in, in their own words. Why: Specific problems get cheaper sign-ups, and a vague one tells us nothing about which concept won.
The promise says what the product does, concretely enough to picture. Why: A fake-door test only measures demand if people know what they are signing up for.
Proof is a real number or a direct quote, with a source someone could check (a link, or our survey with size and date). Never invent it; leave it empty and say so if there isn't any yet. Why: Made-up proof misleads the people who sign up and is a Meta policy risk.
Concepts should differ in the problem or promise, not in wording. If two concepts would get the same ad, merge them. Why: We judge on cost per lead per concept; near-duplicates split the budget without teaching us anything.
Budget is split evenly across concepts. Plan at least $15 a day per concept in the US ($10 in India) for 3 to 5 days. With less, run fewer concepts. Why: On Pinch, $5 a day for one concept got 0 leads in 5 days; concepts that got a read ran $15 to $25 a day. Schol got reads at $10 a day in India, where clicks are cheaper.
Give each concept (and each ad) a different angle, not just a different hook. Note who it speaks to (themselves now, who they want to be, someone they look after, or plain curiosity), whether it's negative or positive, and how intense. New tests go in combinations not tried yet. Why: Meta treats ads with the same emotional premise as one ad and funds only one of them. Pinch's next tests filled its empty cells: duty ("I fixed my mom's bill") and calm, low intensity.
A curiosity question about the reader's own situation is a strong hook ("Is there money in your name?"). Why: It is Pinch's best concept so far: $1.75 per lead at 9.4% CTR. Generic bill pain lagged at about $26 per lead.
Copy rules (pages and ads) (copy). Honest, specific, and safe for Meta review. These apply to every line a visitor or reviewer sees.
Never guarantee an outcome or a deadline: no "guaranteed", "within 15 days", "100%", "never miss". Why: We can't keep guarantees in a fake-door test, and Meta rejects ads that over-promise. Schol's "completed within 15 days" had to be cut.
Never name a real company, landlord, bank, university or person. Use generic descriptions ("your landlord") and fictional first names in example chats. The one exception is a real, sourced customer result about a company the customer pays ("DIRECTV: $55 a month off"), worded so it never implies a partnership. Why: Schol's draft named a real landlord (Avalon): a legal and Meta policy risk. Pinch's real DIRECTV result was its best hook, so a true result is worth the care.
Lead the ad and the page with the most concrete real result you have, with its number. Prices and savings come only from real results, never invented. Why: Pinch's "DIRECTV: $55 a month off" got 8.0% CTR and 20 leads on $33; the generic version in the same ad set got 4.6% CTR and 9 leads on $64.
Keep pages short: the result, what it does in one line, the sign-up. Cut anything a visitor has to read twice. Why: Delbert cut Pinch's landing copy by 80% (17 Oct). About 25% of landing taps signed up, flat across concepts, so the page mostly has to not get in the way.
Spend effort on the ad's hook before the page. Why: On Pinch the page converted about the same for every concept; cost per lead differences came from CTR and cost per click.
Say "coming soon" or "join the waitlist". Never imply the product exists today. Why: It's a fake-door test: honesty about the waitlist keeps trust and keeps the sign-up meaningful.
Each page says one thing the product will NOT do ("Not doing: legal advice"). Why: It sets expectations and reads as more credible than pure promise.
Any illustrative chat, number or scenario is labelled "Example" (or "Dramatization" in video). Why: Meta and our own standard: nothing fictional is presented as real.
Reviewers sign off copy BEFORE launch. Why: Any ad edit after launch goes back through Meta review and resets delivery.
Write plainly, short sentences, no em dashes in anything sent as Delbert or the brand. Why: House style.
Regulated areas (regulated). Housing, finance and employment are Meta special ad categories; immigration and health are sensitive.
Housing, credit/finance and employment concepts go in their own campaign with Meta's special ad category set, even outside the US. Why: Meta now applies HOUSING in India too; mixing it into a normal campaign gets ads rejected or the account restricted.
Treat card fees, unclaimed money, and getting a lower price or rate on a bill or subscription as finance: its own campaign with the financial special category. In normal campaigns avoid "price" and "rate" claims ("loyalty rate", "New York Times price"); "asks for a lower price" passed. Why: Meta rejected Pinch's subscription ads as "Financial and insurance products" even with no dollar amounts. The category applies to a whole campaign, so these concepts can't share one with normal concepts.
Housing copy never says who is or isn't accepted as a tenant. Why: Fair-housing rules behind the special category.
Immigration and paperwork concepts carry "Not legal or immigration advice"; health concepts never name drugs, doses or prescription prices. Why: Schol's paperwork ads needed the disclaimer; Pinch's prescription ad needed LegitScript.
Special-category ad sets can't narrow by age or interests. Plan the test so it works with broad targeting. Why: Meta removes those options for special categories.
Tracking and Meta Leads (tracking). Count leads ourselves, send Meta a deduplicated Lead from browser and server, and never let a test count.
Judge concepts on cost per lead using OUR sign-up count, not Meta's Lead count and not CTR. Why: India broad targeting gives about 15% CTR at about $3.50 CPM, so CTR says little; Meta's count can include duplicates.
Fire the browser Pixel Lead and the server (CAPI) Lead with the same eventid. Why: Meta deduplicates on eventid; without it every sign-up counts twice.
Test with example.com addresses, +test aliases or +1 555 numbers. They're stored as tests and never sent to Meta as real Leads. Why: A test Lead teaches Meta's optimiser the wrong audience and inflates results.
Open your own pages with ?internal=1 once per browser. It sets a cookie that drops you from all counts. Why: Team visits otherwise inflate visitors and lower the lead rate.
Store tokens without surrounding quotes and check a real end-to-end Lead ("sent") before launch. Why: Schol's CAPI token was saved with literal quote marks and failed silently.
Phone-only sign-ups count the same as email ones: send whichever the form collects (email, phone or both). Why: SMS products like Pinch and Porchlight sign people up by phone; the server Lead sends Meta the hashed phone for matching.
Keep the fbclid from the ad click as the fbc cookie (fb.1.<ms>.<fbclid>) and pass it with the Lead, building it from ?fbclid when Meta's cookie isn't set yet. t.js does this. Why: It's the strongest match key for Meta's attribution._
If the sign-up opens another app (sms:, WhatsApp), test it inside the Facebook and Instagram in-app browsers on Android. Use sms:NUMBER?&body=, hop to Chrome with an intent:// link, and copy the message to the clipboard on tap. Why: On Pinch, 21% of Android taps texted from the in-app browser vs 35% from a real browser until the Chrome hop fixed it. A "stuck" check must treat blur or pagehide as success: the FB browser stays visible while Messages opens (75 false alarms on 76 taps).
Never put a tracking code in a message the person sends (a prefilled text or email). Join taps to messages by time and concept instead. Why: Delbert called a code in Pinch's prefilled text "hamfisted"; it reads as spam to the person sending it.
Every ad link carries utmsource=meta, utmmedium=paidsocial, utmcampaign=<brand>, utmcontent=<concept>. The concept is still decided by the page URL, not the UTM. Why: UTMs are an attribution check; routing decides the concept so a shared link can't mis-attribute._
Bringing your own site (own_site). Build the pages however you like (Next, Framer, Webflow, plain HTML). Add one script tag and mark the sign-up form; gather-launch does the counting and the Meta Leads.
One page (URL) per concept, given in the brief's site.urls. No links between concept pages. Why: Sign-ups stay attributable to the concept the visitor landed on.
Add the gather-launch script tag to every concept page, in <head> or before </body>, with data-brand set to the brand slug. Why: It sets the visitor cookies, keeps first-touch UTMs, loads the brand's Pixel and records page views, so your numbers match the dashboard standard.
Mark the sign-up form with data-gather-lead (on submit it reads inputs with type=email or type=tel, or a name containing email or phone), or call GatherLaunch.lead({ email, phone }) after your own submit succeeds. Email, phone or both is fine. Why: That one call stores the lead, fires the browser Lead and the server Lead with one eventid, and skips tests._
Don't add your own Meta Pixel or CAPI code. The script loads the brand's Pixel. Why: Two pixels double-count PageViews and break deduplication.
Mark buttons that open the sign-up with data-gather-cta="<id>". Why: CTA clicks show where a page loses people before the form.
Ask for email first and keep anything else optional on a second step. Why: Schol's email-first sheet kept the form short; extra questions after the sign-up still get answered.
Meta domain verification uses a DNS TXT record on your domain. If the domain isn't in our Vercel account, you'll get a task with the exact record to add. Why: A script can't add the meta tag Meta's crawler needs to see in the HTML.
Link a privacy page and terms in the footer, naming Rockstar Automations Ltd, delbert@gatherat.ai, and Meta ad measurement with hashed identifiers. Why: Meta and app stores check for them; Pinch's legal pages are the template.
Before launch the worker opens each URL, checks the script and form, and submits a test sign-up. Fix anything it reports and it re-checks. Why: We prove tracking works end to end without needing access to your code.
Ad creative (creative). Static ads in 4:5 and 9:16 per concept; video ads in their own ad sets with their own budget.
Use fictional names and generic places in creative; show the real, sourced stat as the proof. Why: Same honesty rules as the copy; a real stat is more convincing than an invented quote anyway.
Tick "AI info" on every ad and every Reel, and again after any image or video swap. Why: Meta requires it and there's no API for it.
Videos go in their own ad set (or campaign) with their own budget. Never mix them with statics, and never add them later to ad sets that already ran. Why: Meta puts almost all of an ad set's money behind one ad, usually a still: on Pinch one still took $63.66 of $66.59 and another $89.21 of $89.76, and the video with the best CTR got about $5 a day. Schol's videos added later got $0.
Make UGC video with Flux 3 Max on fal (blackforestlabs/flux-3-max/text-to-video, queue API): duration as a whole number (18, max 20), aspectratio 9:16, resolution 720p, generateaudio true. About $0.38 per 18 s take. Order 2 takes per concept. Why: One continuous take with native speech keeps the same actor throughout; MiniMax H3 makes 10 s clips and the actor changed between them. Delbert rated Flux significantly better. About 1 in 4 takes is unusable.
Prompt it as raw phone footage: "A vertical 9:16 iPhone video filmed by his wife, one continuous take, no cuts." Then the person, place and light, the exact spoken lines in quotes, and "Natural room sound, no music, no captions, no on-screen text. Raw, candid iPhone UGC." For a listening beat, write "for about ten seconds says nothing at all and just listens with his mouth closed". Why: It reads as real UGC, and the silent beat leaves a clean window (about 8 s on Pinch) to lay the product's voice over.
Captions are small and low, above the Reels bottom bar, and every line reads with the sound off. Caption from the script, not from speech-to-text, and have a person listen once. Why: Delbert: bold captions "cover the whole thing". Whisper misheard money ("for a year" came back as "four years").
Burn "Dramatization with an AI actor." into every AI video; add "Based on a real user's result" only when it is one. Never generate product UI: render the real screens and cut them in. Why: Honesty standard, and a generated UI shows a product that doesn't exist.
Make the product the other character in the scene (its voice on the call, its text arriving), not a feature demo. Show it early, not at 15 s. Why: The finding from the LightReel UGC study that held on Pinch. Long 30 to 40 s formats don't transfer to paid.
If a voice model mispronounces a word, change the word; re-rolling won't fix it. Why: Learned on Pinch's UGC takes.
Never commit media files; publish them with the video tooling. Why: Keeps repos small and avoids leaking drafts.
Campaigns (campaigns). Built PAUSED with a hard spend cap; launched only on Delbert's approval.
Everything is created PAUSED. Only Delbert approves going live. Why: Nothing spends until the budget and copy are signed off.
Every campaign has a hard Meta spend cap equal to its budget for the whole test. Why: If the Mac running the automation is off, Meta itself still stops the spend.
One ad set per concept with its own daily budget (not campaign budget sharing). Why: Equal spend per concept makes the cost-per-lead comparison fair. Pinch moved to this on 17 Oct.
Every ad carries the brand's Instagram account (instagramuserid), not just the Page. Why: Without it Instagram placements show the Page name or don't deliver.
Expect spend to follow the market's daytime; don't judge a concept on its first few hours. Why: India spend clustered in IST daytime on schol.
Nothing pauses or scales automatically on early numbers. The worker reports and flags; Delbert decides kills and scales. The hard spend cap and the test's end date are the only automatic stops. Why: Delbert's standing rule: it's too early to call ads on early numbers.
Read a concept after about $20 spent per ad or 7 days, against that concept's own benchmark, not the account average. CTR above about 8% in the first 48 hours is a good early sign, once there are a few hundred impressions. Why: Pinch's concepts differ by more than 10x in cost per lead ($1.75 vs $26), so one account-wide bar would kill good concepts in expensive categories.
Expect Meta to throttle after about 20 creates in a row (code 17, subcode 2446079). Keep your own record of every ID you create and read objects by ID. The worker resumes from its record 30 minutes later. Why: Pinch hit it building ad sets, and it cleared within an hour. On the development tier the account list endpoints are throttled too.
Record every asset, the idea it tests and every change to it in one place, before launch. Why: Without it, a later read can't tell which change moved the numbers. Pinch logs adAssets, adTests and adChanges in Convex.
Reporting (reporting). Every 2 hours to the Gather Dashboard, using the standard metric names.
Use the dashboard's canonical metric names (spendusd, leads, leadsmeta, sitevisitors, costperleadusd, leadrate...). Why: Aliases are rejected and boards stay comparable across tests._
Never send emails, phones, visitor ids, fbc/fbp, IPs or user agents to the dashboard. Why: The dashboard rejects them, and it's shared more widely than our database.
Report cost per lead on our own sign-up count. Show Meta's count next to it, never instead of it. Why: On Pinch, Meta's pixel said 121 leads when 133 people had signed up.
sitevisitors are distinct visitor cookies with a page view, excluding bots and internal traffic; brand totals are distinct across concepts, not a sum. Why: The tracking standard agreed with the dashboard, so every launch counts the same way._
Errors
Code
Meaning
400
The brief or the answers body can't be read. issues or error says what to fix.
401
Missing, unknown or revoked API key.
404
No launch with that id.
409
The launch isn't waiting for answers (it is already locked, or still checking).
413
Body over 64 KB.
For Claude sessions calling this API
If you are a Claude session launching a test for someone, follow this loop. The person you are working with is the source of truth for the copy; you are the go-between.
Read the key from GATHER_LAUNCH_KEY in the environment or a gitignored .env.local. Never print it, log it, or put it in a commit, a brief or a chat message.
Build the brief from what the person told you. Don't invent proof: if they gave no real stat or quote with a source, leave proof empty and let the question ask for it.
POST it. If you get 400, fix the issues yourself (they are structural) and resend.
On needs_clarification, show every question to the person, in plain words, with the why. Don't answer copy questions on their behalf, and never pick "keep it" or "It's fictional, keep it" unless they said so. You may answer purely factual ones you already know from the conversation (market, budget, reviewers, owner).
Send their answers keyed by question id. Repeat until the status is ready. Retry answer_errors with a corrected answer.
On checking, wait about a minute and GET the launch; don't resubmit the brief (that creates a second launch).
Read and apply the guidance in each response, and GET /api/playbooks/all before you build pages, ads or tracking for a launch. Explain a rule to the person when it changes what they asked for.
If the person is building their own site, set site.mode to own, add the script tag and data-gather-lead from the guidance, and watch the launch's steps for anything the site check reports.
Once ready, give the person the status page link and stop. You can't approve, launch or spend; only Delbert can, from his own queue.
Use the same launch id for the whole conversation. One brief per brand test.