Focus Group
API

Focus Group for agents

One call puts a piece of copy in front of 48 simulated panelists and returns their scores, objections and revisions — including the one who disliked it. Three rails: REST with a key, MCP, and keyless x402.

These panelists are simulated by a language model. Nobody was surveyed, no real person is represented, and none of this is market research data. Every response carries synthetic: true for exactly this reason. Do not present this output as research.

Authentication

Three credentials work, and they differ only in what they can reach.

  • fcg_live_ — this app's own key, minted at Dashboard → API keys. Runs panels, reads them, exports them. Cannot pass brain_ids, because that reaches into your private Ounie account.
  • ounie_live_— your ounie.com developer key, the fleet MASTER key. Everything above, plus grounding a panel in your Ounie brains. Enable “Use across Ounie apps” on ounie.com first.
  • x402 — no account at all. Pay per call in USDC on Base. See below.
Authorization: Bearer fcg_live_…

# or, when your client cannot set headers:
https://focusgroup.ounie.com/api/panels?api_key=fcg_live_…

Every keyed call draws the key owner's shared Ounie credit wallet and is refused with 402 insufficient_credits when the balance is short — an agent can never overdraw.

Run a panel

POST/api/panels8 credits
FieldTypeNotes
artifactstringThe copy under test, 100–8000 chars. Required unless artifact_url is given.
artifact_urlstringA public http(s) page whose readable text becomes the artifact. Ignored when artifact is supplied.
icpstringWho the panel represents. Optional, but the sharper it is the sharper the objections.
focusenumclarity | objections | pricing | positioning | trust — default objections.
panel_sizeint4–8, default 6. Does not change the price.
brain_idsuuid[]Up to 5 Ounie brains to ground the panelists in. Needs the master key.
titlestringOptional name for the panel.
curl -X POST https://focusgroup.ounie.com/api/panels \
  -H "Authorization: Bearer fcg_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "artifact": "One tool. Every workflow. Start free…",
    "icp": "Solo consultants who bill hourly",
    "focus": "pricing",
    "panel_size": 6
  }'
{
  "ok": true,
  "panel_id": "…",
  "synthetic": true,
  "notice": "These panelists are simulated…",
  "panel_score": 2.8,
  "dissent": "The freelancer never reaches the form — there is no price on the page.",
  "top_objections": ["No price anywhere", "…"],
  "revisions": ["Put the number on the page", "…"],
  "personas": [
    {
      "id": "p1",
      "label": "Solo consultant, 8 years, bills hourly",
      "archetype": "Budget-conscious skeptic",
      "context": "Evaluating on a Sunday, will not book a call",
      "score": 2,
      "firstReaction": "…",
      "objections": ["…"],
      "wouldAct": false,
      "suggestedRevision": "…",
      "quote": "…",
      "citations": [],
      "synthetic": true
    }
  ],
  "grounded": false,
  "credits_spent": 8
}
Panelists are labelled, never named. A label is a role description. If the model returns something that reads as a person's name, the server replaces it with the archetype before it reaches you — so you can never accidentally publish “what Sarah Chen said” about copy nobody read.

Read and export

Reads of a panel you already paid for are free, forever.

GET/api/panelsfree
GET/api/panels/{id}free
GET/api/panels/{id}/export?format=csv|markdown|jsonfree
PATCH/api/panels/{id}rename · free
DELETE/api/panels/{id}free

CSV and Markdown exports carry the “simulated, not surveyed” notice inside the file, because an exported table is the thing most likely to be mistaken for research once it leaves this app.

MCP

One MCP server at https://focusgroup.ounie.com/api/mcp (Streamable HTTP) and /api/sse (legacy). Works with Claude, Cursor, ChatGPT, the AI SDK, and the Ounie AI Team — which cannot set static headers, so the key rides the URL as ?api_key=.

