add_project_file
ChatGPTAttach a text reference document to a project: research notes, a discussion guide, prior findings, a data extract. Chats in the project can read it, and get_project lists it. Send the document's text in content; the extension on filename sets its type and must be .md, .txt, .csv or .json. To attach a PDF or Office file, send its text as .md, or upload the original on the project page in Listen Twins. At most 4,000,000 characters. These are Listen Twins projects, not Listen Labs studies or study folders. For studies and folders, use list_studies, move_study and manage_folder.
archive_project
ChatGPTArchive a project: it leaves the user's project list in Listen Twins, and its brief, files, chats and runs are kept. Pass archived=false to restore it. list_projects with includeArchived=true shows archived projects. These are Listen Twins projects, not Listen Labs studies or study folders. For studies and folders, use list_studies, move_study and manage_folder.
classify_question
ChatGPTPREFLIGHT a question against a Twins panel BEFORE running it. Cheap (a few seconds, and it doesn't ask the panel) and safe to auto-approve. Reach for it whenever you're unsure the panel's respondents were asked about the topic, so you don't spend the 30-90s of a run only to get confidence: low, reasonCode: unrelated back at the end. Returns the SAME verdict a run reports as result.questionClassification: confidence (very_high/high/medium/low), a one-sentence reason, and a reasonCode (e.g. direct_match, wrong_audience, unrelated). Branch on it: very_high/high -> run; medium -> run but caveat; low -> rephrase, pick a better-fit panel from list_panels, or caveat hard before running anyway. panelName must come from list_panels. kind (mcq/oe/ranking) and options are optional: fit is about audience and topic, so (panelName, question) is enough.
create_project
ChatGPTCreate a Project the user will see in Listen Twins. Write the brief as standing context for every chat in the project: the decision it informs, the audience, what is already known, and any constraints. Confirm the name and brief with the user first. These are Listen Twins projects, not Listen Labs studies or study folders. For studies and folders, use list_studies, move_study and manage_folder.
create_study
ChatGPTStart a new guided user-interview study with the platform's creation agent. The agent walks the user through a multi-stage onboarding (study_goals -> recruitment_type -> recruitment_target -> interview_mode -> study_guide). This first call seeds the study and starts the conversation. Subsequent turns MUST use edit_study with the returned studyId AND the chatId from this call's response, supplying either prompt (free-form answers or refinements) or buttonClick (one of the structured events returned in nextActions). Returns the new studyId, the server-minted chatId, summary, current onboardingStatus, optional agentQuestion, suggestions, a nextActions array describing the structured events the user can send next, and the full study state (including state.studyGuide). ALWAYS print the whole study guide. After this call returns, render the entire state.studyGuide to the user verbatim: list every block in order with its title, and under each block every question and statement in order, including the full question text and all answer options. Never summarize, truncate, or omit questions. Do this on every create_study call, even when onboarding is still in progress and the guide is partial or empty (in that case say the guide has no questions yet). Organization: REQUIRED when the user belongs to more than one organization. Call list_creatable_orgs first if the user did not name a workspace, ask which org to use, then pass orgName (or orgId) on create_study. Never call create_study without orgName/orgId when multiple orgs exist. chatId: omit this argument. The server mints a fresh chatId and returns it. Persist that chatId locally so you can pass it back into edit_study for follow-up turns in the same conversation.
describe_panel
ChatGPTUse this to find out WHO a Twins panel is and WHAT IT CAN SPEAK TO before you spend a run on it. Free and instant: it reads the panel's profile and doesn't ask the panel anything. What you can't get from list_panels: - segments: the axes this panel can be cut by, each with its values and how many Twins hold each. These are the same axes every run reports result.segmentBreakdown along, and the same {dimension, value} pairs find_quotes(segment=...) takes. Check here BEFORE promising the user a cut; a panel that doesn't declare region cannot be read by region. - askedAbout: what these people were really asked when the panel was built: a question count and a short description of the ground covered. Their Twins know it first-hand, so it is the cheapest way to write a question that comes back high-confidence. - knows / usedFor: the panel's brief, what its interviews cover and what it is worth asking. Reach for it when the user's request implies a subgroup ('what do the younger ones think'), when you're about to write copy or a question and want the panel's own framing, or when classify_question came back low and you need to see what the panel actually knows about. panelName must come from list_panels. Returns the list_panels fields plus segments, knows, usedFor and askedAbout (each of the last three is null when the panel does not have it yet).
edit_study
ChatGPTSend a natural-language edit instruction for a study to the platform's creation agent. See the server-level instructions for the full capability boundary (what edit_study can and cannot do). Two modes, picked automatically by the server from the chatId's prior turns: 1. Fresh chatId on an existing study: direct-edit mode. Send a plain prompt ('remove Q3', 'switch panel to professionals and bump to 200 people'). No buttonClick. 2. chatId carried over from a create_study call this session: the agent walks onboarding stages. Use buttonClick values returned in the previous turn's nextActions; use prompt to answer the agent's question or refine. Each call must supply EITHER prompt OR buttonClick. Call get_study_state first if you don't already have a recent snapshot. chatId: omit on the first turn of a brand-new edit session. To continue an in-progress conversation (either a create_study walkthrough or a prior edit_study turn), pass the chatId returned in the previous turn's response. The response always echoes the chatId actually used; persist that value for follow-up turns.
fetch_organization_calls
ChatGPTOrganization-admin only. Fetch the MCQ and open-ended questions that non-admin members of a Twins organization you administer have asked against panels granted to that organization. Use it to see what a team has been asking Twins, not to read run results: each entry is the question text, who asked it (display name), and the classifier's confidence that the panel fit the question. Call list_organizations first to obtain organizationId. Dates are inclusive UTC calendar dates. Pages are newest first; when nextCursor is set, pass it back to fetch the next page. Returns {organizations: [{organizationId, organizationName, calls: [{askedBy, question, confidence}]}], nextCursor}.
find_quotes
ChatGPTUse this to PULL THE ACTUAL WORDS Twins used in a past run: for quotation in your reply, for verifying that a theme means what its label suggests, or for designing a sharper follow-up. Real human-modeled language beats your paraphrase every time, so reach for this whenever the user wants to 'hear from' the panel or you're about to summarize results in your own words. Works on mcq and oe runs. On an OE run verbatim is each Twin's answer. On an MCQ run verbatim is the respondent's own words from their interview that explain the pick, reasoning sums up why they chose it, and choice is the pick, so find_quotes(runId, option='Headline 3') answers 'why did the ones who chose it choose it' from the finished run, without asking the panel again. Common chains: - OE: summarize_run for themes -> find_quotes(theme=...) for 3-5 verbatims per theme. - MCQ: run_mcq -> find_quotes(option=<winner>) for the reasons it won, then option=<runner-up> for the objection you have to beat. Filters intersect; a quote must clear every one you pass: - questionId: one question (from a summarize_run entry). - theme: OE only; Twins whose answer carries that theme label. - option: MCQ only; Twins who picked that option. Exact label or integer index. - segment: one declared segment value, e.g. {dimension: 'age_band', value: '25-34'}. Valid names and values come from describe_panel. An unknown theme, option or segment returns an empty list rather than an error, so a filter that matches nothing is always distinguishable from a failed call. limit defaults to 50, max 200. Returns {quotes: [...]}.
get_project
ChatGPTUse this before designing questions for a project: the brief says what decision the research serves and who it is for. Returns the full brief, the default panel, the reference files attached to the project, and the runs filed under it (pass a runId to summarize_run or find_quotes). These are Listen Twins projects, not Listen Labs studies or study folders. For studies and folders, use list_studies, move_study and manage_folder.
get_reel_download_url
ChatGPTGet a direct MP4 download link for a reel, for handing the file to another workflow. Take reelId from the Reel ID line of a reel section in get_study_analysis. Requires view access to the reel, and only works for organizations with video import enabled; other organizations get a 403. status already_rendered: signedUrl is a signed link that anyone holding it can use to download the MP4 for 24 hours. Treat it as sensitive and do not post it publicly unless the user asks. status queued or already_requested: the MP4 is not rendered yet. This call starts the render, and Listen Labs emails the user a link when it finishes. Tell the user that, and call this tool again later to get signedUrl.
get_response
ChatGPTDeep-dive into a single respondent's interview. Returns a structured transcript with question tracking, input types, and multiple choice data. Paginated for large interviews. Each transcript item carries a source_url deep-link to that exact message in the respondent's transcript (e.g. https://listenlabs.ai/response/<id>?message=<n>); it is null only for items with no respondent answer. When the respondent arrived via a recruitment link with custom URL parameters, those key/value pairs are returned in url_params (omitted when none were captured). Grounding requirement: whenever you quote a respondent, surface the quote verbatim and immediately follow it with a [Source] markdown link built from that item's source_url. Never present a quote without its source link, never reuse one item's link for another, and never state a finding, number, or sentiment that is not present in the returned transcript.
get_study_analysis
ChatGPTGet the AI-generated analysis report for a study, rendered as markdown. Use list_studies first to find studies with has_analysis=true. The returned markdown is sourced. Each respondent quote is shown verbatim followed by a [Source] link to that respondent's transcript at the cited message (e.g. https://listenlabs.ai/response/<id>?message=<n>). Named metric, chart, table, and reel references appear by name, with their real values listed in the sections appended below the report body. Grounding requirement: when you summarize or draw any conclusion from this report, support every claim or opinion with a source from this output: quote the respondent verbatim and keep its [Source] link. STRICT, NON-NEGOTIABLE REQUIREMENT: every quote you surface MUST be immediately followed by its exact [Source] link, copied verbatim from the report. Never present a quote without its [Source] link, never move, merge, reorder, paraphrase, or fabricate a link, and never reuse one quote's link for another. If a quote has no [Source] link in the returned content, do not use that quote at all. Never state a finding, number, or sentiment that is not present in the returned content, and never invent or paraphrase a quote, attribution, number, or link.
get_study_responses
ChatGPTGet response transcripts for a study. Returns formatted interview transcripts with pagination to stay within token limits. Each respondent answer is followed by a [Source] markdown link to that exact message in the respondent's transcript (e.g. https://listenlabs.ai/response/<id>?message=<n>). Grounding requirement: whenever you quote a respondent, surface the quote verbatim and immediately follow it with its exact [Source] link, copied verbatim from this output. Never present a quote without its [Source] link, never move, merge, reorder, or fabricate a link, and never reuse one quote's link for another. Never state a finding, number, or sentiment that is not present in the returned transcripts.
get_study_state
ChatGPTReturn the current state of a study (title, audience, study guide outline, questions, screener, recruitment) in a slim, LLM-friendly shape. Use this before edit_study to see what the study currently contains. Also returns a launch block: whether the study is published, the organization's credit balance, per-recruitment cost and eligibility (alreadyLaunched, eligibleAlone), the total cost to launch every unlaunched recruitment, and any blockers (e.g. no_recruitments, study_busy). Call this before launch_study to preview which recruitments will be launched and which will be skipped for insufficient credits.
launch_study
ChatGPTPublish the study's dev revision (if needed) and start every unlaunched recruitment that fits the organization's credit balance. Recruitments are launched greedily in dashboard order: each one whose cost fits the running balance is launched and its cost deducted; any that no longer fit are reported as skippedInsufficientCredits so you can tell the user exactly which ones could not run. Self-recruit links bill per response and are always launched if unlaunched. Panel recruitments have an upfront cost that must fit the balance. Custom-link recruitments (driven by external-panel dashboards) are not launched here. If the user picked "I'll bring my own participants" in onboarding but never created a recruitment link, this tool will create one automatically and activate it so the study can launch in a single step. Returns: published (whether a dev revision was promoted to prod), launched (recruitments that started successfully), skippedInsufficientCredits, skippedAlreadyLaunched, balanceBefore, balanceAfter, totalCostLaunched, and any failures. Call get_study_state before this tool to preview which recruitments will launch and which will be skipped for credits. The user must add credits in the Listen Labs dashboard if balance is too low. Safe to re-call: already-launched recruitments are skipped, not relaunched.
list_creatable_orgs
ChatGPTList organizations the user belongs to where they can create studies. Supports pagination and case-insensitive substring search on org name. Returns JSON: { "orgs": [{ "id", "name", "role" }], "total": number, "hasMore": boolean, "nextCursor"?: string, "textHint"?: string }. total is the count of all orgs matching the textHint (or all orgs the user is in, if no textHint). Inputs: textHint (optional substring to filter org names by, case-insensitive), cursor (opaque value from a previous response's nextCursor), limit (defaults to 10, max 25). Call when the user asks to list their orgs/workspaces, when they ask 'how many orgs am I in', or before create_study when they have not named which organization to use. Pagination: when hasMore is true, pass the returned nextCursor on the next call to fetch the following page. Keep textHint identical across pages of the same search. Search: when the user names a workspace, prefer passing textHint over enumerating. If textHint returns 0 orgs, ask the user for a different name; do not retry with no textHint and dump every org. If multiple orgs are returned, ask the user to choose one and pass orgName (or orgId) on create_study.
list_organizations
ChatGPTList the Twins organizations the user belongs to, with their role in each (user or admin). Call this first to obtain the organizationId that fetch_organization_calls takes. Returns {organizations: [{organizationId, name, role}]}.
list_panels
ChatGPTSTART HERE for Twins. Call this first whenever the user asks anything about what real people think, prefer, or would pick: it shows you which Twins panels you have access to and the kinds of questions each one will answer. Every run_* tool needs a real panelName from this list, or a projectId whose project has a default panel. Each entry: panelName (pass verbatim to run_mcq / run_oe / run_ranking), panelistCount, description (who this panel represents and what topics they're good for), composition (the demographic axes its Twins are profiled on; use these to judge fit for the question), and supportedKinds (the subset of {mcq, oe, ranking} this panel can run). Every panel listed is ready to run for the kinds it lists. This listing is deliberately thin. Once you've picked a panel, describe_panel(panelName) is free and adds the two things you need to plan a study: the segment dimensions it can be cut by, and what its real respondents were asked. Returns {panels: [...]}.
list_projects
ChatGPTList the user's Listen Twins Projects: named research contexts, each with a brief and a default panel that every chat in the project starts from. Returns the opening of each brief; get_project has the full brief and reference files. Pass includeArchived=true to include archived projects. These are Listen Twins projects, not Listen Labs studies or study folders. For studies and folders, use list_studies, move_study and manage_folder.
list_studies
ChatGPTList studies accessible to the authenticated user. Returns study ID, name, status, creation date, response count, and whether analysis is available. Paginated (50 per page). Response: { studies, total_count, next_cursor, text_hint? }. total_count reflects the filter (textHint + status); use it to answer 'how many studies do I have?'. Pagination: when next_cursor is non-null, pass it as cursor on the next call to fetch the following page. Keep textHint and status identical across pages of the same search. Search: when the user names a study (or part of a study title), pass it as textHint to filter case-insensitively on the study title (matches either the published or draft title). If textHint returns 0 studies, ask the user for a different name; do not retry with no textHint and dump every study.
manage_folder
ChatGPTCreate, rename, re-nest, or delete a dashboard folder. Folders only: it never creates, moves, or deletes a study. Use move_study to file a study in a folder. Rename, move, and delete need an existing folder, matched case-insensitively at any depth; pass a "/"-separated path (e.g. "Research / Q3 2026") to choose between duplicate names. When a name is unknown or ambiguous the error lists the folders with their full paths: retry with one of those exactly. action="create": makes the folder, plus any missing parents along the path. The path is resolved from the dashboard root, so "Q3" is a root-level folder even when a nested "Research / Q3" exists. A folder already at exactly that path is returned unchanged with didCreate=false. action="rename": renames the folder, keeping its position and contents. Requires newName. action="move": re-nests the folder under parentFolderName (or parentFolderId), or to the dashboard root when both are omitted. Works on a folder holding studies or subfolders too: the contents travel with it. A folder cannot move into itself or its own descendants. action="delete": permanently removes an EMPTY folder, and only when the user explicitly asked to delete it. Folders holding studies or subfolders are refused; empty them with move_study first rather than assuming the contents are unwanted. Never delete to "tidy up" on your own initiative. create_study and move_study only accept a folder that already exists, so this is also how you make room for them when the user names a folder they do not have yet. Organization: REQUIRED when the user belongs to more than one organization, same as create_study. Call list_creatable_orgs first if they did not name a workspace.
move_study
ChatGPTMove a study into a folder, or back out to the dashboard root. Pass folderName to move it into an existing folder. Names are matched case-insensitively at any nesting depth; when several folders share a name, pass the full path instead (e.g. "Research / Q3 2026"). Omit both folderName and folderId to move the study out of its folder and back to the dashboard root. The folder must already exist: an unknown or ambiguous name is rejected and the error lists the available folders with their full paths. Retry with one of those exactly, or create the folder with manage_folder first if the user asked for a new one. Never invent a folder name the user did not say. A study lives in exactly one folder, so this replaces its current location rather than adding to it. Same organization only: folderName and folderId are resolved against the folders of the study's own organization, so a folder from another org (even one the user can access) is not a valid destination. This tool cannot transfer a study to another organization, and nothing else can either. If that is what the user wants, say it is not possible. Returns the resolved destination: folderPath (null when the study is now on the dashboard root) and a human-readable location.
publish_study
ChatGPTPublish the study's current dev revision to prod so respondents see the latest version. Use this after edit_study on an already-launched study, otherwise respondents keep seeing the old published guide. No-op when the dev revision is identical to prod; the response will report published=false with a note. Does not start any recruitments. Use launch_study if you also want to begin sourcing respondents; launch_study auto-publishes before launching. Returns: published (boolean), prodRevisionId on success, or a note when there were no changes.
run_mcq
ChatGPTUse this when the user wants to know HOW A POPULATION SPLITS across concrete options: A vs B preference, brand pick, feature priority, ad-creative winner, pricing tier, anything where the answers can be listed up front. Returns the share of the panel's Twins that picks each option. Reach for run_mcq over run_oe whenever you can list the options: a forced choice gives you a percentage to quote. The chain worth knowing: run_mcq to size the split, then run_oe with screen={screenerRunId: <this runId>, keepOptions: [<option>]} to ask WHY of only the Twins who picked it, with {{choice}} in the question standing in for each one's pick. Cheaper still when you only need the reasons: find_quotes(runId, option=...) reads them off this finished run without asking the panel again. BLOCKS until the panel finishes (typically 30-90s); progress notifications stream while it works if your client supports them. Returns {runId, taskId, kind, result}. result.finalDistribution carries each option's percentage with stdDev: Listen's uncertainty for that share in percentage points, covering systematic error as well as sampling noise. Quote splits as 47% (±2.1) and never call it a 95% CI or a standard deviation; when the panel hides uncertainty stdDev is absent, so quote shares without one. result.questionClassification.confidence (very_high/high/medium/low, with a reason) says how well the question fits what the panel's respondents were asked: ALWAYS check it, and caveat or rephrase when it is low. When the panel declares segments, result.segmentBreakdown.dimensions[] carries the same split per segment value (with per-cell n), so you can quote 'Women: 72% A' directly. result.responses is a sample of about 5 Twins per option (uniqueId, choice, note); pass verbose=true for every Twin. panelName must come from list_panels. To show the panel images, send them inline in attachments (an MCP ImageContent block plus a ref: {ref: 'image_1', type: 'image', data: <base64>, mimeType: 'image/png'}) and name each as [image_1] in optionConceptImagePaths (images per option) or conceptImagePaths (images shown with the question). Those fields take refs only, never URLs. All images in one call share a budget of about 3 MB, so downscale large creative first. When the work belongs to a project (see list_projects), pass its projectId: the run is filed there, get_project lists it, and panelName defaults to the project's default panel. Set includeCsv=true to also attach the option split as a downloadable CSV; the JSON result is returned either way.
run_oe
ChatGPTUse this when the user wants the WHY, the verbatims or the themes: anything where the answers can't be listed up front and the population's own words are the point. Good for reasons behind a behavior, unprompted feature requests, language for marketing copy, and objections you didn't know to ask about. Reach for run_oe over run_mcq whenever forcing a choice would throw away the interesting variance. Most useful chained onto a prior run_mcq via screen: with screen={screenerRunId: <the mcq's runId>, keepOptions: ['B']} only the Twins who picked B are asked, so the answers belong to a known group. In the question text, {{choice}} and {{answer}} become each Twin's own earlier answer: 'You picked {{choice}}. What made it land?' reads as a real follow-up. BLOCKS until the panel finishes (typically 30-90s); progress notifications stream while it works if your client supports them. Returns {runId, taskId, kind, result}. result.themes is the aggregate [{name, count}] and result.responses holds verbatims tagged with their themes. result.questionClassification.confidence (very_high/high/medium/low, with a reason) says how well the question fits what the panel's respondents were asked: ALWAYS check it, and caveat or rephrase when it is low. When the panel declares segments, result.segmentBreakdown.dimensions[] carries theme counts per segment value (themeCounts, with per-cell n). Responses are a sample of about 3 verbatims per theme; pass verbose=true for every Twin's answer. respondentContext ({uniqueId: {key: value}}) is the manual form of the same thing: it fills {{key}} per Twin and asks only the Twins it names. Prefer screen, which derives all of it from one runId. To show the panel an image, send it inline in attachments (an MCP ImageContent block plus a ref: {ref: 'image_1', type: 'image', data: <base64>, mimeType: 'image/png'}) and put [image_1] in conceptImagePaths. That field takes refs only, never URLs. All images in one call share a budget of about 3 MB, so downscale large creative first. When the work belongs to a project (see list_projects), pass its projectId: the run is filed there, get_project lists it, and panelName defaults to the project's default panel. Set includeCsv=true to also attach the theme counts as a downloadable CSV; the JSON result is returned either way.
run_ranking
ChatGPTUse this when comparing 2-16 VISUAL concepts and the user wants to know which would win in market: ad creative, product designs, packaging, landing-page variants, app screens, logo options. Returns a yes-rate per concept, ordered so the winner is obvious. Yes-rates are calibrated against real launch outcomes, so read them as the share of the population that would buy, click or engage, not as abstract scores. Pick this over run_mcq when each concept stands on its own and you want absolute approval, not a forced pick. Pick this over run_oe when you already have the concepts and want the numerical winner, not language about why. BLOCKS until the panel finishes (typically 30-90s); progress notifications stream while it works if your client supports them. Returns {runId, taskId, kind, result}. result.rankingAggregate carries each concept's yes-rate with respondentCount and stdDev: Listen's uncertainty for that rate in percentage points, covering systematic error as well as sampling noise. Never call it a 95% CI or a standard deviation; when the panel hides uncertainty it is absent. result.questionClassification.confidence (very_high/high/medium/low, with a reason) says how well the question fits what the panel's respondents were asked: ALWAYS check it, and caveat or rephrase when it is low. When the panel declares segments, result.segmentBreakdown.dimensions[] carries a rankingAggregate per segment value (per-cell n), so you can see which segments drive each concept. Per-Twin responses are left out by default; pass verbose=true for each Twin's yes/no per concept. Each concept needs a stable ref (the key it comes back under), a short title (scored with the image) and an optional description (1-2 sentences of context the panel sees, never displayed). path is optional: - [image_N], naming an attachments entry: scored from image, title and description. This is the calibrated read. Send each image inline as an MCP ImageContent block plus a ref: {ref: 'image_1', type: 'image', data: <base64>, mimeType: 'image/png'}. All images in one call share a budget of about 3 MB, so downscale large creative first. - omitted: a text-only concept, scored from title and description. Fine for comparing rough ideas, LESS reliable than the image read for a decision. path takes refs only, never URLs, and a ref with no matching attachment fails the call instead of quietly scoring the concept as text. When the work belongs to a project (see list_projects), pass its projectId: the run is filed there, get_project lists it, and panelName defaults to the project's default panel. Set includeCsv=true to also attach the yes-rates as a downloadable CSV; the JSON result is returned either way.
search_across_studies
ChatGPTSearch over stud and response metadata (name, goal, and classification/theme labels)
summarize_run
ChatGPTUse this to RECALL the aggregate results of a past Twins run without asking the panel again. Free and instant: it reads the stored results. Reach for it whenever the user references a prior run ('what did the last MCQ say?', 'compare to the Tuesday ranking'), or any time you need the headline numbers from a runId without the 30-90s of a new run. Pass the runId returned by run_mcq / run_oe / run_ranking, or any earlier runId of yours. Returns one entry per question in the run with a kind-specific rollup: - mcq: distribution of option percentages. - oe: themes (label + how many Twins per theme). - ranking: aggregate of per-concept yes-rates. Pair with find_quotes to pull the actual verbatim answers behind each OE theme. Set includeCsv=true to also attach the aggregate as a downloadable CSV (one row per option / theme / concept); the JSON result is returned either way.
get_response
Claudeget_study_analysis
Claudeget_study_responses
Claudelist_studies
Claudesearch_across_studies
Claude