← ThinkAloud · API keys (profile) · English · Dansk
ThinkAloud is API-first: the UI uses exactly the same endpoints described here. Built for server-to-server integration (e.g. an analytics tool as an add-on: measurement finds the page with the high exit rate and the search query — ThinkAloud simulates *why* people leave). No CORS is opened on purpose: call from your backend, not from the browser.
Base URL: https://thinkaloud.activero.net (pilot; one machine — run one test at a time).
Auth — two ways:
Authorization: Bearer ta_... — created at /app/profile → "API keys" (shown only once; up to 10 active, revoke individually). The key carries account context: runs are attributed to the account, credits are deducted, and GET /api/runs lists only the account's own runs.X-Access-Code: <code>. The server's THINKALOUD_ACCESS_CODE is a comma-separated list — give each integration its own code so it can be revoked independently. No account context (no crediting/ownership).Read endpoints require one of: a signed ?t= token (from status/list responses), ownership, or an access code. The 401 detail is always "access_denied".
POST /api/runs
Content-Type: application/json
Authorization: Bearer ta_...
{
"url": "https://example.com/page",
"goal": "Understand what the page offers, and whether it is relevant to you.",
"contentType": "dissemination", // conversion | dissemination | reference
"mode": "thinkaloud", // thinkaloud (UX) | e2e (functional test)
"lang": "en", // output language for personas + report (en | da)
"model": "glm", // comma-sep.: glm (default, cheapest),gemini,claude,llama,mistral (EU),grok (premium)
"steps": 8, // steps per persona (e2e flows: use 15)
"synthesisModel": "grok", // optional premium pen: WRITES the recommendations/
// synthesis (personas keep their own model).
// Comprehensive runs use grok automatically.
"personas": [
{"id": "searcher", "label": "Searcher with concrete intent",
"role": "You googled '<query from Search Console>' and landed here. You are impatient...",
"style": "maalrettet"} // optional: metodisk (reads everything, in order) |
] // rodet (skims, jumps around) | maalrettet (beelines)
}
→ {"run_id": "20260722-091653-f5fc0f"}
For search-query integrations: build the persona's role from the real query ("You googled X …") instead of an invented profile — that is the whole point.
Three optional fields in the same POST /api/runs body open pages behind a gate — TEST ACCOUNTS ONLY (the personas click around for real, including destructive buttons):
{
"auth": {"user": "test", "password": "..."}, // HTTP Basic (staging gates)
"headers": {"X-Api-Key": "..."}, // arbitrary headers, e.g. a key-gated site
"storage_state": "{\"cookies\": [...]}" // session injection: log in yourself, export
} // the session (Playwright storageState OR a
// cookie-editor array) → SSO/2FA without
// sharing a password
Security: secrets are handed to the browser harness via ephemeral env variables only — the server-side config.json keeps redacted placeholders (***), and they never appear in the report or the log. Form login with a test account also works without these fields: put the account in goal ("log in as test@… / password …") and the persona fills in the login form itself.
GET /api/runs/{run_id}
→ {"run_id": ..., "status": "running|done|error", "log": "<tail>",
"progress": {"phase": "reading", "pct": 42, "text": "Persona · Model", ...}}
Poll every 10-15 s. A run typically takes 1-5 min (more personas/models = longer; per-page first-glance anchoring costs ~25 s per new unique page the personas visit).
GET /api/runs/{run_id}/results ← machine-readable (integrations)
GET /api/runs/{run_id}/report ← HTML report (humans; sharing: the link is open)
GET /api/runs/{run_id}/report.pdf ← PDF
/results (only when status: "done"):
{
"run_id": "...", "status": "done",
"url": "...", "contentType": "...", "goal": "...", "mode": "thinkaloud", "lang": "en",
"firstGlancePriming": true, // personas' first impression was saliency-anchored
"modelsUsed": ["Gemini-2.5"],
"personas": [{"label": "...", "model": "Gemini-2.5", "endedBy": "done"}],
"outcomes": [ /* e2e mode: {label, model, succeeded, endedBy, steps, blocker, url, step} */ ],
"issues": [{"title": "...", "field": "tryghed|mening|kan|besvaer|defekt",
"summary": "...", "nPersonas": 2, "avgSev": 3.5, "models": ["Gemini-2.5"],
"pts": [{"label": "...", "model": "...", "url": "...", "what": "...",
"severity": 4, "step": 3}]}],
"positives": [ /* same shape, severity 0 */ ],
"attention": {"model": "deepgaze-iie", "on_action_total": 0.21, "off_action": 0.79,
"elements": [{"name": "...", "kind": "handling|overskrift|billede",
"share": 0.17, "primary": true}]}
}
The field values are the behavioural-friction fields (in Danish, stable as API identifiers): tryghed = trust, mening = meaning, kan = ability, besvaer = effort, defekt = defect.
Interpretation: issues are sorted worst-first (most personas → most models → highest average severity). nPersonas >= 2 = convergent. Everything is prediction, not measurement — label it that way in your UI. The validation loop: compare predicted friction with the actual exit rate.
POST /api/panel {brief|candidates, ...} → focus group (synchronous, 1-3 min;
~25 credits). model default =
"claude,glm": a variance test
(2026-08-24) showed single-model panels
can flip the winner between runs — two
models reproduced the same verdict 3/3
POST /api/suggest-personas {url, goal?, lang?} → suggest personas from the page (gated)
GET /sample → static example report (open)
GET /api/defaults?lang=en → default personas + {"gated": true}
GET /api/keys · POST /api/keys · DELETE /api/keys/{id} → API-key management
(requires a login session, NOT a key/code)
GET /api/auth/me → account + credit balance (works w. Bearer)
The thinkaloud-mcp npm package exposes all of the above as tools in Claude Desktop/Code and other MCP clients — thinkaloud_start_test, thinkaloud_test_status, thinkaloud_test_results, thinkaloud_list_tests, thinkaloud_focus_group, thinkaloud_suggest_personas, thinkaloud_account. One-line setup in Claude Code:
claude mcp add thinkaloud -e THINKALOUD_API_KEY=ta_YOUR_KEY -- npx -y thinkaloud-mcp
Auth via THINKALOUD_API_KEY (the Bearer key above). Source lives in mcp/ in the repo.