{
  "mcpServers": {
    "focus-group": {
      "url": "https://focusgroup.ounie.com/api/mcp",
      "headers": { "Authorization": "Bearer fcg_live_…" }
    }
  }
}
ToolCostWhat it does
run_panel8 crRun a simulated panel against an artifact.
list_panelsfreeYour panels, newest first, with scores and dissent.
get_panelfreeOne panel with every panelist's full reaction.
export_panelfreeCSV or Markdown text.
get_credit_balancefreeSpendable Ounie credits.
get_pricingfreePublic — no auth needed.
whoamifreeOwner id, key id, and whether this key can ground in brains.

x402 — no account, pay per call

POST/api/x402/panelUSDC on Base

For agents with no Ounie account. The first call returns 402 with a price quoted in atomic USDC; retry the same request with an X-Payment header. The price is computed from your payload, never a constant in the route.

FieldTypeNotes
artifactstring100–8000 chars. Required unless artifact_url is given.
artifact_urlstringA public http(s) page. Read for free before any price is quoted.
icpstringWho the panel represents.
focusenumclarity | objections | pricing | positioning | trust
panel_sizeint4–8
source_textstringOptional customer research to ground the panelists in, 200–20000 chars. Replaces brain_ids on this rail — a keyless caller has no brains.
source_titlestringWhat to call that research in citations.
# 1. quote
curl -X POST https://focusgroup.ounie.com/api/x402/panel \
  -H "Content-Type: application/json" \
  -d '{"artifact":"…","icp":"Solo consultants","focus":"pricing"}'
→ 402 { "x402Version": 1, "accepts": [{ "scheme": "exact",
        "network": "base", "maxAmountRequired": "…", "payTo": "0x…" }] }

# 2. pay
curl -X POST https://focusgroup.ounie.com/api/x402/panel \
  -H "X-Payment: <base64 signed authorization>" \
  -H "Content-Type: application/json" -d '{ … }'
Two gates, and the order matters. Anything knowable from your request alone and always fatal — a bad focus, a too-short artifact, a non-public artifact_url, a page with no readable text — returns 4xx with no price attached. We will not make you sign an authorization for work that cannot succeed. Anything only knowable afterwards — a panel with no dissenter — is caught after your payment is verified but before it is settled, so nothing moves on chain and your signed authorization simply goes unused.

Panels paid over x402 are returned in the response and not stored — there is no account to store them against.

The honesty gate

A panel where every panelist scored the artifact 4 or 5 is not a good result. It is a broken panel, and the entire value of this product is the reaction you did not want. So it is checked in code, not left to the prompt:

  • At least one panelist must score 3 or lower.
  • That panelist must raise at least one concrete objection.
  • The disagreement must survive into the summary as a non-empty dissent line, rather than being averaged away.

If the first pass fails, we retry once with a stricter brief. If it fails again the run is refused: 422 thin_panel on the credit rail with a full pool-exact refund, and 402 thin_panel before settlement on x402. You are never billed for a room that agreed with you.

This is also why a genuinely strong piece of copy still comes back with a low scorer. That panelist is the person your copy is not for, explaining why — which is usually the more useful half of the answer.

Errors

StatusErrorMeaning
400invalid_payloadA required field is missing or an enum value is unknown. Always fatal; never quoted on x402.
400artifact_too_shortUnder 100 characters of copy.
400 / 422artifact_unreadableThe URL was non-public, unreachable, the wrong content type, or returned no readable text. 400 on x402 (before the quote), 422 on the credit rail (before anything is reserved).
401auth_requiredNo valid credential.
402insufficient_creditsCarries required_credits, balance_credits and buy_credits_url.
403master_key_requiredbrain_ids was passed with a per-app key. Drop brain_ids or use your ounie_live_ key.
404unknown_brainA brain_id is not on your account.
422 / 402thin_panelNo genuine dissenter after a retry. Refunded in full; never settled on x402.
502generation_failedThe model returned nothing usable. Refunded in full.