{"openapi": "3.1.0", "info": {"title": "Funnel Leak Finder API", "version": "1.0.0", "description": "Search journey leak finder. It samples real search paths that start from category entry keywords with path_finder, classifies every node as a brand, generic, compare, exit or channel query, and shows where journeys switch to competitor brands and where your brand captures or loses them. Brand marketers and search / content teams use it to pick points to defend or attack. Data comes from the ListeningMind Data API (path_finder, keyword_info, intent_finder, SERP / AI Overview). Markets: Korea, Japan, US. Workflow: get_config → suggest_competitors → check_inputs → estimate_credits → start_run → get_run → prune_brands → reclassify_node → get_watch_lines → explain_watch_lines → explain_node → check_ai_overview → get_node_brief_markdown → get_report_markdown → download_leaks_csv", "contact": {"email": "m-support@listeningmind.com"}}, "servers": [{"url": "https://daas-funnel.listeningmind.ai"}], "paths": {"/api/config": {"get": {"operationId": "get_config", "summary": "Settings and options", "description": "Returns markets, example inputs per market, comparison points, path_finder sample sizes with their credit cost, score weights, credit cap and the current account credit balance. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "lang", "in": "query", "required": false, "description": "Language of generated text: ko, ja or en.", "schema": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko"}}, {"name": "gl", "in": "query", "required": false, "description": "Search market: kr (Korea), jp (Japan) or us (United States).", "schema": {"type": "string", "enum": ["kr", "jp", "us"], "default": "kr"}}]}}, "/api/suggest_competitors": {"post": {"operationId": "suggest_competitors", "summary": "Suggest competitors", "description": "Proposes category-qualified competitor brands grounded on the queries around the seeds, returning {competitors: [...]} with names and aliases. Uses intent_finder plus one LLM call; about 330 Data API credits per seed (cap twice that). Fetched data is cached and reused by the run. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: about 330 Data API credits per seed plus 1 LLM call. Time: 10-40 seconds. Usually followed by: check_inputs, estimate_credits.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "about 330 Data API credits per seed plus 1 LLM call", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"seeds": {"type": "string", "description": "Entry keywords where the journey starts, comma separated (a list of strings is also accepted). Up to 3 are used; extra ones are dropped. Each seed is a starting point of the sampled journeys, so seeds define the whole result. Should be a category term, not a brand. Example: 런닝화.", "default": ""}, "my_brand": {"type": ["object", "null"], "description": "Your brand. With it the result is in brand mode (my share, where my brand wins or loses); omit or null for a landscape view (mode landscape) without an own brand. The web form fields are brand name, aliases and domain.", "properties": {"name": {"type": "string", "description": "Brand name as people search it, e.g. 아식스."}, "aliases": {"type": "string", "description": "Other spellings, comma separated (a list of strings is also accepted), e.g. 'asics, 아식스런닝'."}, "domain": {"type": "string", "description": "Brand website domain used to spot it in SERPs, e.g. asics.com."}}}, "competitors": {"type": "string", "description": "Competitors already typed, same format as in start_run, so the suggestion adds to them.", "default": ""}, "gl": {"type": "string", "description": "Search market: kr (Korea), jp (Japan) or us (United States).", "enum": ["kr", "jp", "us"], "default": "kr"}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": ["seeds"]}}}}}}, "/api/check_inputs": {"post": {"operationId": "check_inputs", "summary": "Check inputs", "description": "Checks the run inputs against the brand / channel dictionary and returns {warnings: [{field, text}]}, e.g. a seed that is a brand, or a competitor that is really a retailer or platform. No credits, no LLM. Read only, free. Usually followed by: estimate_credits.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"seeds": {"type": "string", "description": "Entry keywords where the journey starts, comma separated (a list of strings is also accepted). Up to 3 are used; extra ones are dropped. Each seed is a starting point of the sampled journeys, so seeds define the whole result. Should be a category term, not a brand. Example: 런닝화.", "default": ""}, "my_brand": {"type": ["object", "null"], "description": "Your brand. With it the result is in brand mode (my share, where my brand wins or loses); omit or null for a landscape view (mode landscape) without an own brand. The web form fields are brand name, aliases and domain.", "properties": {"name": {"type": "string", "description": "Brand name as people search it, e.g. 아식스."}, "aliases": {"type": "string", "description": "Other spellings, comma separated (a list of strings is also accepted), e.g. 'asics, 아식스런닝'."}, "domain": {"type": "string", "description": "Brand website domain used to spot it in SERPs, e.g. asics.com."}}}, "competitors": {"type": "string", "description": "Competitor brands, comma separated; each may carry aliases after ';' and a trailing '*' for strict matching. Example: '나이키;nike, 아디다스;adidas, 호카*'. A list of {name, aliases, strict} objects is also accepted. Changes which nodes count as competitor brands and therefore every leak and share figure. Brands found by the classifier are added even when not listed.", "default": ""}, "cmp_point": {"type": ["string", "null"], "description": "Compare the current journey with this earlier point: 3m, 6m or 12m. null for no comparison (halves path_finder calls).", "enum": ["3m", "6m", "12m", null], "default": "6m"}, "limit": {"type": "integer", "description": "path_finder sample size per call (paths sampled). Other values are snapped to the nearest of 100, 200, 300, 500. Bigger samples find rarer branches and cost more (about 2,850 / 5,650 / 8,450 / 14,050 credits per path_finder call).", "enum": [100, 200, 300, 500], "default": 200}, "deep": {"type": "boolean", "description": "Extra path_finder calls starting from your brand (if given) and the top 2 hubs, filling result.deep with where those journeys go next. Adds one path_finder call per origin.", "default": false}, "aio_auto": {"type": "boolean", "description": "Read SERP and AI Overview for the top nodes during the run, so the result shows which brands AI Overview mentions there. false skips it.", "default": true}, "aio_nodes": {"type": "integer", "description": "How many top nodes get the automatic SERP / AI Overview read (30 credits each). Clamped to 0-60. Ignored when aio_auto is false.", "default": 20, "minimum": 0, "maximum": 60}, "gl": {"type": "string", "description": "Search market: kr (Korea), jp (Japan) or us (United States).", "enum": ["kr", "jp", "us"], "default": "kr"}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}, "credit_cap": {"type": "integer", "description": "Hard Data API credit cap for this run; the run stops with an error when it is reached. Default is the server cap (30,000 at the time of writing; get_config.credit_cap). The server does not clamp a higher value, so do not raise it without the person's consent.", "minimum": 1}}, "required": []}}}}}}, "/api/estimate": {"post": {"operationId": "estimate_credits", "summary": "Estimate credits", "description": "Returns the expected Data API credits for the inputs: {credits, path_finder_calls, per_call, nodes_est, deep, aio, aio_nodes, cap, note}. Computed locally, no API calls. Read only, free. Usually followed by: start_run.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"seeds": {"type": "string", "description": "Entry keywords where the journey starts, comma separated (a list of strings is also accepted). Up to 3 are used; extra ones are dropped. Each seed is a starting point of the sampled journeys, so seeds define the whole result. Should be a category term, not a brand. Example: 런닝화.", "default": ""}, "my_brand": {"type": ["object", "null"], "description": "Your brand. With it the result is in brand mode (my share, where my brand wins or loses); omit or null for a landscape view (mode landscape) without an own brand. The web form fields are brand name, aliases and domain.", "properties": {"name": {"type": "string", "description": "Brand name as people search it, e.g. 아식스."}, "aliases": {"type": "string", "description": "Other spellings, comma separated (a list of strings is also accepted), e.g. 'asics, 아식스런닝'."}, "domain": {"type": "string", "description": "Brand website domain used to spot it in SERPs, e.g. asics.com."}}}, "competitors": {"type": "string", "description": "Competitor brands, comma separated; each may carry aliases after ';' and a trailing '*' for strict matching. Example: '나이키;nike, 아디다스;adidas, 호카*'. A list of {name, aliases, strict} objects is also accepted. Changes which nodes count as competitor brands and therefore every leak and share figure. Brands found by the classifier are added even when not listed.", "default": ""}, "cmp_point": {"type": ["string", "null"], "description": "Compare the current journey with this earlier point: 3m, 6m or 12m. null for no comparison (halves path_finder calls).", "enum": ["3m", "6m", "12m", null], "default": "6m"}, "limit": {"type": "integer", "description": "path_finder sample size per call (paths sampled). Other values are snapped to the nearest of 100, 200, 300, 500. Bigger samples find rarer branches and cost more (about 2,850 / 5,650 / 8,450 / 14,050 credits per path_finder call).", "enum": [100, 200, 300, 500], "default": 200}, "deep": {"type": "boolean", "description": "Extra path_finder calls starting from your brand (if given) and the top 2 hubs, filling result.deep with where those journeys go next. Adds one path_finder call per origin.", "default": false}, "aio_auto": {"type": "boolean", "description": "Read SERP and AI Overview for the top nodes during the run, so the result shows which brands AI Overview mentions there. false skips it.", "default": true}, "aio_nodes": {"type": "integer", "description": "How many top nodes get the automatic SERP / AI Overview read (30 credits each). Clamped to 0-60. Ignored when aio_auto is false.", "default": 20, "minimum": 0, "maximum": 60}, "gl": {"type": "string", "description": "Search market: kr (Korea), jp (Japan) or us (United States).", "enum": ["kr", "jp", "us"], "default": "kr"}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}, "credit_cap": {"type": "integer", "description": "Hard Data API credit cap for this run; the run stops with an error when it is reached. Default is the server cap (30,000 at the time of writing; get_config.credit_cap). The server does not clamp a higher value, so do not raise it without the person's consent.", "minimum": 1}}, "required": []}}}}}}, "/api/runs": {"post": {"operationId": "start_run", "summary": "Start a leak run", "description": "Starts a background run: samples search journeys from the seeds with path_finder (now and at cmp_point), measures every node with keyword_info, classifies nodes into brand, generic, compare, exit and channel queries (rules plus LLM), reads SERP / AI Overview for top nodes and computes where journeys leak to competitors. Returns {run_id, url}. Poll get_run. Typically 2-7 minutes and about 6,000-15,000 credits with defaults. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: about 6,000-15,000 Data API credits with defaults; deep or larger samples can reach the server cap of 30,000; plus LLM calls. Time: 2-7 minutes. Usually followed by: get_run.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "about 6,000-15,000 Data API credits with defaults; deep or larger samples can reach the server cap of 30,000; plus LLM calls", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"seeds": {"type": "string", "description": "Entry keywords where the journey starts, comma separated (a list of strings is also accepted). Up to 3 are used; extra ones are dropped. Each seed is a starting point of the sampled journeys, so seeds define the whole result. Should be a category term, not a brand. Example: 런닝화.", "default": ""}, "my_brand": {"type": ["object", "null"], "description": "Your brand. With it the result is in brand mode (my share, where my brand wins or loses); omit or null for a landscape view (mode landscape) without an own brand. The web form fields are brand name, aliases and domain.", "properties": {"name": {"type": "string", "description": "Brand name as people search it, e.g. 아식스."}, "aliases": {"type": "string", "description": "Other spellings, comma separated (a list of strings is also accepted), e.g. 'asics, 아식스런닝'."}, "domain": {"type": "string", "description": "Brand website domain used to spot it in SERPs, e.g. asics.com."}}}, "competitors": {"type": "string", "description": "Competitor brands, comma separated; each may carry aliases after ';' and a trailing '*' for strict matching. Example: '나이키;nike, 아디다스;adidas, 호카*'. A list of {name, aliases, strict} objects is also accepted. Changes which nodes count as competitor brands and therefore every leak and share figure. Brands found by the classifier are added even when not listed.", "default": ""}, "cmp_point": {"type": ["string", "null"], "description": "Compare the current journey with this earlier point: 3m, 6m or 12m. null for no comparison (halves path_finder calls).", "enum": ["3m", "6m", "12m", null], "default": "6m"}, "limit": {"type": "integer", "description": "path_finder sample size per call (paths sampled). Other values are snapped to the nearest of 100, 200, 300, 500. Bigger samples find rarer branches and cost more (about 2,850 / 5,650 / 8,450 / 14,050 credits per path_finder call).", "enum": [100, 200, 300, 500], "default": 200}, "deep": {"type": "boolean", "description": "Extra path_finder calls starting from your brand (if given) and the top 2 hubs, filling result.deep with where those journeys go next. Adds one path_finder call per origin.", "default": false}, "aio_auto": {"type": "boolean", "description": "Read SERP and AI Overview for the top nodes during the run, so the result shows which brands AI Overview mentions there. false skips it.", "default": true}, "aio_nodes": {"type": "integer", "description": "How many top nodes get the automatic SERP / AI Overview read (30 credits each). Clamped to 0-60. Ignored when aio_auto is false.", "default": 20, "minimum": 0, "maximum": 60}, "gl": {"type": "string", "description": "Search market: kr (Korea), jp (Japan) or us (United States).", "enum": ["kr", "jp", "us"], "default": "kr"}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}, "credit_cap": {"type": "integer", "description": "Hard Data API credit cap for this run; the run stops with an error when it is reached. Default is the server cap (30,000 at the time of writing; get_config.credit_cap). The server does not clamp a higher value, so do not raise it without the person's consent.", "minimum": 1}}, "required": ["seeds"]}}}}}, "get": {"operationId": "list_runs", "summary": "Run history", "description": "Lists recent runs with run_id, status, credits, created_at, url, seeds, my_brand, cmp_point, gl and deep. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "limit", "in": "query", "required": false, "description": "Maximum runs to return.", "schema": {"type": "integer", "default": 40}}]}}, "/api/runs/{run_id}": {"get": {"operationId": "get_run", "summary": "Run status and result", "description": "Returns one run: status (running, done, error), progress {stage, pct, steps}, credits, params, error and, when done, the result (kpi, share, hubs, leak_edges, funnel, inbound, transition, tree, metrics, classes, serp, overview). Repeat until status is done or error. Read only, free. Waits up to about 45 s for 'status' to reach done, error; call again if not done. Usually followed by: get_watch_lines, explain_node, get_report_markdown.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}, "delete": {"operationId": "delete_run", "summary": "Delete run", "description": "Deletes a run and its stored data. Cannot be undone. Deletes data. A person approves it first.", "tags": ["delete"], "x-lm-kind": "delete", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/api/runs/{run_id}/evidence": {"get": {"operationId": "get_run_evidence", "summary": "Credit log", "description": "For a finished run, returns the Data API calls per endpoint with calls, credits and cache hits. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/api/runs/{run_id}/watch": {"get": {"operationId": "get_watch_lines", "summary": "Lines to watch", "description": "Marketer view of a finished run: rule-selected journey lines (conquest, uncaptured, defend, poach, strengthen) with numbers, plus the LLM wording if it was generated before for this language. Free. Read only, free. Usually followed by: explain_watch_lines.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}, {"name": "lang", "in": "query", "required": false, "description": "Language of generated text: ko, ja or en.", "schema": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko"}}]}}, "/api/runs/{run_id}/watch_explain": {"post": {"operationId": "explain_watch_lines", "summary": "Write watch-line narrative", "description": "Generates and stores the LLM wording (strategy narrative) for the rule-selected watch lines. Returns {items, narrative}. Paid LLM call, no Data API credits. The stored wording also appears in report exports. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: 1 LLM call, no Data API credits. Time: 10-40 seconds.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "1 LLM call, no Data API credits", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": []}}}}}}, "/api/runs/{run_id}/explain": {"post": {"operationId": "explain_node", "summary": "Explain a node", "description": "Explains one journey node of a finished run (who arrives, where they go next, which brands win) with an LLM narrative. With fetch_serp=true and no stored SERP it first reads the SERP (about 30 credits, cap 200). Stored per keyword and language, so repeating is free. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: 1 LLM call; about 30 Data API credits more with fetch_serp; free when stored. Time: 10-40 seconds. Usually followed by: get_node_brief_markdown, download_node_brief_pdf.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "1 LLM call; about 30 Data API credits more with fetch_serp; free when stored", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"keyword": {"type": "string", "description": "Node keyword exactly as in result.metrics."}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}, "fetch_serp": {"type": "boolean", "description": "Read the SERP for this node first when the run has none stored.", "default": false}}, "required": ["keyword"]}}}}}}, "/api/runs/{run_id}/explain_path": {"post": {"operationId": "explain_path", "summary": "Explain a path", "description": "LLM narrative for one line of the journey tree, from the root to a node. The path must exist in result.tree. Stored, so repeating is free. No Data API credits. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: 1 LLM call, no Data API credits; free when stored. Time: 10-40 seconds.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "1 LLM call, no Data API credits; free when stored", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"path": {"type": "array", "description": "Node keywords from the root to the selected node, in order.", "items": {"type": "string"}}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": ["path"]}}}}}}, "/api/runs/{run_id}/aio_check": {"post": {"operationId": "check_ai_overview", "summary": "Check AI Overview", "description": "Reads SERP and AI Overview for the given nodes of a finished run and stores which brands appear. Up to 30 keywords per call; 30 Data API credits per node not yet fetched (cap 900 per call). Returns the stored SERP digests. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: 30 Data API credits per new node, cap 900 per call. Time: 10-60 seconds.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "30 Data API credits per new node, cap 900 per call", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"keywords": {"type": "array", "description": "Node keywords to check (max 30).", "items": {"type": "string"}}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": ["keywords"]}}}}}}, "/api/runs/{run_id}/reclassify": {"post": {"operationId": "reclassify_node", "summary": "Reclassify a node", "description": "Changes the role of one node and recomputes the whole result without credits. Roles: my (own brand), comp (a competitor brand, give brand), generic, compare, exit, channel. Returns the new result. Changes saved data in the app; no credits.", "tags": ["action"], "x-lm-kind": "action", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"keyword": {"type": "string", "description": "Node keyword."}, "role": {"type": "string", "description": "New role.", "enum": ["my", "comp", "generic", "compare", "exit", "channel"]}, "brand": {"type": "string", "description": "Competitor brand name, required when role is comp."}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": ["keyword", "role"]}}}}}}, "/api/runs/{run_id}/demote_brand": {"post": {"operationId": "demote_brand", "summary": "Demote a brand", "description": "Turns a name wrongly treated as a competitor (a platform or OS such as Android) into a category term: all its nodes become channel queries, it leaves the competitor list, and the result is recomputed. Your own brand cannot be demoted. No credits. Returns the new result. Changes saved data in the app; no credits.", "tags": ["action"], "x-lm-kind": "action", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"brand": {"type": "string", "description": "Brand name as shown in the result."}, "lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": ["brand"]}}}}}}, "/api/runs/{run_id}/prune_brands": {"post": {"operationId": "prune_brands", "summary": "Prune retailer and platform names", "description": "One LLM pass over the run's brand names: retailers, carriers, marketplaces and platforms stop being competitors and their nodes become channel queries; the result is recomputed. No Data API credits. Returns the new result. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: 1 LLM call, no Data API credits. Time: 10-40 seconds.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "1 LLM call, no Data API credits", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}], "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"lang": {"type": "string", "description": "Language of generated text: ko, ja or en.", "enum": ["ko", "ja", "en"], "default": "ko"}}, "required": []}}}}}}, "/api/runs/{run_id}/brief.md": {"get": {"operationId": "get_node_brief_markdown", "summary": "Node brief (Markdown)", "description": "Returns {keyword, markdown}: a one-page brief for a node already explained with explain_node in the same language. Free. 404 if not explained yet. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}, {"name": "keyword", "in": "query", "required": true, "description": "Journey node (keyword) that was explained with explain_node.", "schema": {"type": "string"}}, {"name": "lang", "in": "query", "required": false, "description": "Language the node explanation was generated in.", "schema": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko"}}]}}, "/runs/{run_id}/brief.html": {"get": {"operationId": "get_node_brief_html", "summary": "Node brief (print HTML)", "description": "Returns the printable HTML brief for an explained node. download=1 adds an attachment header. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}, {"name": "keyword", "in": "query", "required": true, "description": "Journey node (keyword) that was explained with explain_node.", "schema": {"type": "string"}}, {"name": "lang", "in": "query", "required": false, "description": "Language the node explanation was generated in.", "schema": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko"}}, {"name": "download", "in": "query", "required": false, "description": "1 to send as a file download, 0 to view inline.", "schema": {"type": "integer", "enum": [0, 1], "default": 0}}]}}, "/runs/{run_id}/brief.pdf": {"get": {"operationId": "download_node_brief_pdf", "summary": "Node brief PDF", "description": "Downloads the node brief as a PDF. Returns 503 when the server has no headless Chrome; then use get_node_brief_html. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}, {"name": "keyword", "in": "query", "required": true, "description": "Journey node (keyword) that was explained with explain_node.", "schema": {"type": "string"}}, {"name": "lang", "in": "query", "required": false, "description": "Language the node explanation was generated in.", "schema": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko"}}]}}, "/runs/{run_id}/report.md": {"get": {"operationId": "get_report_markdown", "summary": "Report (Markdown)", "description": "Returns the full run report as Markdown, including the watch lines and their stored wording. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/runs/{run_id}/preview.html": {"get": {"operationId": "get_report_preview", "summary": "Report preview", "description": "Returns the full run report as an HTML page for viewing. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/runs/{run_id}/report.html": {"get": {"operationId": "download_report_html", "summary": "Download report HTML", "description": "Downloads the full report as a self-contained HTML file. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/runs/{run_id}/report.pdf": {"get": {"operationId": "download_report_pdf", "summary": "Report PDF", "description": "Downloads the full report as PDF. Returns 503 when the server has no headless Chrome; then use get_report_preview. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/runs/{run_id}/leaks.csv": {"get": {"operationId": "download_leaks_csv", "summary": "Leaks CSV", "description": "Downloads the leak table as CSV (UTF-8 with BOM). Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/runs/{run_id}/run.json": {"get": {"operationId": "download_run_json", "summary": "Result JSON", "description": "Returns {result, raw, classes} for a finished run: the computed result, the raw sampled paths and node data, and the node classes. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/api/runs/{run_id}/rerun": {"post": {"operationId": "rerun_run", "summary": "Re-run with same inputs", "description": "Starts a new run with the same parameters as an earlier run. Returns {run_id, url, from}. Same cost as start_run minus cached calls. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: same as start_run (cached calls are free). Time: 2-7 minutes. Usually followed by: get_run.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "same as start_run (cached calls are free)", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id returned by start_run or list_runs (shown in the result URL /r/{run_id}).", "schema": {"type": "string"}}]}}, "/api/entities": {"get": {"operationId": "list_entities", "summary": "Brand and channel dictionary", "description": "Lists the learned name dictionary used to classify nodes: {items: [{name, gl, scope, type, source, updated_at}], types}. Types: brand, channel, platform, other. Free. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "gl", "in": "query", "required": false, "description": "Filter by market. Omit for all.", "schema": {"type": "string", "enum": ["kr", "jp", "us"]}}]}, "post": {"operationId": "add_entity", "summary": "Add dictionary entry", "description": "Adds or updates a name in the classification dictionary so future runs treat it as the given type. scope limits it to one seed keyword's category; empty scope means global. No credits. Changes saved data in the app; no credits.", "tags": ["action"], "x-lm-kind": "action", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"name": {"type": "string", "description": "Name, e.g. 쿠팡."}, "gl": {"type": "string", "description": "Market.", "enum": ["kr", "jp", "us"]}, "type": {"type": "string", "description": "Entry type.", "enum": ["brand", "channel", "platform", "other"]}, "scope": {"type": "string", "description": "Seed keyword whose category this entry applies to. Empty or omitted for global."}}, "required": ["name", "gl", "type"]}}}}}, "delete": {"operationId": "delete_entity", "summary": "Remove dictionary entry", "description": "Removes a name from the classification dictionary for a market and scope ('*' = all scopes). Deletes data. A person approves it first.", "tags": ["delete"], "x-lm-kind": "delete", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "name", "in": "query", "required": true, "description": "Name to remove.", "schema": {"type": "string"}}, {"name": "gl", "in": "query", "required": true, "description": "Market.", "schema": {"type": "string", "enum": ["kr", "jp", "us"]}}, {"name": "scope", "in": "query", "required": false, "description": "Scope to remove ('*' for every scope).", "schema": {"type": "string", "default": "*"}}]}}, "/api/recent": {"get": {"operationId": "list_recent", "summary": "Recent diagnoses", "description": "The three most recent finished runs shown on the start screen (run_id, seeds, my_brand, shares). Signed-in members only. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}}}, "/_lm/report-robots/{run_id}": {"get": {"operationId": "get_report_robots", "summary": "Report search visibility", "description": "Returns whether search engines may index this report page (index or noindex), and why: a company member's run or one set by hand is index; a customer's run, and any run that is still going, failed or came back empty, is noindex. Read only; no credits. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "The report's run id: the id in the report address (for ACO the project id, or project~step).", "schema": {"type": "string"}}]}}}, "externalDocs": {"url": "https://daas-funnel.listeningmind.ai/agents.md", "description": "Agent guide"}}