{"openapi":"3.0.1","info":{"title":"Martekio Public API","description":"The Martekio Public API provides programmatic access to AI Share of Voice analytics data.\nTrack how brands and competitors appear in responses from AI models (ChatGPT, Perplexity, Gemini, Anthropic, Grok),\nincluding citations, sentiment, prominence, and visibility trends.\n\n## Authentication\nAll endpoints require a Bearer token in the `Authorization` header.\nTokens use the format `mkto_selector.validator` and can be generated from the Martekio\ndashboard under My Profile > API Token. Tokens issued before the `mkto_` prefix was introduced\nremain valid with or without it.\n\n## Rate Limiting\nAPI requests are rate-limited. If you exceed the limit, you will receive a `429 Too Many Requests` response.\n\n## Maintenance Mode\nMartekio operates a kill switch on the public API. When the switch is\nactive, every endpoint returns `503 Service Unavailable` with `{\"error\": \"API is\ntemporarily disabled for maintenance\"}`. Treat `503` as\ntransient and back off; the switch is only used for short maintenance windows or incident\nresponse.\n\n## Changes in 1.3.0\n`best_provider` on a Radar analysis result is **removed** and replaced by two fields,\n`best_provider_branded` and `best_provider_nonbranded`, because one field could not\ndistinguish where a brand wins when a prompt names it from where it wins when the prompt\ndoes not. Either may be an empty string, meaning no assistant stood out, which is a normal\nanswer rather than a missing value and is common on the non-branded field. The `providers`\narray is now documented as being returned in canonical assistant order. There is no alias\nfor the old field: a client reading `best_provider` will find it absent.\n\n## Client Ownership\nClient-scoped endpoints (`/clients/{clientID}/...`) enforce ownership validation.\nYou can only access data for clients associated with your API token.\n","version":"1.3.0","contact":{"name":"Martekio Support","email":"support@martekio.com"}},"servers":[{"url":"https://api.martekio.com/api/v1","description":"Production"},{"url":"http://localhost:8089/api/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"tags":[{"name":"Industry","description":"Industry-level insights and domain data"},{"name":"Share of Voice","description":"Share of Voice metrics and historical trends"},{"name":"Performance","description":"Client performance data and averages"},{"name":"Competitors","description":"Competitor citations, trends, and groupings"},{"name":"Analysis","description":"Analysis summaries, details, responses, and dates"},{"name":"Prompt Check","description":"Run a single prompt through selected AI assistants in real time and get back the visibility insights (ephemeral, metered, AI Overview excluded)."},{"name":"Tekka","description":"Ask Tekka, the Martekio AI assistant, a question in plain language and get a written answer grounded in your own space's data. Metered per day."},{"name":"Responses","description":"Bulk and single-record access to AI replies with their citations. Designed for external tools that need the full response payload alongside citation URLs in one call."},{"name":"Citations","description":"Citation trends and URL-level data"},{"name":"Citation Consistency","description":"How consistently the brand appears in AI-generated responses across a trailing window of analyses, per provider and branding axis. Per-(session, prompt) BOOL_OR dedupe means a prompt counts at most once per session."},{"name":"Sentiment","description":"Sentiment score averages and trends"},{"name":"Agentic Traffic","description":"AI bot visit summaries and traffic distribution"},{"name":"AI Referral Traffic","description":"Human visits referred from AI chatbots (e.g. ChatGPT) detected via UTM parameters and referrer domains"},{"name":"Sources","description":"Influential sources tracking"},{"name":"Recommendations","description":"AI-generated recommendations for improving brand visibility"},{"name":"Feeds","description":"Product feed management. Refresh feed URLs, regenerate feed JSON, and download the merchant-facing feed file for ACP / Impact Engine workflows."},{"name":"Radar","description":"Martekio Radar: the metered, partner-issued AI visibility API. Submit a brand plus a set of prompts and get back, asynchronously, the same Share of Voice analysis Martekio runs in-app across the AI assistants enabled for the partner: six as standard (ChatGPT, Perplexity, Gemini, Anthropic, Google AI Mode, Google AI Overview), with Grok as a paid add-on. Authenticated with a per-partner key issued by Martekio (not a self-serve dashboard token); one prompt is deducted per prompt submitted from the key's annual allowance, whatever the number of assistants, so the assistant set changes what you get back rather than what you are charged. Read the set from the `providers` array in a result rather than assuming a fixed number.\n"}],"paths":{"/industry/insights":{"get":{"operationId":"getIndustryInsights","summary":"Get industry insights","description":"Returns industry data grouped by industry and AI provider.","tags":["Industry"],"parameters":[{"name":"industryCode","in":"query","description":"Restrict results to a single industry sector code.","schema":{"type":"integer"}}],"responses":{"200":{"description":"Industry insights grouped by provider","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/IndustryInsight"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/industry/top-domains":{"get":{"operationId":"getTopIndustryDomains","summary":"Get top cited domains per industry","description":"Returns the most-cited domains for each industry, grouped by AI provider.","tags":["Industry"],"parameters":[{"name":"country","in":"query","description":"Filter results by country code.","schema":{"type":"string"}},{"name":"industryCode","in":"query","description":"Restrict results to a single industry sector code.","schema":{"type":"integer"}}],"responses":{"200":{"description":"Top domains grouped by industry and provider","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/IndustryTopDomains"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/sov/metrics":{"get":{"operationId":"getSoVMetrics","summary":"Get Share of Voice metrics by provider","description":"Returns SoV metrics for a specific AI provider. If no date is specified, returns the latest available data.","tags":["Share of Voice"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/aiProviderQuery"},{"name":"analysis_date","in":"query","description":"Specific analysis date (YYYY-MM-DD). If omitted, returns the latest.","schema":{"type":"string","format":"date"}},{"name":"getLatestSoV","in":"query","description":"If `true`, forces retrieval of the latest data regardless of date.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"SoV metrics for the requested provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoVMetrics"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/sov/metrics/historic":{"get":{"operationId":"getSoVMetricsHistoric","summary":"Get historical SoV trends","description":"Returns SoV metrics over a date range, grouped by AI provider.","tags":["Share of Voice"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","required":true,"description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","required":true,"description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Historical SoV data grouped by provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoVHistoricResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/performance":{"get":{"operationId":"getClientPerformance","summary":"Get client performance data","description":"Returns performance data for a client, grouped by AI provider.\nOptionally filter by analysis date and/or include a comparison date.\n","tags":["Performance"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"analysis_date","in":"query","description":"Filter to a specific analysis date (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"comparison_date","in":"query","description":"Compare against a second date (YYYY-MM-DD). Requires `analysis_date`.","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Performance data grouped by provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerformanceResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/performance/all-time":{"get":{"operationId":"getClientPerformanceAllTime","summary":"Get all-time performance averages","description":"Returns all-time average performance metrics grouped by AI provider.","tags":["Performance"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"All-time performance averages by provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerformanceAllTimeResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/competitors":{"get":{"operationId":"getCompetitors","summary":"Get competitors by provider","description":"Returns the client's TRACKED competitors and their citation counts for a specific AI provider within a date range. Competitors are opt-in: only brands the client has chosen to track (via Curate Competitors) are returned; an untracked space yields an empty list.","tags":["Competitors"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/aiProviderQuery"},{"$ref":"#/components/parameters/startDate"},{"$ref":"#/components/parameters/endDate"},{"name":"includeClientBrand","in":"query","description":"Include the client's own brand in results","schema":{"type":"boolean"}}],"responses":{"200":{"description":"List of competitors with citation counts","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Competitor"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/competitors/curated":{"get":{"operationId":"getCuratedCompetitors","summary":"Get curated competitors","description":"Returns both of the space's curated competitor sets in one call. Tracked is the explicit set chosen for ongoing monitoring in Competition Explorer; Threats are the curated real rivals. The two are mutually exclusive, so they are returned together rather than as separate calls. Both lists are curated independently of citation volume and are ordered alphabetically. An uncurated space returns empty lists.","tags":["Competitors"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"The client's curated competitor sets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CuratedCompetitorsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/competitors/trends":{"get":{"operationId":"getCompetitorTrends","summary":"Get competitor citation trends","description":"Returns citation trends (branded vs non-branded) for the client's TRACKED competitors on a specific AI provider. Only brands the client has chosen to track are included.","tags":["Competitors"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/aiProviderQuery"},{"$ref":"#/components/parameters/startDate"},{"$ref":"#/components/parameters/endDate"},{"name":"includeClientBrand","in":"query","description":"Include the client's own brand in results","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Competitor trends with branded/non-branded split","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitorTrendsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/competitors/trends/grouped":{"get":{"operationId":"getCompetitorTrendsGrouped","summary":"Get competitors grouped as winners/losers","description":"Returns the client's CURATED competitors grouped into winners and losers across all AI providers. The curated set is the union of the client's Tracked brands and its Competitive Threats (the two mutually exclusive lists). Each row carries onTrackedList and onWatchList flags so callers can tell which set a competitor belongs to. Brands the client has neither tracked nor flagged as a threat are excluded.","tags":["Competitors"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"includeClientBrand","in":"query","description":"Include the client's own brand in results","schema":{"type":"boolean"}},{"name":"sort","in":"query","description":"Ranking mode. `cumulative` (default, empty also means cumulative) ranks by all-time\ncitation totals from the CompetitorCitationTrends running totals. `momentum` switches\nto a \"current vs prior window\" comparison computed live from all_citation_urls so\nrecent gainers / losers surface ahead of long-tail totals.\n","schema":{"type":"string","enum":["cumulative","momentum"],"default":"cumulative"}}],"responses":{"200":{"description":"Winners and losers grouped by provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitorTrendsGroupedResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/analysis/summaries":{"get":{"operationId":"getAnalysisSummaries","summary":"Get analysis summaries","description":"Returns analysis run summaries for a client, including provider-level totals.","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"Client profile and analysis summaries","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisSummariesResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/analysis/details":{"get":{"operationId":"getAnalysisDetails","summary":"Get analysis details (paginated)","description":"Returns detailed analysis results for a client. Supports pagination via `page` and\n`limit`, plus optional date-range filtering via `startDate`/`endDate`.\n\nDefault windowing rule: if neither date range nor pagination is supplied, the response\nis restricted to the trailing 30 days. Pass `page=1&limit=N` for paginated access to\nthe full history, or `startDate`/`endDate` to scope to a specific range.\n","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"page","in":"query","description":"Page number (1-based). Omit for all results.","schema":{"type":"integer","minimum":1}},{"name":"limit","in":"query","description":"Records per page (max 1000)","schema":{"type":"integer","minimum":1,"maximum":1000}},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD). Filters by analysis CreatedAt.","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD). Filters by analysis CreatedAt (inclusive of the end day).","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Analysis details with optional pagination metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisDetailsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/prompt-movers":{"get":{"operationId":"getPromptMovers","summary":"Get prompt movers (rising and falling citation rate)","description":"Ranks the client's prompts (tracked requests) by the change in citation rate, the\nshare of AI answers that cite the brand, between a trailing window and the previous\nequal window, cross-provider, with a per-assistant breakdown.\n\nThe change is reported in percentage points (`delta_points`): positive is improving,\nnegative is declining. Prompts with fewer than `min_answers` answers in either window\nare excluded, so a single answer never surfaces as a trend. The windows anchor to the\nspace's latest analysis run, so `recent_period`/`prior_period` return the exact dates\ncompared. Designed for BI dashboards and alert rules (\"notify when a prompt's citation\nrate drops 20 points\").\n","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"days","in":"query","description":"Trailing window in days for each side of the comparison (default 30).","schema":{"type":"integer","minimum":7,"maximum":90}},{"name":"min_answers","in":"query","description":"Minimum answers a prompt needs in BOTH windows to qualify (default 3).","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"direction","in":"query","description":"Filter to improving, declining, or all movers.","schema":{"type":"string","enum":["improving","declining","all"],"default":"all"}},{"name":"branded","in":"query","description":"Filter to branded prompts, non-branded prompts, or all.","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"limit","in":"query","description":"Cap the number of movers returned (omit for no cap).","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Ranked prompt movers with the comparison periods","content":{"application/json":{"schema":{"type":"object","properties":{"window_days":{"type":"integer"},"min_answers":{"type":"integer"},"recent_period":{"type":"object","properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"}}},"prior_period":{"type":"object","properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"}}},"count":{"type":"integer"},"movers":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"origin_keyword":{"type":"string"},"branded":{"type":"boolean"},"rate_current_pct":{"type":"number","description":"Share of answers citing the brand in the recent window (0..100)."},"rate_prior_pct":{"type":"number"},"delta_points":{"type":"number","description":"Change in citation rate in percentage points; positive = improving."},"trend":{"type":"string","enum":["surging","rising","stable","declining","disappearing"]},"answers_current":{"type":"integer"},"answers_prior":{"type":"integer"},"cited_current":{"type":"integer"},"cited_prior":{"type":"integer"},"providers":{"type":"array","items":{"type":"object","properties":{"ai_provider":{"type":"string"},"rate_current_pct":{"type":"number"},"rate_prior_pct":{"type":"number"},"delta_points":{"type":"number"},"trend":{"type":"string"}}}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/visibility-report-prompt":{"get":{"operationId":"getVisibilityReportPrompt","summary":"Get the Visibility Report prompt","description":"Returns the effective AI Visibility Report prompt for the client. Martekio publishes\nseveral named reports; `prompt` selects one by name and omitting it returns the\ndefault, so a call written before named reports existed keeps working unchanged.\n\nThe response is the space's own saved version of that report when it has one\n(`custom: true`), otherwise the report as Martekio wrote it (`custom: false`). The\nlatter still contains `martekio_*` placeholders; a saved version is already\npersonalised for the space. `available` lists every report name you can ask for.\n","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"prompt","in":"query","required":false,"schema":{"type":"string"},"description":"Which named report to return, e.g. `Default Long Form`. Matched case-insensitively. Omit for the default report."}],"responses":{"200":{"description":"The Visibility Report prompt","content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","description":"The report prompt text."},"promptName":{"type":"string","description":"The name of the report returned."},"custom":{"type":"boolean","description":"True when this is the space's own saved version of the report; false when it is the report as Martekio wrote it."},"available":{"type":"array","items":{"type":"string"},"description":"Every report name available for this client."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"No report carries the requested name. The body lists the `available` names."},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/analysis/responses/{queryID}":{"get":{"operationId":"getAIResponse","summary":"Get full AI response","description":"Returns the full AI-generated response text for a specific query.","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"queryID","in":"path","required":true,"description":"Unique identifier of the query","schema":{"type":"string"}}],"responses":{"200":{"description":"The full AI response text","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIResponseResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/analysis/dates":{"get":{"operationId":"getAnalysisDates","summary":"Get available analysis dates","description":"Returns a list of dates on which analysis was performed for this client.","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"Array of analysis dates","content":{"application/json":{"schema":{"type":"array","items":{"type":"string","format":"date"},"example":["2026-03-15","2026-03-14","2026-03-13"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/responses":{"get":{"operationId":"getAllResponses","summary":"Get all AI responses with citations","description":"Returns every non-archived AI response for the client, with each row carrying its full\nreply text, sentiment, brand presence flags, and the full citation URL list. Optional\nquery parameters narrow the result to a single session or single provider.\n\nThe payload is dense: prefer narrowing via `sessionId` or `aiProvider` for routine calls.\n\nPaged since 2026-09-29: `limit` (default 100, maximum 1000) and `offset` (default 0)\nselect a page ordered newest first; `total` is the number of rows that match the\nfilters, `returned` the number in this page. Page with `offset` until `offset +\nreturned` reaches `total`.\n","tags":["Responses"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"sessionId","in":"query","description":"Restrict results to one analysis session.","schema":{"type":"string"}},{"name":"aiProvider","in":"query","description":"Restrict results to one AI provider.","schema":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]}},{"name":"limit","in":"query","description":"Rows per page (default 100, maximum 1000; larger values are clamped).","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"offset","in":"query","description":"Rows to skip before the page (default 0).","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"One page of responses with citations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponsesListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/responses/{queryID}":{"get":{"operationId":"getResponseWithCitations","summary":"Get a single AI response with citations","description":"Returns one AI response identified by `queryID`, including the full reply text, brand\npresence flags, sentiment, and the ordered citation URL list. Use this when drilling\ndown from a list-level response into a single row.\n","tags":["Responses"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"queryID","in":"path","required":true,"description":"Unique identifier of the query.","schema":{"type":"string"}}],"responses":{"200":{"description":"Single response with citations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/search-queries":{"get":{"operationId":"getSearchQueries","summary":"Get fan-out web-search queries","description":"Returns the fan-out web-search queries an AI provider fired while generating per-keyword\nresponses for a client. Surfaces the long-tail of related searches the model considered,\neven when those queries did not appear in the final reply. Populated for providers whose\nintegrations expose their tool-use telemetry: ChatGPT-via-DFS, Anthropic, Gemini. Three\nresponse shapes:\n  - default (flat): one row per fan-out query.\n  - `group=prompt`: rows collapsed by parent prompt, queries[] preserves their original order.\n  - `dedupe=true`: identical (search_query, provider) pairs collapsed, sorted by\n    occurrence_count desc — useful for discovering canonical research questions about\n    an industry.\n","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"provider","in":"query","description":"Filter to a single provider. Omit to return all.","schema":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]}},{"name":"session_id","in":"query","description":"Exact session id to scope the lookup.","schema":{"type":"string"}},{"name":"prompt","in":"query","description":"Exact match on the parent prompt (the original keyword that triggered the fan-out).","schema":{"type":"string"}},{"name":"from","in":"query","description":"Lower bound on parent analysis_date (YYYY-MM-DD, inclusive).","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Upper bound on parent analysis_date (YYYY-MM-DD, inclusive).","schema":{"type":"string","format":"date"}},{"name":"group","in":"query","description":"`prompt` collapses rows by parent prompt. Omit (or `none`) for a flat list. Mutually\nexclusive with `dedupe=true`; when both are set, `dedupe` wins.\n","schema":{"type":"string","enum":["none","prompt"],"default":"none"}},{"name":"dedupe","in":"query","description":"When `true`, collapses identical (search_query, provider) pairs into a single row with\nan occurrence_count and a sample of up to 5 parent prompts that triggered it. Sorted\nby occurrence_count desc.\n","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","description":"Max rows to return. Default 100, max 1000.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"offset","in":"query","description":"Pagination offset.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Fan-out search queries (one of three shapes)","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"shape":{"type":"string","enum":["flat","grouped","deduped"]},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"queries":{"description":"Present when shape is `flat` or `deduped`.","type":"array","items":{"type":"object"}},"prompts":{"description":"Present when shape is `grouped`.","type":"array","items":{"type":"object"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/analysis/compare":{"post":{"operationId":"compareAnalyses","summary":"Compare two analysis sessions","description":"Returns the delta between two analysis sessions for one client: per-prompt changes in\ncitation and brand-citation counts, an aggregated summary, and optionally the diff of\nDataForSEO-extracted competitor brand entities (new vs lost across the two runs).\nDesigned for BI dashboards and alert rules (\"notify when brand citations drop >20%\nweek-over-week\"). The same prompt evaluated by multiple providers is compared\nper-provider; rows are keyed on (prompt, ai_provider).\n","tags":["Analysis"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["session_id_a","session_id_b"],"properties":{"session_id_a":{"type":"string","description":"Baseline session id."},"session_id_b":{"type":"string","description":"Comparison session id. Must differ from session_id_a."},"providers":{"type":"array","description":"Restrict comparison to a subset of providers. Omit for all.","items":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","GoogleAIMode","GoogleAIOverview","Grok"]}},"changed_only":{"type":"boolean","description":"When true, omits rows where citations_delta, brand_citations_delta, and\nbrand_cited are all unchanged. Default false.\n","default":false},"include_competitors":{"type":"boolean","description":"When true, includes new_competitors and lost_competitors in the summary,\ndiffed from analysis_detail_brand_entities. Currently populated only by the\nChatGPT-via-DFS route; the diff is empty when neither session was routed\nvia DFS. Default false.\n","default":false}}}}}},"responses":{"200":{"description":"Per-prompt comparison plus summary header","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"session_a":{"type":"object","properties":{"session_id":{"type":"string"},"analysis_date":{"type":"string","format":"date"},"is_partial":{"type":"boolean"}}},"session_b":{"type":"object","properties":{"session_id":{"type":"string"},"analysis_date":{"type":"string","format":"date"},"is_partial":{"type":"boolean"}}},"providers":{"type":"array","items":{"type":"string"}},"changed_only":{"type":"boolean"},"summary":{"type":"object","properties":{"prompts_a":{"type":"integer"},"prompts_b":{"type":"integer"},"prompts_in_both":{"type":"integer"},"total_citations_a":{"type":"integer"},"total_citations_b":{"type":"integer"},"total_citations_delta":{"type":"integer"},"brand_citations_a":{"type":"integer"},"brand_citations_b":{"type":"integer"},"brand_citations_delta":{"type":"integer"},"new_competitors":{"type":"array","items":{"type":"string"}},"lost_competitors":{"type":"array","items":{"type":"string"}}}},"by_prompt":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"ai_provider":{"type":"string"},"a_present":{"type":"boolean"},"b_present":{"type":"boolean"},"citations_a":{"type":"integer"},"citations_b":{"type":"integer"},"citations_delta":{"type":"integer"},"brand_citations_a":{"type":"integer"},"brand_citations_b":{"type":"integer"},"brand_citations_delta":{"type":"integer"},"brand_cited_a":{"type":"boolean"},"brand_cited_b":{"type":"boolean"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/ai-overview/attendance":{"get":{"operationId":"getAIOverviewAttendance","summary":"Get AI Overview attendance trend","description":"Returns the time series of how often Google rendered an AI Overview for this client's\nqueries. A metric unique to Martekio: Google chooses to render AI Overview for roughly\nhalf of queries, and which half varies by prompt intent, locale, and ranking landscape.\nTracking attendance over time surfaces shifts in coverage well before they show up in\ntraditional ranking reports. The `is_partial` flag on each bucket marks dates where the\nDataForSEO async queue timed out before all tasks were retrieved — a 0% rendered_pct on\na partial day reflects DFS infrastructure, not Google's render decision.\n","tags":["AI Overview"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","description":"Lower bound on analysis_date (YYYY-MM-DD, inclusive). Defaults to trailing 30 days.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Upper bound on analysis_date (YYYY-MM-DD, inclusive). Defaults to today (UTC).","schema":{"type":"string","format":"date"}},{"name":"granularity","in":"query","description":"Bucket width.","schema":{"type":"string","enum":["day","week","month"],"default":"day"}},{"name":"branded","in":"query","description":"Filter to one branding axis. `all` (default) returns both.","schema":{"type":"string","enum":["true","false","all"],"default":"all"}}],"responses":{"200":{"description":"AI Overview attendance trend","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"granularity":{"type":"string"},"branded":{"type":"string"},"series":{"type":"array","items":{"type":"object","properties":{"period_start":{"type":"string","format":"date"},"total_queries":{"type":"integer"},"rendered_count":{"type":"integer"},"rendered_pct":{"type":"number","format":"float"},"is_partial":{"type":"boolean"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/ai-overview/attendance/prompts":{"get":{"operationId":"getAIOverviewAttendancePrompts","summary":"Get per-prompt AI Overview attendance","description":"For each prompt the client ran in the window, returns how often Google rendered an AI\nOverview. Useful for surfacing prompts that reliably trigger AIO (a vulnerability for\ncitation steering) and prompts that never do (where AIO is not a SoV signal).\n","tags":["AI Overview"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"branded","in":"query","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"sort","in":"query","description":"Sort order for the returned prompts.","schema":{"type":"string","enum":["rendered_pct_desc","rendered_pct_asc","total_queries_desc","total_queries_asc"],"default":"rendered_pct_desc"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}}],"responses":{"200":{"description":"Per-prompt attendance list","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"branded":{"type":"string"},"limit":{"type":"integer"},"prompts":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"is_prompt_branded":{"type":"boolean"},"total_queries":{"type":"integer"},"rendered_count":{"type":"integer"},"rendered_pct":{"type":"number","format":"float"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/ai-overview/citations":{"get":{"operationId":"getAIOverviewCitations","summary":"Get AI Overview citation URLs","description":"Returns the citation URLs Google's AI Overview box linked to for the client's tracked\nprompts, with the full ownership / competitor / influential-source classification from\nthe citation pipeline. Unlike chatbot citations these come from real Google SERPs, so\nthey are the closest thing to consumer-truth visibility.\n","tags":["AI Overview"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","description":"Lower bound (YYYY-MM-DD, inclusive). Defaults to trailing 30 days.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Upper bound (YYYY-MM-DD, inclusive). Defaults to today (UTC).","schema":{"type":"string","format":"date"}},{"name":"branded","in":"query","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"domain","in":"query","description":"Substring filter on the citation URL.","schema":{"type":"string"}},{"name":"ownedOnly","in":"query","description":"When true, limits results to client-owned citations.","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":1000}}],"responses":{"200":{"description":"AI Overview citations","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"branded":{"type":"string"},"count":{"type":"integer"},"citations":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"url":{"type":"string"},"position_rank":{"type":"integer"},"is_prompt_branded":{"type":"boolean"},"is_client_owned":{"type":"boolean"},"is_competition":{"type":"boolean"},"is_influential_source":{"type":"boolean"},"competitor_name":{"type":"string"},"path_contains_brand":{"type":"boolean"},"analysis_date":{"type":"string","format":"date-time"},"session_id":{"type":"string"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/serp/questions":{"get":{"operationId":"getSerpQuestions","summary":"Get People Also Ask questions","description":"Returns the distinct People Also Ask questions Google surfaced alongside the client's\ntracked prompts, deduplicated to their most recent occurrence. Each question carries the\nanswer source where Google attributed one, classified against the client's brand\nwebsites (`answered_by`: own | other | none). These are real user questions and make\nstrong candidates for new custom prompts.\n","tags":["SERP Capture"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"keyword","in":"query","description":"Exact seed keyword (case-insensitive).","schema":{"type":"string"}},{"name":"prompt","in":"query","description":"Exact analysed prompt (case-insensitive).","schema":{"type":"string"}},{"name":"branded","in":"query","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"answeredBy","in":"query","description":"Filter by who owns the answer source domain.","schema":{"type":"string","enum":["own","other","none","all"],"default":"all"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":1000}}],"responses":{"200":{"description":"People Also Ask questions","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"branded":{"type":"string"},"answered_by":{"type":"string"},"count":{"type":"integer"},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string"},"answer_domain":{"type":"string"},"answer_url":{"type":"string"},"answer_text":{"type":"string"},"prompt":{"type":"string"},"keyword":{"type":"string"},"is_branded":{"type":"boolean"},"answered_by":{"type":"string","enum":["own","other","none"]},"occurrences":{"type":"integer"},"first_seen":{"type":"string","format":"date-time"},"last_seen":{"type":"string","format":"date-time"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/serp/organic":{"get":{"operationId":"getSerpOrganic","summary":"Get organic blue-link results","description":"Returns the organic \"blue link\" results captured alongside the AI Overview for the\nclient's tracked prompts: a rank tracker scoped to exactly the prompts tracked for AI\nvisibility. Each link carries the client-ownership classification and whether the same\ndomain was cited inside the AI Overview for that query, so \"you rank #3 but the AI\nOverview cites someone else\" is a single API call.\n","tags":["SERP Capture"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"keyword","in":"query","schema":{"type":"string"}},{"name":"prompt","in":"query","schema":{"type":"string"}},{"name":"branded","in":"query","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"domain","in":"query","description":"Substring filter on the result domain.","schema":{"type":"string"}},{"name":"ownedOnly","in":"query","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":1000}}],"responses":{"200":{"description":"Organic results","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"branded":{"type":"string"},"owned_only":{"type":"boolean"},"count":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"keyword":{"type":"string"},"rank":{"type":"integer"},"url":{"type":"string"},"domain":{"type":"string"},"title":{"type":"string"},"snippet":{"type":"string"},"is_branded":{"type":"boolean"},"is_client_owned":{"type":"boolean"},"ai_overview_rendered":{"type":"boolean"},"cited_in_ai_overview":{"type":"boolean"},"seen_at":{"type":"string","format":"date-time"},"session_id":{"type":"string"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/serp/products":{"get":{"operationId":"getSerpProducts","summary":"Get popular products carousel entries","description":"Returns the Google shopping carousel (\"popular products\") entries captured for the\nclient's tracked prompts, with seller, price, and rating where Google provides them:\ncompetitor shelf placement on the SERPs that matter to the brand.\n","tags":["SERP Capture"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"keyword","in":"query","schema":{"type":"string"}},{"name":"prompt","in":"query","schema":{"type":"string"}},{"name":"branded","in":"query","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"seller","in":"query","description":"Substring filter on the seller name.","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":1000}}],"responses":{"200":{"description":"Popular products","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"branded":{"type":"string"},"count":{"type":"integer"},"products":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"keyword":{"type":"string"},"rank":{"type":"integer"},"title":{"type":"string"},"description":{"type":"string"},"seller":{"type":"string"},"price_current":{"type":"number","nullable":true},"price_displayed":{"type":"string"},"currency":{"type":"string"},"rating_value":{"type":"number","nullable":true},"rating_votes":{"type":"integer","nullable":true},"is_branded":{"type":"boolean"},"seen_at":{"type":"string","format":"date-time"},"session_id":{"type":"string"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/serp/visibility-matrix":{"get":{"operationId":"getSerpVisibilityMatrix","summary":"Get the per-prompt SERP visibility matrix","description":"Classifies each tracked prompt by how the client shows up on the Google SERP, using the\nlatest AI Overview query per prompt in the window. Classifications: `fully_visible`\n(AI Overview rendered, client cited in it AND ranked organically), `rank_not_converted`\n(ranked organically but the rendered Overview does not cite the client),\n`ai_only` (cited in the Overview without an organic ranking), `invisible` (Overview\nrendered, client neither cited nor ranked), `no_ai_overview` (Google rendered no AI\nOverview for the prompt). The `rank_not_converted` bucket is the actionable one: pages\nthat rank but are not being chosen as Overview sources.\n","tags":["SERP Capture"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","schema":{"type":"string","format":"date"}},{"name":"branded","in":"query","schema":{"type":"string","enum":["true","false","all"],"default":"all"}}],"responses":{"200":{"description":"Visibility matrix","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"branded":{"type":"string"},"count":{"type":"integer"},"summary":{"type":"object","additionalProperties":{"type":"integer"},"description":"Prompt counts per classification bucket."},"prompts":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string"},"is_branded":{"type":"boolean"},"ai_overview_rendered":{"type":"boolean"},"owned_organic_rank":{"type":"integer","nullable":true},"cited_in_ai_overview":{"type":"boolean"},"classification":{"type":"string","enum":["fully_visible","rank_not_converted","ai_only","invisible","no_ai_overview"]},"seen_at":{"type":"string","format":"date-time"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/citations/trends":{"get":{"operationId":"getCitationTrends","summary":"Get citation trends","description":"Returns citation trend data with source breakdowns and provider-level insights.\nSupports filtering by competitor, industry, date range, and citation type.\n","tags":["Citations"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"client","in":"query","description":"Client ID filter","schema":{"type":"string"}},{"name":"competitor","in":"query","description":"Filter to a specific competitor name","schema":{"type":"string"}},{"name":"industry","in":"query","description":"Filter by industry code","schema":{"type":"string"}},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"filter","in":"query","description":"Citation type filter","schema":{"type":"string","enum":["my_brand","competitors","brand_in_url","branded","non_branded"]}},{"name":"limit","in":"query","description":"Maximum number of raw rows to process","schema":{"type":"integer"}}],"responses":{"200":{"description":"Citation trends with provider insights and top URLs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationTrendsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/citations/by-query/{queryID}":{"get":{"operationId":"getCitationsByQuery","summary":"Get citations for a query","description":"Returns all citation URLs associated with a specific analysis query.","tags":["Citations"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"queryID","in":"path","required":true,"description":"Unique identifier of the query","schema":{"type":"string"}}],"responses":{"200":{"description":"List of citation URLs with metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationsByQueryResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/citations/by-session/{sessionID}":{"get":{"operationId":"getCitationsBySession","summary":"Get top competitors in a session","description":"Returns the top competitors by citation count within an analysis session.","tags":["Citations"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"sessionID","in":"path","required":true,"description":"Unique identifier of the analysis session","schema":{"type":"string"}}],"responses":{"200":{"description":"Competitors by citation count, grouped by provider","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SessionCompetitorGroup"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/citations/domains":{"get":{"operationId":"getCitationDomains","summary":"Get cited domains with counts","description":"Returns every distinct cited domain for the client with its total citation count, ordered\nby count descending. Useful for building \"most-cited sources\" rollups outside of\n`/citations/trends`. Optional query parameters narrow the result to a single session or\nsingle provider.\n","tags":["Citations"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"sessionId","in":"query","description":"Restrict counts to one analysis session.","schema":{"type":"string"}},{"name":"aiProvider","in":"query","description":"Restrict counts to one AI provider.","schema":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]}}],"responses":{"200":{"description":"Cited domains with counts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationDomainsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/consistency/trends":{"get":{"operationId":"getCitationConsistencyTrend","summary":"Get citation consistency trend","description":"Returns the per-(session, provider, branding-axis) citation consistency time series for one\nclient. Each anchor session carries a score computed against the trailing `window`\nanalyses; scores are floats from 0.0 (never cited / mentioned) to 1.0 (cited / mentioned\nin every analysis in the window). Use this endpoint to pipe consistency into BI tools,\nscheduled reports, or custom dashboards.\n","tags":["Citation Consistency"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"window","in":"query","description":"Trailing N analyses each anchor's score is computed over. Default 8 (~4 weeks at the\nplatform's standard 2x/week cadence). Schedule-invariant — the same `window` value\nproduces comparable scores across clients regardless of their analysis frequency.\n","schema":{"type":"integer","minimum":1,"default":8}},{"name":"provider","in":"query","description":"Filter to a single provider. Omit to return all four.","schema":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]}},{"name":"branded","in":"query","description":"Filter to one branding axis. `all` (default) returns both.","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"startDate","in":"query","description":"Anchor date lower bound (YYYY-MM-DD, inclusive).","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"Anchor date upper bound (YYYY-MM-DD, inclusive).","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Citation consistency trend points","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationConsistencyTrendResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/consistency/prompts":{"get":{"operationId":"getCitationConsistencyPrompts","summary":"Get per-prompt citation consistency detail","description":"Returns every (prompt, provider, branding-axis) tuple in the trailing window for one\nclient, with per-prompt consistency scores plus full-window binary patterns aligned 1:1\nwith analysis date and session_id arrays. Pattern cells carry one of three values:\n`1` = brand was cited (or mentioned), `0` = prompt ran but brand was not cited / mentioned,\n`-1` = prompt did not run in that analysis (window slot present, no analysis_details row).\nUse this endpoint to build custom drill-downs, score buckets client-side, or feed analysis\ninto a data warehouse.\n","tags":["Citation Consistency"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"window","in":"query","description":"Trailing N analyses. Default 8.","schema":{"type":"integer","minimum":1,"default":8}},{"name":"branded","in":"query","description":"Filter to one branding axis. `all` (default) returns both.","schema":{"type":"string","enum":["true","false","all"],"default":"all"}},{"name":"providers","in":"query","description":"Comma-separated subset of providers. Omit to include all four.\nExample: `Anthropic,ChatGPT`.\n","schema":{"type":"string"}}],"responses":{"200":{"description":"Per-prompt consistency rows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationConsistencyPromptsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/sentiment/averages":{"get":{"operationId":"getSentimentAverages","summary":"Get sentiment score averages","description":"Returns overall and per-provider average sentiment scores for a client. By default the\naverages are computed across the client's full history; pass `getLatest=true` to limit\nthe average to the most recent analysis session.\n","tags":["Sentiment"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"getLatest","in":"query","description":"When `true`, restricts the average to the most recent analysis session.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Sentiment averages overall and by provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SentimentAveragesResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/sentiment/scores":{"get":{"operationId":"getSentimentScores","summary":"Get sentiment score trends","description":"Returns daily sentiment scores over a date range, grouped by AI provider.","tags":["Sentiment"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/startDate"},{"$ref":"#/components/parameters/endDate"}],"responses":{"200":{"description":"Sentiment scores by date, grouped by provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SentimentScoresResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/agentic/summaries":{"get":{"operationId":"getAgenticSummaries","summary":"Get AI bot visit summaries","description":"Returns daily visit counts by AI bot name within an optional date range.","tags":["Agentic Traffic"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Bot visit summaries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AIBotSummary"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/agentic/distribution":{"get":{"operationId":"getAgenticDistribution","summary":"Get bot visit distribution","description":"Returns aggregated visit counts per bot over an optional date range.","tags":["Agentic Traffic"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Visit distribution by bot","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BotDistribution"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/agentic/proportion":{"get":{"operationId":"getTrafficProportion","summary":"Get AI vs non-AI traffic proportion","description":"Returns a breakdown of traffic between AI bots and non-AI traffic.","tags":["Agentic Traffic"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Traffic proportion breakdown","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TrafficProportion"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/referrals/summaries":{"get":{"operationId":"getAIReferralSummaries","summary":"Get AI referral visit summaries","description":"Returns daily human visit counts from AI chatbot referrals, grouped by source.","tags":["AI Referral Traffic"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Daily referral summaries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AIReferralSummary"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/referrals/distribution":{"get":{"operationId":"getAIReferralDistribution","summary":"Get AI referral source distribution","description":"Returns aggregated visit counts per AI referral source over an optional date range.","tags":["AI Referral Traffic"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Referral distribution by source","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReferralDistribution"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/referrals/top-pages":{"get":{"operationId":"getAIReferralTopPages","summary":"Get top pages receiving AI referral traffic","description":"Returns the top pages receiving human visits from AI chatbot referrals, grouped by URL and source. Limited to 100 results.","tags":["AI Referral Traffic"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Top pages by referral visit count","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReferralTopPage"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/performance/summary":{"get":{"operationId":"getPerformanceSummary","summary":"Get performance summary","description":"Returns aggregated performance metrics for the requested window, broken down by AI provider,\nplus an `overall` row covering every provider together.\nIncludes citation rate, mention rate, sentiment score, and Share of Voice.\nIf no date parameters are provided, returns all-time figures.\n\nHow the figures are aggregated:\n\n* **Share of Voice** (`totalSOV`, `brandedSOV`, `nonBrandedSOV`) is pooled from the underlying\n  citation counts: total brand citations divided by total citations across the window. It is not\n  an average of per-analysis percentages, so a session with 500 citations counts for more than one\n  with 3. The same pooling produces the `overall` row, which is therefore not the mean of the\n  per-provider values. Analyses that returned no citations at all contribute nothing rather than\n  counting as a zero.\n* **Citation rate, mention rate and sentiment** are means of per-analysis values. In the `overall`\n  row each provider is weighted by `analysisCount`, so providers with more analyses carry more of\n  the result.\n* `analysisCount` on the `overall` row is the total number of analyses behind it, not the number\n  of providers.\n","tags":["Performance"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"startDate","in":"query","description":"Start of date range (YYYY-MM-DD). Optional, omit for all-time data.","schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","description":"End of date range (YYYY-MM-DD). Optional, omit for all-time data.","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Performance summary per provider, plus a pooled overall row","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerformanceSummaryResponse"},"example":{"overall":{"aiProvider":"All","citationRate":50.71,"mentionRate":56.06,"sentimentScore":0.66,"totalSOV":21.41,"brandedSOV":43.01,"nonBrandedSOV":1.61,"analysisCount":187},"providers":[{"aiProvider":"ChatGPT","citationRate":44.05,"mentionRate":51.89,"sentimentScore":0.63,"totalSOV":17.9,"brandedSOV":42.5,"nonBrandedSOV":0.36,"analysisCount":43}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/recommendations":{"get":{"operationId":"getRecommendations","summary":"Get AI recommendations","description":"Returns active (non-archived, non-dismissed) AI-generated recommendations for improving\nthe client's brand visibility in AI responses. Recommendations are sorted by priority\n(1 = most urgent). Each recommendation includes an action, supporting context, type,\nand whether it has been added to the backlog.\n\nUse the optional `type` query parameter to return only certain recommendation types\n(single value or comma-separated list). Valid types: `content_gap`, `citation_improvement`,\n`competitor_insight`, `sentiment_improvement`, `ranking_opportunity`.\n","tags":["Recommendations"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/recommendationTypeFilter"}],"responses":{"200":{"description":"List of active recommendations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Recommendation"}},"example":[{"id":1,"recommendationAction":"Create dedicated product education content targeting non-branded queries","recommendationText":"ChatGPT shows only 2 non-branded citations vs 620 competitor citations.","recommendationType":"citation_improvement","priority":1,"aiProviderContext":"All","isDismissed":false,"isArchived":false,"isOnBacklog":true}]}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/recommendations/all":{"get":{"operationId":"getAllRecommendations","summary":"Get all recommendations (full history)","description":"Returns the client's FULL recommendation history, newest first, including\narchived and dismissed entries (unlike /recommendations, which returns only the\nactive, latest set). Each item carries the analysis sessionId and createdAt so\nyou can see which run produced it. Internal audit fields are not exposed.\n\nUse the optional `type` query parameter to return only certain recommendation types\n(single value or comma-separated list). Valid types: `content_gap`, `citation_improvement`,\n`competitor_insight`, `sentiment_improvement`, `ranking_opportunity`.\n","tags":["Recommendations"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/recommendationTypeFilter"}],"responses":{"200":{"description":"Full recommendation history","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendationHistory"},"example":{"total":2,"recommendations":[{"id":168,"sessionId":"a1b2c3","createdAt":"2026-06-16","recommendationAction":"Create dedicated product education content targeting non-branded queries","recommendationText":"ChatGPT shows only 2 non-branded citations vs 620 competitor citations.","recommendationType":"citation_improvement","priority":1,"aiProviderContext":"All","isDismissed":false,"isArchived":false,"isOnBacklog":true},{"id":120,"sessionId":"z9y8x7","createdAt":"2026-06-02","recommendationAction":"Improve structured data on key product pages","recommendationText":"Competitor pages expose richer schema markup.","recommendationType":"content_gap","priority":2,"aiProviderContext":"ChatGPT,Perplexity","isDismissed":false,"isArchived":true,"isOnBacklog":false}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/recommendations/overview":{"get":{"operationId":"getRecommendationsOverview","summary":"Get the recommendations overview (all datasets in one call)","description":"Convenience endpoint that returns all four recommendation datasets in a single\nresponse: the latest (active) recommendations, the full recommendation history,\nand the Visibility Backlog. Equivalent to calling\n/recommendations, /recommendations/all, and /backlog\ntogether. Use this when you want everything in one round-trip; the four\nstandalone endpoints remain available when you only need one dataset.\n","tags":["Recommendations"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"All recommendation datasets for the client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendationsOverview"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/impact-actions":{"get":{"operationId":"getImpactActions","summary":"Get Impact Actions","description":"Returns the client's recommendations GROUPED by the work they describe and ranked\nby how many SEPARATE analyses reached the same conclusion. Use this instead of\n/recommendations when the question is what to prioritise or what keeps coming\nback: /recommendations returns one analysis's list, this returns the durable\npicture across every analysis.\n\nEach action carries `sessionCount`, the ranking key, with `firstSeen` and\n`lastSeen` dates and a `lane` of recurring, new or unconfirmed.\n\nRead `sessionCount` against `analysisCount`: raised by 3 analyses is strong out\nof 4 and weak out of 30. Treat it as evidence only when `rankingEnabled` is\ntrue; below `minToRank` analyses there is not enough history for recurrence to\nmean anything.\n\nWhether an action has been added to the Visibility Backlog is NOT reported here;\n/backlog is where a caller asks what has been picked up.\n","tags":["Recommendations"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"The client's impact actions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpactActionsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/backlog":{"get":{"operationId":"getBacklog","summary":"Get the Visibility Backlog","description":"Returns every item in the client's Visibility Backlog, newest first. Backlog\nitems come from recommendations, weekly tips, or manual entries (see source).\nRecommendation-sourced items are annotated with the originating recommendation's\nAI provider context. Completed tasks carry completedOn (date) and completedBy\n(the name of the user who marked the task complete). Internal collaborator\nnotes are not exposed.\n","tags":["Recommendations"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"The client's backlog items","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Backlog"},"example":{"total":2,"backlog":[{"id":51,"source":"recommendation","sourceId":168,"sessionId":"a1b2c3","title":"Create dedicated product education content targeting non-branded queries","description":"ChatGPT shows only 2 non-branded citations vs 620 competitor citations.","category":"citation_improvement","aiProviderContext":"All","isCompleted":false,"createdAt":"2026-06-17"},{"id":49,"source":"manual","title":"Draft Q3 content calendar","description":"Owner: content team.","category":"Planning","isCompleted":true,"completedOn":"2026-06-24","completedBy":"Jane Smith","createdAt":"2026-06-10"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/influential-sources":{"get":{"operationId":"getInfluentialSources","summary":"Get influential sources","description":"Returns the list of influential sources configured for a client.","tags":["Sources"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"client","in":"query","required":true,"description":"Client ID","schema":{"type":"string"}}],"responses":{"200":{"description":"List of influential sources","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InfluentialSource"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/influential-sources/breakdown":{"get":{"operationId":"getInfluentialSourcesBreakdown","summary":"Get influential sources breakdown (snapshot)","description":"Ranks the client's ACTIVE influential sources (the user-curated list from Manage\nInfluential Sources) by how many AI responses cited each across a date range. Each row\nreports the count of responses citing the source (exact domain or any subdomain) plus\nthat count as a percentage of all matching responses. Sources with zero hits are still\nincluded so the response is stable across runs. Useful for \"where is the AI getting its\ninformation about my industry\" reports.\n","tags":["Sources"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","description":"Lower bound on analysis_date (YYYY-MM-DD, inclusive). Defaults to trailing 30 days.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Upper bound on analysis_date (YYYY-MM-DD, inclusive). Defaults to today (UTC).","schema":{"type":"string","format":"date"}},{"name":"provider","in":"query","description":"Filter to a single provider. Omit to roll up across all six.","schema":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","GoogleAIMode","GoogleAIOverview","Grok"]}}],"responses":{"200":{"description":"Source breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"provider":{"type":"string"},"total_responses":{"type":"integer"},"sources":{"type":"array","description":"One row per active influential source, ranked by responses_citing descending.","items":{"$ref":"#/components/schemas/InfluentialSourceCount"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/influential-sources/trends":{"get":{"operationId":"getInfluentialSourcesTrends","summary":"Get influential sources trends (time series)","description":"Same data as `/influential-sources/breakdown` but aggregated by date so consumers can plot\n\"Reddit citations over time\" without doing the rollup themselves. One bucket per period of\nthe chosen granularity; every bucket lists every active source (zero-filled), so series\nlengths never vary between buckets.\n","tags":["Sources"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"from","in":"query","description":"Lower bound on analysis_date (YYYY-MM-DD, inclusive). Defaults to trailing 30 days.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Upper bound on analysis_date (YYYY-MM-DD, inclusive). Defaults to today (UTC).","schema":{"type":"string","format":"date"}},{"name":"provider","in":"query","description":"Filter to a single provider. Omit to roll up across all six.","schema":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","GoogleAIMode","GoogleAIOverview","Grok"]}},{"name":"granularity","in":"query","description":"Bucket width.","schema":{"type":"string","enum":["day","week","month"],"default":"day"}}],"responses":{"200":{"description":"Source trend series","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"provider":{"type":"string"},"granularity":{"type":"string"},"series":{"type":"array","items":{"type":"object","properties":{"period_start":{"type":"string","format":"date"},"total_responses":{"type":"integer"},"sources":{"type":"array","description":"Every active influential source, zero-filled, ranked by responses_citing descending within the bucket.","items":{"$ref":"#/components/schemas/InfluentialSourceCount"}}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clients/{clientID}/feeds/refresh":{"post":{"operationId":"refreshClientFeeds","summary":"Refresh all client feeds","description":"Regenerates the feed JSON for every eligible feed belonging to the client. \"Eligible\"\nmatches the dispatcher's daily refresh contract: active feeds with at least one URL\nand a known platform.\n\nOptional query parameters:\n- `includeUrls=true` first re-discovers each feed's URL list (sitemap/crawl/upload)\n  using the parameters stored on the set, then regenerates the feed JSON. End-to-end\n  \"make every feed fresh\" in one call.\n- `format=json|csv|tsv` returns the merged feed content in the response body. Without\n  a `format`, the response is a JSON status summary.\n","tags":["Feeds"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"name":"includeUrls","in":"query","description":"When true, re-discover each feed's URL list before regenerating.","schema":{"type":"boolean","default":false}},{"name":"format","in":"query","description":"Return the merged feed content in the response body instead of a summary.","schema":{"type":"string","enum":["json","csv","tsv"]}}],"responses":{"200":{"description":"Feed refresh completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedRefreshSummary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/FeedGenerationDisabled"}}}},"/clients/{clientID}/feeds/{feedId}/refresh":{"post":{"operationId":"refreshClientFeed","summary":"Refresh a single feed","description":"Regenerates the feed JSON for a single feed belonging to the client, leaving every\nother feed untouched. The `feedId` path parameter accepts either the customer-facing\nfeed GUID (shown on the feed card as \"fid\") or the internal integer set ID.\n\nOptional `includeUrls=true` first re-discovers the URL list for this feed using its\nconfigured discovery method, then regenerates the feed JSON.\n","tags":["Feeds"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/feedId"},{"name":"includeUrls","in":"query","description":"When true, re-discover the URL list before regenerating.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Feed regeneration completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedRegenResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"502":{"description":"Feed generation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedRegenResult"}}}},"503":{"$ref":"#/components/responses/FeedGenerationDisabled"}}}},"/clients/{clientID}/feeds/refresh-urls":{"post":{"operationId":"refreshClientFeedUrls","summary":"Refresh URL lists for all client feeds","description":"Re-discovers URLs for every eligible URL set belonging to the client. Replaces each\nset's URL list using the discovery method recorded at creation (sitemap, crawl, or\nupload). This refreshes the URL LIST only, not the feed JSON. To regenerate the feed\nafterwards, call POST `/clients/{clientID}/feeds/refresh`.\n","tags":["Feeds"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"responses":{"200":{"description":"URL list refresh completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/URLRefreshSummary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/FeedGenerationDisabled"}}}},"/clients/{clientID}/feeds/{feedId}/refresh-urls":{"post":{"operationId":"refreshClientFeedUrlsForSet","summary":"Refresh URL list for a single feed","description":"Re-discovers URLs for a single feed belonging to the client. Per-feed form for callers\nthat want fine-grained control. Refreshes the URL LIST only; the feed JSON is not\nregenerated. Call POST `/clients/{clientID}/feeds/{feedId}/refresh` afterwards to\nregenerate the feed.\n","tags":["Feeds"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/feedId"}],"responses":{"200":{"description":"URL list refresh completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/URLRefreshResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/FeedGenerationDisabled"}}}},"/clients/{clientID}/feeds/{feedId}/download":{"get":{"operationId":"downloadClientFeed","summary":"Download a feed as a JSON file","description":"Serves the stored feed JSON for a single feed as a downloadable file. Does NOT\nregenerate; returns whatever the last successful generation left in the feed. Call\nPOST `/clients/{clientID}/feeds/{feedId}/refresh` first if a fresh feed is required.\n\nBrowser callers see a \"save file\" prompt thanks to `Content-Disposition: attachment`.\nCLI callers (curl, scripts) get a plain JSON body that can be piped to a file.\n\nResponse headers include `X-Feed-Id`, `X-Feed-Guid`, and `X-Feed-Product-Count` so\nautomation pipelines can record the feed metadata alongside the saved file.\n","tags":["Feeds"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/feedId"}],"responses":{"200":{"description":"Feed JSON file","headers":{"Content-Disposition":{"description":"Attachment filename (slugified label, falling back to feed GUID).","schema":{"type":"string"}},"X-Feed-Id":{"description":"Internal integer feed set ID.","schema":{"type":"string"}},"X-Feed-Guid":{"description":"Customer-facing feed GUID.","schema":{"type":"string"}},"X-Feed-Product-Count":{"description":"Number of products in the stored feed.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"Generic ACP feed envelope with a products array."}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/clients/{clientID}/tekka/ask":{"post":{"operationId":"askTekka","summary":"Ask Tekka a question about your space","description":"Asks Tekka, the Martekio AI assistant, a question in plain language and returns her\nwritten answer. Tekka reads your space's own analytics to answer: Share of Voice,\ncompetitors, sentiment, citations, prompt movements, AI bot traffic, recommendations\nand more. The endpoint is read-only and never changes your data.\n\nThis is the same assistant, with the same knowledge and the same answer quality, that\nyou get inside the Martekio app. The API exists so you can put her answers where your\nteam already works: a reporting job, an internal dashboard, a chat bot.\n\n### Answers are generated, not looked up\nTekka composes each answer from live data using a language model, so two runs of the\nsame question can word things differently. Do not string-match on the response text.\nIf you need stable, machine-readable values, use the specific data endpoints instead.\n\n### Response time\nAnswers typically take between 4 and 21 seconds, because Tekka reads your data and\nthen writes the answer. Set your client timeout to at least 150 seconds.\n\n### Limits\nEach question consumes one of your space's daily allowance, which resets at 00:00 UTC.\nThe allowance is claimed when the question is accepted, not when the answer comes back,\nso a question that times out or fails still counts. It is recorded either way and is\nvisible to Martekio support, which is what lets us tell you what happened.\n\nThe allowance is reported on the `X-Quota-*` response headers and in the `usage` object\nof every successful answer, so you can pace a batch without a second call. Note these\nare separate from the `X-RateLimit-*` headers, which carry the API-wide throughput\nlimit. Questions asked inside the Martekio app do not count against this allowance.\n\n### One question at a time\nA space may have only one question in flight. Asking again while one is still being\nanswered returns `409`; wait for the first to finish. This keeps a single slow question\nfrom crowding out the rest of your account.\n","tags":["Tekka"],"parameters":[{"$ref":"#/components/parameters/clientID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaAskRequest"}}}},"responses":{"200":{"description":"Tekka answered the question.","headers":{"X-Quota-Limit":{"description":"Your space's daily question allowance.","schema":{"type":"integer"}},"X-Quota-Remaining":{"description":"Questions left in the current UTC day.","schema":{"type":"integer"}},"X-Quota-Reset":{"description":"Unix timestamp at which the allowance resets (next 00:00 UTC).","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaAskResponse"}}}},"400":{"description":"The question was missing, empty, or longer than 1000 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}},"401":{"description":"Missing or invalid API token."},"403":{"description":"API access is not available for this space. Either the plan does not include API\naccess, the contract has expired, your account administrator has switched API\naccess off, or the Tekka API guard is off for this space.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}},"409":{"description":"A question is already being answered for this space. Only one may be in flight at\na time. Retry once the first has returned.\n","headers":{"Retry-After":{"description":"Suggested seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}},"429":{"description":"The daily question limit for this space has been reached. The `Retry-After` header\ngives the seconds until the allowance resets.\n","headers":{"Retry-After":{"description":"Seconds until the daily allowance resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}},"500":{"description":"Tekka failed internally while answering. The question is recorded and counts\nagainst your allowance. Quote `requestId` if you contact support.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}},"503":{"description":"The request was refused rather than answered, either because your question\nallowance could not be determined or because the Tekka API is temporarily\nunavailable. Both are faults on our side, not limits: retry shortly. The `code`\nfield distinguishes them (`quota_unavailable` or `tekka_api_unavailable`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}},"504":{"description":"Tekka did not finish answering within the server deadline. The question IS recorded\nand DOES count against your daily allowance, because the work was performed.\nTry a narrower question.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TekkaError"}}}}}}},"/clients/{clientID}/prompt-check":{"post":{"operationId":"promptCheck","summary":"Run a Prompt Check against AI assistants","description":"Runs a single prompt through the selected AI assistants in real time and returns the\nvisibility insights (Share of Voice, brand mentions and citations, competitor\ncitations, sentiment) for your brand, computed against your space's brand and\ncompetitor configuration. The run is ephemeral: nothing is persisted to your\ndashboards or analytics.\n\nDesigned for occasional, on-demand use (for example, checking a prompt from your CMS\nduring content creation). To test many prompts at scale, set up a scheduled analysis\ninstead.\n\n### Assistants\nSpecify assistants with the stable slugs below. If omitted, defaults to `chatgpt`.\nGoogle AI Overview is not available via the API (it renders only intermittently and\nruns on a slow asynchronous queue); it is planned for a future asynchronous endpoint.\n\n| Slug | Assistant |\n|------|-----------|\n| `chatgpt` | ChatGPT |\n| `claude` | Claude (Anthropic) |\n| `gemini` | Gemini |\n| `perplexity` | Perplexity |\n| `google_ai_mode` | Google AI Mode |\n| `grok` | Grok |\n\n### Idempotency\nAn `Idempotency-Key` header is required. Repeating a request with the same key and\nbody returns the original result without re-running or consuming quota. Reusing a key\nwith a different body returns `422`.\n\n### Limits\nEach accepted request consumes one of your space's daily allowance (regardless of how\nmany assistants it targets) and is reported on the `X-Quota-*` response headers. Only\none test may run per space at a time; a concurrent request returns `409`. The number\nof assistants per request is capped per space.\n","tags":["Prompt Check"],"parameters":[{"$ref":"#/components/parameters/clientID"},{"$ref":"#/components/parameters/idempotencyKeyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckRequest"}}}},"responses":{"200":{"description":"The Prompt Check ran. Returns per-assistant insights and a summary.","headers":{"X-Quota-Limit":{"description":"Your space's daily test allowance.","schema":{"type":"integer"}},"X-Quota-Remaining":{"description":"Tests remaining today.","schema":{"type":"integer"}},"X-Quota-Reset":{"description":"When the daily allowance resets (RFC 3339).","schema":{"type":"string","format":"date-time"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckResponse"}}}},"400":{"description":"Invalid request, unknown or ineligible assistant, or too many assistants.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckError"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Your API token does not have access to this client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another test is already running for this space (single-flight), or a request with this Idempotency-Key is in progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckError"}}}},"422":{"description":"The Idempotency-Key was already used with a different request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckError"}}}},"429":{"description":"The daily test quota has been reached (code `quota_exceeded`). The generic per-token throughput limiter may also return 429.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckError"}}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"Every requested assistant failed to respond. No quota is consumed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCheckError"}}}}}}},"/radar/analyses":{"post":{"operationId":"submitRadarAnalysis","summary":"Submit a Radar analysis","description":"Submits a brand and a set of prompts for analysis and returns immediately with a\n`job_id`. The analysis runs asynchronously on Martekio's standard pipeline across the\nAI assistants enabled for your key: six as standard (ChatGPT, Perplexity, Gemini,\nAnthropic, Google AI Mode, Google AI Overview), plus Grok where your subscription\nincludes the add-on. Poll `GET /radar/analyses/{jobId}` until it reports\n`completed`; that same response then carries the full results inline under `analysis`, so\none poll loop is all you need. `GET /radar/analyses/{jobId}/results` returns the identical\nresults and is retained for backward compatibility.\n\nEach request is independent (there is no idempotency key): submitting the same body twice\nruns the analysis twice and is charged twice.\n\n### Requests\nThe billable unit is the prompt: each prompt in a request is deducted once, whatever the number of assistants.\nThe check is all-or-nothing: if the request needs more prompts than remain in the key's annual\nallowance, the whole request is rejected with `402` and nothing is charged or run. The\ncharged / remaining counts and the reset time are returned in the `prompts` block.\n\n### Concurrency\nA key may have a limited number of runs in flight at once (default 1). Submitting while\nthat limit is reached returns `409 run_in_progress`; poll the in-flight run and retry when\nit completes.\n\n### Fields\nAll five fields are required. `website` (owned domain(s), drives brand citations) and\n`brand_identifier` (brand identifier(s), drives brand mentions) each accept a\ncomma-separated list. `country` is an ISO 3166-1 alpha-2 code (e.g. `US`, `GB`, `FR`).\n`prompts` holds the prompts to run: 1 to the key's per-request maximum (up to 100, the\nsize of a regular analysis), each at most 500 characters. One prompt is the billable\nunit, deducted once from your allowance whatever the number of assistants it reaches. (`citation_key` is accepted as a deprecated alias\nfor `brand_identifier`.)\n","tags":["Radar"],"security":[{"RadarAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarSubmitRequest"}}}},"responses":{"202":{"description":"Accepted. The analysis was queued; poll the status endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarSubmitResponse"}}}},"400":{"description":"Invalid request (missing or invalid field, prompt too long, too many prompts, or unsupported country). Codes include `invalid_request`, `too_many_prompts`, `unsupported_country`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"401":{"description":"Missing or invalid API key, or a key not provisioned for Radar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"402":{"description":"Not enough prompts remaining for this request (code `quota_exceeded`). Nothing is charged or run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarQuotaError"}}}},"403":{"description":"This API key is disabled (code `key_disabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"409":{"description":"The key already has its maximum number of runs in progress (code `run_in_progress`). Poll the in-flight run and retry when it completes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"429":{"description":"Rate limit exceeded (code `rate_limit_exceeded`). Maximum 60 requests per minute.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"500":{"description":"Internal error (code `internal_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"503":{"description":"The Radar API is temporarily disabled for maintenance (code `service_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}}}}},"/radar/analyses/{jobId}":{"get":{"operationId":"getRadarAnalysisStatus","summary":"Get Radar analysis status","description":"Returns the current status of a submitted Radar analysis. Poll this until `status` is\n`completed` or `failed`. On `completed`, the response carries the full results inline\nunder `analysis` (same shape as the results endpoint), so a single poll loop is enough and\nno separate results request is required. Status values: `submitted`, `running`, `completed`,\n`failed`.\n","tags":["Radar"],"security":[{"RadarAuth":[]}],"parameters":[{"$ref":"#/components/parameters/radarJobId"}],"responses":{"200":{"description":"Current status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarStatusResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"403":{"description":"This API key is disabled (code `key_disabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"404":{"description":"No analysis found for this job id (code `not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}}}}},"/radar/analyses/{jobId}/results":{"get":{"operationId":"getRadarAnalysisResults","summary":"Get Radar analysis results","description":"Returns the completed analysis for a job: per-assistant metrics plus brand and competitor\ncitations. Returns `409` while the run is still in progress; poll the status endpoint\nuntil it reports `completed`. The status endpoint now also returns these same results\ninline under `analysis` on completion, so this endpoint is retained mainly for backward\ncompatibility.\n","tags":["Radar"],"security":[{"RadarAuth":[]}],"parameters":[{"$ref":"#/components/parameters/radarJobId"}],"responses":{"200":{"description":"The completed analysis results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarResultsResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"403":{"description":"This API key is disabled (code `key_disabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"404":{"description":"No analysis found for this job id (code `not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"409":{"description":"The analysis is not finished yet (code `not_ready`). Keep polling the status endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}}}}},"/radar/usage":{"get":{"operationId":"getRadarUsage","summary":"Get Radar prompt usage and request history","description":"Returns the partner's request balance for the current annual period (how many requests have been\nused and how many remain against the annual allowance) plus the recent Radar request history.\nEach request in the history carries the exact request and response JSON payloads, so a partner\ncan audit everything it sent and received. Use `?limit` to bound the history length\n(default 50, maximum 500); requests are returned newest first.\n","tags":["Radar"],"security":[{"RadarAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Maximum number of requests to return, newest first. Default 50, maximum 500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":50}}],"responses":{"200":{"description":"Current request balance and request history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarUsageResponse"}}}},"401":{"description":"Missing or invalid API key, or a key not provisioned for Radar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}},"403":{"description":"This API key is disabled (code `key_disabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RadarError"}}}}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API token in `mkto_selector.validator` format"},"RadarAuth":{"type":"http","scheme":"bearer","description":"Per-partner Martekio Radar API key in `mkto_selector.validator` format, issued by Martekio (not generated from the dashboard). The key identifies the paying partner and carries its own annual prompt allowance.\n"}},"parameters":{"radarJobId":{"name":"jobId","in":"path","required":true,"description":"The `job_id` returned when the analysis was submitted.","schema":{"type":"string","format":"uuid"}},"clientID":{"name":"clientID","in":"path","required":true,"description":"Unique identifier of the client (organisation)","schema":{"type":"string"}},"recommendationTypeFilter":{"name":"type","in":"query","required":false,"description":"Optional filter by recommendation type. Provide a single value or a\ncomma-separated list to return only recommendations of those types\n(for example, `type=content_gap` or `type=content_gap,citation_improvement`).\nMatching is case-insensitive. Omit to return all types. An unknown value\nreturns 400.\n","schema":{"type":"string"},"example":"content_gap,citation_improvement"},"feedId":{"name":"feedId","in":"path","required":true,"description":"Feed identifier. Accepts the customer-facing feed GUID (displayed on the feed card\nas \"fid\") or the internal integer set ID.\n","schema":{"type":"string"}},"aiProviderQuery":{"name":"provider","in":"query","required":true,"description":"AI provider name","schema":{"type":"string","enum":["ChatGPT","Perplexity","Gemini","Anthropic","Grok"]}},"startDate":{"name":"startDate","in":"query","required":true,"description":"Start of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},"endDate":{"name":"endDate","in":"query","required":true,"description":"End of date range (YYYY-MM-DD)","schema":{"type":"string","format":"date"}},"idempotencyKeyHeader":{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique, client-supplied key for the request. Repeating the same key with the same\nbody replays the original result without consuming quota; reusing it with a different\nbody returns 422.\n","schema":{"type":"string","maxLength":255}}},"responses":{"Unauthorized":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"FeedGenerationDisabled":{"description":"Product feed generation is switched off platform-wide. Temporary and nothing to do\nwith your account or token: the four refresh endpoints stop, while every feed read\nendpoint keeps serving the contents last published. Distinct from the 403 returned\nwhen product feed generation is not enabled for the account.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message"}}},"InfluentialSourceCount":{"type":"object","description":"One of the client's active influential sources with the count of AI responses citing it.","properties":{"source":{"type":"string","description":"The source's label as configured in Manage Influential Sources.","example":"Reddit"},"domain":{"type":"string","description":"The source's domain; citations match on the exact domain or any subdomain.","example":"reddit.com"},"responses_citing":{"type":"integer","description":"Distinct AI responses in the window whose citations include this source."},"pct":{"type":"number","format":"float","description":"responses_citing as a percentage of total_responses, two decimal places."}}},"PromptCheckError":{"type":"object","description":"Structured error returned by the Prompt Check endpoint.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code (e.g. invalid_request, invalid_assistant, too_many_assistants, request_in_progress, idempotency_key_reuse, quota_exceeded, all_assistants_failed)."},"message":{"type":"string","description":"Human-readable explanation."},"details":{"type":"object","description":"Optional structured context (e.g. the allowed assistant slugs, or the assistant cap).","additionalProperties":true}}}}},"TekkaError":{"type":"object","description":"Structured error returned by the Tekka endpoint. Note that failures raised by the\nshared API middleware (an invalid token, for example) return a flat\n`{\"error\": \"...\"}` string instead; check the type before reading `code`.\n","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code. One of: `invalid_request` (missing, empty or\nover-length question, or a bad clientID), `tekka_api_disabled` (the Tekka API\nis switched off for this space), `tekka_api_unavailable` (switched off\nplatform-wide), `quota_unavailable` (your allowance could not be read),\n`question_in_progress` (another question is still being answered for this\nspace), `daily_limit_reached`, `timeout`, `internal_error`.\n"},"message":{"type":"string","description":"Human-readable explanation."},"details":{"type":"object","description":"Optional structured context. Carries `requestId` on every failure raised by\nthe handler, plus `limit`, `used` and `resetsAt` on a `daily_limit_reached`,\nand `logId` plus `responseTimeMs` on a `timeout`.\n","additionalProperties":true}}}}},"TekkaAskRequest":{"type":"object","required":["question"],"properties":{"question":{"type":"string","maxLength":1000,"description":"The question, in plain language. Ask it the way you would ask a colleague.\nConversation history is not supported: each request is answered on its own, so\ninclude any context the question needs.\n","example":"How did my Share of Voice change over the last 3 months, and who gained on me?"}}},"TekkaAskResponse":{"type":"object","properties":{"answer":{"type":"string","description":"Tekka's written answer. May contain light Markdown and usually ends with a short\n\"Source\" line naming the data behind it. Generated text: do not string-match on it.\n"},"toolsUsed":{"type":"array","items":{"type":"string"},"description":"The Martekio data tools Tekka called to answer, in call order. Empty when she\nanswered from documentation or from a generated query rather than a data tool.\n","example":["get_sov_summary","get_top_competitors"]},"requestId":{"type":"string","description":"Unique id for this request. Quote it when contacting support about an answer."},"logId":{"type":"integer","description":"Identifier of the stored log row for this question, when it was recorded."},"responseTimeMs":{"type":"integer","description":"How long the answer took to produce, in milliseconds, measured server-side from\nthe moment the request was accepted to the moment the answer was ready.\n","example":8432},"usage":{"type":"object","description":"Your space's allowance after this question was counted.","properties":{"limit":{"type":"integer","description":"Questions permitted per UTC day for this space.","example":25},"remaining":{"type":"integer","description":"Questions left in the current UTC day.","example":18},"resetsAt":{"type":"string","format":"date-time","description":"When the allowance resets (next 00:00 UTC).","example":"2026-09-05T00:00:00Z"}}}}},"PromptCheckRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":500,"description":"The prompt to test.","example":"where can i buy gilded wardrobe silk products"},"assistants":{"type":"array","description":"Assistant slugs to test. Defaults to [\"chatgpt\"] when omitted or empty.","items":{"type":"string","enum":["chatgpt","claude","gemini","perplexity","google_ai_mode","grok"]},"example":["chatgpt","claude"]},"include":{"type":"array","description":"Optional heavy fields to attach to each assistant. Insights only by default.","items":{"type":"string","enum":["citations","responses"]},"example":["citations","responses"]}}},"PromptCheckResponse":{"type":"object","properties":{"request_id":{"type":"string","example":"tp_45bb9329-68a7-496c-be17-7c4d7135e730"},"prompt":{"type":"string"},"is_branded":{"type":"boolean","description":"Whether the prompt itself contains your brand."},"requested_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time"},"summary":{"type":"object","properties":{"assistants_tested":{"type":"integer"},"brand_mentioned_count":{"type":"integer"},"brand_cited_count":{"type":"integer"},"total_brand_mentions":{"type":"integer"},"total_brand_citations":{"type":"integer"},"total_competitive_citations":{"type":"integer"}}},"assistants":{"type":"array","items":{"$ref":"#/components/schemas/PromptCheckAssistant"}}}},"PromptCheckAssistant":{"type":"object","properties":{"assistant":{"type":"string","description":"Assistant slug."},"share_of_voice":{"type":"number","format":"float"},"brand_mentioned":{"type":"boolean"},"brand_cited":{"type":"boolean"},"brand_mentions":{"type":"integer"},"brand_citations":{"type":"integer"},"competitive_citations":{"type":"integer"},"competitors":{"type":"array","description":"Competitors cited in this assistant's response, with per-competitor counts. Sums to competitive_citations.","items":{"$ref":"#/components/schemas/PromptCheckCompetitor"}},"sentiment":{"type":"string","nullable":true,"description":"Sentiment toward the brand, or null when not computed (brand not present in the response)."},"sentiment_explanation":{"type":"string","description":"The reasoning behind the sentiment. Present only when include contains \"responses\"."},"response_text":{"type":"string","description":"The full AI response. Present only when include contains \"responses\"."},"citations":{"type":"array","description":"All classified citation URLs in this assistant's response. Present only when include contains \"citations\".","items":{"$ref":"#/components/schemas/PromptCheckCitation"}},"error":{"type":"string","nullable":true,"description":"Set when this assistant failed; other assistants still return (partial success is still 200)."}}},"PromptCheckCompetitor":{"type":"object","properties":{"name":{"type":"string"},"citations":{"type":"integer"}}},"PromptCheckCitation":{"type":"object","properties":{"url":{"type":"string"},"competitor_name":{"type":"string"},"position":{"type":"integer"},"is_client_owned":{"type":"boolean"},"is_competitor":{"type":"boolean"},"is_influential_source":{"type":"boolean"}}},"IndustryInsight":{"type":"object","properties":{"industryCode":{"type":"integer"},"industryName":{"type":"string"},"aiProviders":{"type":"object","description":"Industry records grouped by AI provider name","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/IndustryRecord"}}}}},"IndustryRecord":{"type":"object","properties":{"industry_code":{"type":"integer"},"ai_provider":{"type":"string"},"domain":{"type":"string"},"citation_count":{"type":"integer"}}},"IndustryTopDomains":{"type":"object","properties":{"industryCode":{"type":"integer"},"aiProviders":{"type":"object","description":"Top domains grouped by AI provider name","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/IndustryDomain"}}}}},"IndustryDomain":{"type":"object","properties":{"company_industry_code":{"type":"integer"},"ai_provider":{"type":"string"},"domain":{"type":"string"},"citation_count":{"type":"integer"}}},"SoVMetrics":{"type":"object","properties":{"overallSOV":{"type":"number","format":"double","description":"Overall Share of Voice percentage"},"brandedCitationSOV":{"type":"number","format":"double","description":"Branded citation Share of Voice"},"nonBrandedCitationSOV":{"type":"number","format":"double","description":"Non-branded citation Share of Voice"},"analysisDate":{"type":"string","format":"date","description":"Date of the analysis"}}},"SoVHistoricResponse":{"type":"object","description":"Historical SoV data keyed by provider name","properties":{"Anthropic":{"type":"array","items":{"$ref":"#/components/schemas/SoVHistoricEntry"}},"ChatGPT":{"type":"array","items":{"$ref":"#/components/schemas/SoVHistoricEntry"}},"Gemini":{"type":"array","items":{"$ref":"#/components/schemas/SoVHistoricEntry"}},"Perplexity":{"type":"array","items":{"$ref":"#/components/schemas/SoVHistoricEntry"}},"Grok":{"type":"array","items":{"$ref":"#/components/schemas/SoVHistoricEntry"}}}},"SoVHistoricEntry":{"type":"object","properties":{"refreshDate":{"type":"string","format":"date"},"overallSOV":{"type":"number","format":"double"},"totalBrandedSOV":{"type":"number","format":"double"},"totalNonBrandedSOV":{"type":"number","format":"double"}}},"PerformanceResponse":{"type":"object","properties":{"performance":{"type":"object","description":"Performance records grouped by provider name","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformance"}}},"SessionID":{"type":"string","description":"Session ID for the analysis date (present when analysis_date is specified)"},"comparison_performance":{"type":"object","description":"Comparison performance records (present when comparison_date is specified)","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformance"}}},"ComparisonSessionID":{"type":"string","description":"Session ID for the comparison date"}}},"ClientPerformance":{"type":"object","properties":{"ai_provider":{"type":"string"},"analysis_date":{"type":"string","format":"date"},"total_queries":{"type":"integer"},"total_citations":{"type":"integer"},"average_position":{"type":"number","format":"double"},"visibility_score":{"type":"number","format":"double"}}},"PerformanceAllTimeResponse":{"type":"object","properties":{"performance":{"type":"object","description":"All-time averages bucketed by provider. Each provider key holds an array (length 0 or 1)\nof records. The array shape exists because the frontend chart consumer expects it; in\npractice there is at most one row per (client, provider) pair.\n","properties":{"Anthropic":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformanceAllTime"}},"ChatGPT":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformanceAllTime"}},"Gemini":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformanceAllTime"}},"Perplexity":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformanceAllTime"}},"Grok":{"type":"array","items":{"$ref":"#/components/schemas/ClientPerformanceAllTime"}}}}}},"ClientPerformanceAllTime":{"type":"object","description":"All-time aggregated metrics for one (client, AI provider) pair. Field names are\nPascalCase because the handler serialises the Go `ClientAveragesAllTime` model directly.\n","properties":{"ID":{"type":"integer","description":"Internal GORM primary key."},"CreatedAt":{"type":"string","format":"date-time"},"UpdatedAt":{"type":"string","format":"date-time"},"DeletedAt":{"type":"string","format":"date-time","nullable":true},"ClientID":{"type":"integer"},"AIProvider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]},"AvgNonBrandCitations":{"type":"number","format":"double","description":"Average brand citations per non-branded response."},"AvgBrandCitations":{"type":"number","format":"double","description":"Average brand citations per branded response."},"AvgNonBrandReferences":{"type":"number","format":"double","description":"Average brand references per non-branded response."},"AvgBrandReferences":{"type":"number","format":"double","description":"Average brand references per branded response."},"PercentNonBrandedRepliesWithCitations":{"type":"number","format":"double","description":"Percentage of non-branded responses containing a brand citation."},"PercentBrandedRepliesWithCitations":{"type":"number","format":"double","description":"Percentage of branded responses containing a brand citation."},"PercentNonBrandedRepliesWithReferences":{"type":"number","format":"double","description":"Percentage of non-branded responses containing a brand reference."},"PercentBrandedRepliesWithReferences":{"type":"number","format":"double","description":"Percentage of branded responses containing a brand reference."},"AvgNoCitationsInResponse":{"type":"number","format":"double","description":"Average total number of citations (any brand) per response."},"IsArchived":{"type":"boolean","description":"True when the record has been archived under the data retention policy."}}},"Competitor":{"type":"object","properties":{"aiProvider":{"type":"string"},"competitorName":{"type":"string"},"totalCitationCount":{"type":"integer"}}},"CuratedCompetitorsResponse":{"type":"object","required":["tracked","threats"],"properties":{"tracked":{"allOf":[{"$ref":"#/components/schemas/CuratedCompetitorSet"}],"description":"Brands chosen for ongoing monitoring in Competition Explorer."},"threats":{"allOf":[{"$ref":"#/components/schemas/CuratedCompetitorSet"}],"description":"Curated real rivals, mutually exclusive with the Tracked set."}}},"CuratedCompetitorSet":{"type":"object","required":["total","competitors"],"properties":{"total":{"type":"integer","description":"Number of curated competitors in this set."},"competitors":{"type":"array","description":"Alphabetically ordered curated competitors.","items":{"$ref":"#/components/schemas/CuratedCompetitor"}}}},"CuratedCompetitor":{"type":"object","required":["competitor_name","added_at"],"properties":{"competitor_name":{"type":"string","description":"Normalised competitor or brand name, commonly a domain.","example":"example-rival.com"},"added_at":{"type":"string","format":"date-time","description":"When the competitor was added to this curated set."}}},"CompetitorTrendsResponse":{"type":"object","properties":{"aiProvider":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/CompetitorTrend"}}}},"CompetitorTrend":{"type":"object","properties":{"ID":{"type":"string"},"CompetitorName":{"type":"string"},"BrandedCumulativeURLsCited":{"type":"integer"},"NonBrandedCumulativeURLsCited":{"type":"integer"}}},"CompetitorTrendsGroupedResponse":{"type":"object","description":"Winners and losers grouped by provider name","properties":{"Anthropic":{"$ref":"#/components/schemas/WinnersLosers"},"ChatGPT":{"$ref":"#/components/schemas/WinnersLosers"},"Gemini":{"$ref":"#/components/schemas/WinnersLosers"},"Perplexity":{"$ref":"#/components/schemas/WinnersLosers"},"Grok":{"$ref":"#/components/schemas/WinnersLosers"}}},"WinnersLosers":{"type":"object","properties":{"winners":{"type":"array","items":{"$ref":"#/components/schemas/GroupedCompetitor"}},"losers":{"type":"array","items":{"$ref":"#/components/schemas/GroupedCompetitor"}}}},"GroupedCompetitor":{"type":"object","properties":{"ID":{"type":"string"},"competitorName":{"type":"string"},"totalCumulativeURLsCited":{"type":"integer"},"noDaysSinceLastSeen":{"type":"integer"},"noTimesDetected":{"type":"integer"},"onWatchList":{"type":"boolean","description":"True when the competitor is one of the client's Competitive Threats."},"onTrackedList":{"type":"boolean","description":"True when the competitor is one of the client's Tracked brands."},"lastSeenDate":{"type":"string"},"providerName":{"type":"string"}}},"AnalysisSummariesResponse":{"type":"object","properties":{"client":{"$ref":"#/components/schemas/ClientProfile"},"summary":{"type":"array","items":{"$ref":"#/components/schemas/AnalysisSummary"}}}},"AnalysisSummary":{"type":"object","description":"High-level aggregated results for one (client, session, AI provider) tuple. Field names\nare PascalCase because the handler serialises the Go `AnalysisSummary` GORM model\ndirectly.\n","properties":{"ID":{"type":"integer","description":"Internal GORM primary key."},"CreatedAt":{"type":"string","format":"date-time"},"UpdatedAt":{"type":"string","format":"date-time"},"DeletedAt":{"type":"string","format":"date-time","nullable":true},"ClientID":{"type":"integer"},"SessionID":{"type":"string","description":"Unique ID for the analysis session this summary belongs to."},"AIProvider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","GoogleAIMode","GoogleAIOverview","Grok"]},"AnalysisDate":{"type":"string","format":"date"},"CompanyIndustryCode":{"type":"integer"},"KeywordsProcessed":{"type":"integer","description":"Total keywords evaluated in this session."},"PromptGenerator":{"type":"string","description":"AI used to generate the search phrases (e.g. \"ChatGPT\")."},"AIProviderModel":{"type":"string","description":"Specific provider model used (e.g. \"claude-3-opus-20240229\")."},"KeywordSource":{"type":"string","description":"Origin of the keywords (e.g. \"Import\", \"GSC\")."},"NoNonBrandCitations":{"type":"integer"},"NoNonBrandReferences":{"type":"integer"},"NoBrandCitations":{"type":"integer"},"NoBrandReferences":{"type":"integer"},"TotalResponsesContainingReferences":{"type":"integer"},"TotalResponsesContainingCitations":{"type":"integer"},"TotalCitations":{"type":"integer"},"TotalReferences":{"type":"integer"},"NoNonBrandedRepliesWithCitations":{"type":"integer"},"NoBrandedRepliesWithCitations":{"type":"integer"},"NoNonBrandedRepliesWithReferences":{"type":"integer"},"NoBrandedRepliesWithReferences":{"type":"integer"},"TotalCompetitiveCitations":{"type":"integer"},"CitationKey":{"type":"string","description":"Primary brand identifier used for matching in this session."},"AnalysisDuration":{"type":"number","format":"double","description":"Cumulative elapsed minutes from session start to when this provider's row was written.\nThe largest value across providers in a session approximates total wall-clock time.\n"},"AnalysisDurationKeyword":{"type":"number","format":"double","description":"AnalysisDuration divided by KeywordsProcessed for this row, in minutes per keyword."},"SentimentScore":{"type":"number","format":"double","description":"Average sentiment score across responses where sentiment was computed."},"IsArchived":{"type":"boolean","description":"True when the record has been archived under the data retention policy."},"IsPartial":{"type":"boolean","description":"True when the row was rebuilt from analysis_details after the provider failed\nmid-run, so KeywordsProcessed reflects what completed rather than what was\nscheduled. This also covers runs that did not produce a row for every scheduled\nkeyword, for example when a DataForSEO async task (Google AI Overview) timed out\nbefore becoming ready. Treat IsPartial=true sessions as a degraded run, not a\nfinal benchmark.\n"},"RepairedAt":{"type":"string","format":"date-time","nullable":true,"description":"When a staff admin ran repair_analysis on this summary. Null means IsPartial was set\nby the analysis pipeline (originally broken); a value means a manual repair corrected it.\n"}}},"AnalysisDetailsResponse":{"type":"object","properties":{"client":{"$ref":"#/components/schemas/ClientProfile"},"summary":{"type":"array","description":"Present when pagination is not used","items":{"$ref":"#/components/schemas/AnalysisDetail"}},"data":{"type":"array","description":"Present when pagination is used","items":{"$ref":"#/components/schemas/AnalysisDetail"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"AnalysisDetail":{"type":"object","description":"One row per (session, provider, phrase) analysis result. Field names are PascalCase\nbecause the handler serialises the Go `AnalysisDetail` GORM model directly.\n\nRows are wide: each one includes the AI reply text, sentiment data, and a boolean for\nevery tracked source domain. For lightweight listings, prefer paginating and only\nreading the fields you need.\n","properties":{"ID":{"type":"integer"},"CreatedAt":{"type":"string","format":"date-time"},"UpdatedAt":{"type":"string","format":"date-time"},"DeletedAt":{"type":"string","format":"date-time","nullable":true},"ClientID":{"type":"integer"},"CompanyIndustryCode":{"type":"integer"},"SessionID":{"type":"string"},"QueryID":{"type":"string","description":"Unique ID linking this record to its citation URL entries."},"AIProvider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","GoogleAIMode","GoogleAIOverview","Grok"]},"Keyword":{"type":"string","description":"Original seed keyword from the source."},"KeywordSource":{"type":"string","description":"Origin of the keyword (e.g. \"Import\", \"GSC\")."},"IsBranded":{"type":"boolean","description":"True if the original seed keyword was classified as Branded."},"IsBrandReferenced":{"type":"boolean","description":"True if the client's brand was mentioned (referenced) in the AI reply."},"IsBrandCited":{"type":"boolean","description":"True if the client's brand was linked to (cited) in the AI reply."},"NoBrandReferences":{"type":"integer","description":"Count of unlinked text mentions of the client's brand in the AI reply."},"NoBrandCitations":{"type":"integer","description":"Count of citation links to the client's brand in the AI reply."},"TotalCitationsInResponse":{"type":"integer","description":"Total count of all citation links (client + competitors) in the AI reply."},"TotalCompetitiveCitations":{"type":"integer"},"Phrase":{"type":"string","description":"Final search query sent to the AI provider."},"PhraseSOV":{"type":"number","format":"double","description":"Citation-based Share of Voice for this specific phrase."},"PhraseWords":{"type":"integer"},"PhraseChars":{"type":"integer"},"Clicks":{"type":"integer","description":"Clicks for the original keyword from the source, if available."},"AvgPosition":{"type":"number","format":"double","description":"Average search position for the original keyword from the source, if available."},"CTR":{"type":"number","format":"double","description":"Click-Through Rate for the original keyword from the source, if available."},"CitationKey":{"type":"string","description":"Brand identifier used for matching during this analysis."},"ReplyWords":{"type":"integer"},"ReplyChars":{"type":"integer"},"Reply":{"type":"string","description":"The AI reply text. In listing endpoints this is truncated to the first sentence\n(via a Postgres regex) or the first 100 characters as a fallback. Use\n`/analysis/responses/{queryID}` to fetch the full untruncated reply.\n"},"IsPhraseBranded":{"type":"boolean"},"IsArchived":{"type":"boolean","description":"Listing endpoints filter out archived rows; this will always be false in responses."},"DomainNameEvaluated":{"type":"string","description":"Domain name(s) used to identify brand citations in the response (comma-separated)."},"IsSentimentComputed":{"type":"boolean"},"Sentiment":{"type":"string","nullable":true,"description":"Sentiment classification (\"Positive\", \"Negative\", \"Neutral\")."},"SentimentScore":{"type":"number","format":"double","nullable":true,"description":"Sentiment score, typically from -1 (Negative) to 1 (Positive)."},"SentimentExplanation":{"type":"string","nullable":true},"PromptTokens":{"type":"integer"},"CompletionTokens":{"type":"integer"},"TotalTokens":{"type":"integer"},"IsCitationPathContainsBrand":{"type":"boolean"},"NoCitationsPathContainsBrand":{"type":"integer"},"IsCustomPrompt":{"type":"boolean"},"NoSearchQueries":{"type":"integer","description":"Count of fan-out web-search queries the provider fired while generating this response."},"IsSourceRedditFound":{"type":"boolean"},"IsSourceWikipediaFound":{"type":"boolean"},"IsSourceQuoraFound":{"type":"boolean"},"IsSourceAmazonFound":{"type":"boolean"},"IsSourceYouTubeFound":{"type":"boolean"},"IsSourceTikTokFound":{"type":"boolean"},"IsSourceGoogleFound":{"type":"boolean"},"IsSourceFacebookFound":{"type":"boolean"},"IsSourceInstagramFound":{"type":"boolean"},"IsSourceWalmartFound":{"type":"boolean"},"IsSourceEbayFound":{"type":"boolean"},"IsSourceYahooFound":{"type":"boolean"},"IsSourcePinterestFound":{"type":"boolean"},"IsSourceMediumFound":{"type":"boolean"},"IsAIOverviewRendered":{"type":"boolean","description":"True only on Google AI Overview rows where Google actually rendered an AI Overview\nfor the query (renders ~44-51% of the time). False on every other provider's row\nand on AI Overview rows where Google chose not to render. Powers AI Overview\nattendance trends.\n"},"SystemPromptVersionID":{"type":"integer","nullable":true,"description":"Foreign key to system_prompt_versions identifying the AI system prompt template\nactive when this call was made. Populated for ChatGPT (direct API), Anthropic, and\nGemini (Vertex) rows. NULL for Perplexity, Google AI Mode, Google AI Overview, and\nany row produced by a DFS-backed route (ChatGPT-via-DFS, Gemini-via-DFS), since\nthose paths bypass the Martekio system prompt to preserve consumer-truth.\n"},"DFSTaskID":{"type":"string","description":"DataForSEO task id (tasks[0].id) for rows produced by a DFS-backed provider path:\nChatGPT-via-DFS, Gemini-via-DFS, Google AI Mode, Google AI Overview. Empty string\nfor direct-API providers and for legacy rows written before the column was added.\nQuote this id to DataForSEO support when troubleshooting an anomalous response, or\nuse it to re-fetch the original result from the DFS task history endpoint without\nre-running the analysis.\n"}}},"Pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"totalRecords":{"type":"integer","format":"int64"},"totalPages":{"type":"integer"}}},"AIResponseResponse":{"type":"object","properties":{"reply":{"type":"string","description":"The full AI-generated response text"}}},"ClientProfile":{"type":"object","description":"Full client (organisation) profile, serialised from the Go `ClientProfile` GORM model\ndirectly. Field names are PascalCase. The object carries roughly 50 fields covering\nidentity, tier and entitlement flags, billing state, agency relationships, and platform\ndetection (Shopify / Magento / etc.). The schema below lists the fields most callers\nactually use; treat any extra fields the response contains as opaque operational metadata\nthat may be added without notice.\n","additionalProperties":true,"properties":{"ID":{"type":"integer","description":"Internal GORM primary key."},"ClientID":{"type":"integer","description":"Public client identifier used everywhere in the API path."},"ClientGUID":{"type":"string","description":"Unique GUID for public-facing client identification (e.g. feed URLs)."},"CompanyName":{"type":"string"},"CompanyCountry":{"type":"string"},"CompanyWWW":{"type":"string","description":"Primary website URL."},"CompanyWWWNormalised":{"type":"string","description":"Normalised primary website (e.g. \"martekio.com\" from \"www.martekio.com\")."},"CompanyIndustryCode":{"type":"integer","description":"Foreign key to IndustrySectors."},"ExpirationDate":{"type":"string","format":"date"},"TierID":{"type":"string"},"TierName":{"type":"string"},"IsTrialMode":{"type":"boolean"},"IsExpired":{"type":"boolean"},"IsAgency":{"type":"boolean"},"IsAgencyClient":{"type":"boolean"},"ParentClientID":{"type":"integer","description":"For agency-managed clients, the parent agency's ClientID. Zero otherwise."},"APIIncluded":{"type":"boolean","description":"True when API access is enabled for this client."}}},"CitationTrendsResponse":{"type":"object","properties":{"trends":{"type":"array","items":{"$ref":"#/components/schemas/CitationTrend"}},"providerInsights":{"type":"array","items":{"$ref":"#/components/schemas/ProviderInsight"}},"topURLs":{"type":"array","items":{"$ref":"#/components/schemas/TopURL"}}}},"CitationTrend":{"type":"object","properties":{"identifier":{"type":"string"},"sources":{"type":"array","items":{"$ref":"#/components/schemas/CitationSource"}}}},"CitationSource":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string"},"count":{"type":"integer","format":"int64"},"subreddits":{"type":"array","items":{"type":"string"}}}},"ProviderInsight":{"type":"object","properties":{"provider":{"type":"string"},"totalCount":{"type":"integer","format":"int64"},"sources":{"type":"array","items":{"$ref":"#/components/schemas/CitationSource"}},"trend":{"type":"array","items":{"$ref":"#/components/schemas/TrendPoint"}}}},"TrendPoint":{"type":"object","properties":{"x":{"type":"string","format":"date-time","description":"ISO 8601 timestamp"},"y":{"type":"integer","format":"int64"}}},"TopURL":{"type":"object","properties":{"url":{"type":"string"},"count":{"type":"integer","format":"int64"}}},"CitationsByQueryResponse":{"type":"object","properties":{"allCitationURLs":{"type":"array","items":{"$ref":"#/components/schemas/CitationURL"}}}},"CitationURL":{"type":"object","properties":{"full_url":{"type":"string"},"competitor_name":{"type":"string"},"path_contains_brand":{"type":"boolean"},"is_phrase_branded":{"type":"boolean"},"analysis_date":{"type":"string","format":"date-time"},"is_url_client_owned":{"type":"boolean"},"is_url_influential_source":{"type":"boolean"},"is_url_competition":{"type":"boolean"},"position_rank":{"type":"integer"},"has_tech_insights":{"type":"boolean","description":"True when url_technical_details exists for this (query_id, full_url) pair. Indicates the AI Response Explorer can drill into per-citation crawl data."}}},"SessionCompetitorGroup":{"type":"object","properties":{"aiProvider":{"type":"string"},"competitors":{"type":"array","items":{"$ref":"#/components/schemas/SessionCompetitor"}}}},"SessionCompetitor":{"type":"object","properties":{"competitorName":{"type":"string"},"citationCount":{"type":"integer"}}},"SentimentAveragesResponse":{"type":"object","properties":{"overall_average_score":{"type":"number","format":"double"},"total_responses_overall":{"type":"integer"},"averages_by_provider":{"type":"array","items":{"$ref":"#/components/schemas/ProviderSentimentAverage"}}}},"ProviderSentimentAverage":{"type":"object","properties":{"ai_provider":{"type":"string"},"average_score":{"type":"number","format":"double"},"total_responses":{"type":"integer"}}},"SentimentScoresResponse":{"type":"object","description":"Daily sentiment scores keyed by provider name","properties":{"Anthropic":{"type":"array","items":{"$ref":"#/components/schemas/SentimentScore"}},"ChatGPT":{"type":"array","items":{"$ref":"#/components/schemas/SentimentScore"}},"Gemini":{"type":"array","items":{"$ref":"#/components/schemas/SentimentScore"}},"Perplexity":{"type":"array","items":{"$ref":"#/components/schemas/SentimentScore"}},"Grok":{"type":"array","items":{"$ref":"#/components/schemas/SentimentScore"}}}},"SentimentScore":{"type":"object","properties":{"analysis_date":{"type":"string","format":"date"},"sentiment_score":{"type":"number","format":"double"}}},"AIBotSummary":{"type":"object","description":"Daily per-bot summary of agentic traffic. Note the mixed casing: most fields are\nsnake_case per the Go `json:` tags, but the embedded GORM model leaks `ID`, `CreatedAt`,\n`UpdatedAt`, and `DeletedAt` in PascalCase.\n","properties":{"ID":{"type":"integer","description":"Internal GORM primary key."},"CreatedAt":{"type":"string","format":"date-time"},"UpdatedAt":{"type":"string","format":"date-time"},"DeletedAt":{"type":"string","format":"date-time","nullable":true},"client_id":{"type":"integer"},"bot_name":{"type":"string"},"log_date":{"type":"string","format":"date-time","description":"Calendar date the summary covers (time component is always 00:00:00 UTC)."},"visit_count":{"type":"integer","description":"Total visits attributed to this bot for the day."},"unique_ip_count":{"type":"integer","description":"Distinct source IPs observed for this bot on this day."},"first_visit_timestamp":{"type":"string","format":"date-time","description":"Earliest visit timestamp within the day."},"last_visit_timestamp":{"type":"string","format":"date-time","description":"Latest visit timestamp within the day."},"hourly_distribution":{"type":"object","description":"Map of hour-of-day (\"0\"..\"23\") to visit count.","additionalProperties":{"type":"integer"}},"request_method_counts":{"type":"object","description":"Map of HTTP method (e.g. \"GET\", \"POST\") to visit count.","additionalProperties":{"type":"integer"}},"status_code_counts":{"type":"object","description":"Map of HTTP status code (as string, e.g. \"200\") to visit count.","additionalProperties":{"type":"integer"}},"top_folders":{"type":"object","description":"Top URL folders (first path segment) by visit count.","additionalProperties":{"type":"integer"}},"top_urls":{"type":"array","description":"Up-to-N most-visited URLs for this bot on this day.","items":{"type":"string"}},"is_archived":{"type":"boolean","description":"True when the record has been archived under the data retention policy."}}},"BotDistribution":{"type":"object","properties":{"bot_name":{"type":"string"},"visit_count":{"type":"integer","format":"int64"}}},"TrafficProportion":{"type":"object","properties":{"name":{"type":"string","description":"Bot name or \"Non-AI Bot Traffic\""},"value":{"type":"integer","format":"int64"}}},"AIReferralSummary":{"type":"object","description":"Daily summary of human visits referred from a specific AI chatbot. Most fields are\nsnake_case per the Go `json:` tags, but the embedded GORM model leaks `ID`, `CreatedAt`,\n`UpdatedAt`, and `DeletedAt` in PascalCase.\n","properties":{"ID":{"type":"integer","description":"Internal GORM primary key."},"CreatedAt":{"type":"string","format":"date-time"},"UpdatedAt":{"type":"string","format":"date-time"},"DeletedAt":{"type":"string","format":"date-time","nullable":true},"client_id":{"type":"integer"},"log_date":{"type":"string","format":"date-time"},"source":{"type":"string","description":"AI chatbot source (e.g. \"ChatGPT\")."},"visit_count":{"type":"integer","format":"int64"},"is_archived":{"type":"boolean"}}},"ReferralDistribution":{"type":"object","properties":{"source":{"type":"string","description":"AI chatbot source name"},"visit_count":{"type":"integer","format":"int64"}}},"ReferralTopPage":{"type":"object","properties":{"request_uri":{"type":"string","description":"The URL path that received referral visits"},"source":{"type":"string","description":"AI chatbot source name"},"visit_count":{"type":"integer","format":"int64"}}},"InfluentialSource":{"type":"object","properties":{"id":{"type":"integer"},"client_id":{"type":"integer"},"domain":{"type":"string","description":"The domain being tracked (e.g. \"medium.com\")."},"label":{"type":"string","description":"Human-readable display name for the source (e.g. \"Medium\")."},"is_active":{"type":"boolean","description":"Whether this source is currently being tracked."},"is_default":{"type":"boolean","description":"True when this is a system-seeded default source. Cannot be deleted, only deactivated."},"is_hero":{"type":"boolean","description":"True when this source appears as one of the hero trend charts on the Influential Sources dashboard (at most five)."},"color":{"type":"string","description":"Hex brand colour used for chart lines (e.g. \"#FF4500\" for Reddit)."},"created_at":{"type":"string","format":"date-time"}}},"ProviderSummary":{"type":"object","properties":{"aiProvider":{"type":"string","description":"AI provider name, or \"All\" for the row covering every provider together","example":"ChatGPT"},"citationRate":{"type":"number","format":"double","description":"Average percentage of responses containing a brand citation","example":44.05},"mentionRate":{"type":"number","format":"double","description":"Average percentage of responses containing a brand mention","example":51.89},"sentimentScore":{"type":"number","format":"double","description":"Average sentiment score (-1 to 1)","example":0.63},"totalSOV":{"type":"number","format":"double","description":"Total Share of Voice percentage, pooled over the window: brand citations divided by all citations. Not an average of per-analysis percentages.\n","example":17.9},"brandedSOV":{"type":"number","format":"double","description":"Share of Voice percentage across responses to branded phrases, pooled the same way","example":42.5},"nonBrandedSOV":{"type":"number","format":"double","description":"Share of Voice percentage across responses to non-branded phrases, pooled the same way","example":0.36},"analysisCount":{"type":"integer","description":"Number of analyses behind this row in the period. On the \"All\" row this is the total across providers, and the weight each provider carries in the averaged metrics.\n","example":43}}},"PerformanceSummaryResponse":{"type":"object","properties":{"overall":{"$ref":"#/components/schemas/ProviderSummary"},"providers":{"type":"array","items":{"$ref":"#/components/schemas/ProviderSummary"}}}},"Recommendation":{"type":"object","properties":{"id":{"type":"integer","description":"Unique recommendation ID"},"recommendationAction":{"type":"string","description":"Short, actionable statement describing what to do"},"recommendationText":{"type":"string","description":"Supporting context explaining why this action matters"},"recommendationType":{"type":"string","enum":["content_gap","citation_improvement","competitor_insight","sentiment_improvement","ranking_opportunity"],"description":"Category of the recommendation"},"priority":{"type":"integer","minimum":1,"maximum":5,"description":"Priority level (1 = most urgent, 5 = lowest)"},"aiProviderContext":{"type":"string","description":"Comma-separated list of AI providers this applies to, or \"All\"","example":"ChatGPT,Perplexity"},"isDismissed":{"type":"boolean","description":"Whether the user has dismissed this recommendation"},"isArchived":{"type":"boolean","description":"Whether this recommendation has been archived by a newer analysis"},"isOnBacklog":{"type":"boolean","description":"Whether this recommendation has been added to the backlog"}}},"RecommendationHistoryItem":{"type":"object","description":"A recommendation in the full-history view (includes archived/dismissed).","properties":{"id":{"type":"integer"},"sessionId":{"type":"string","description":"The analysis session that produced this recommendation"},"createdAt":{"type":"string","format":"date","description":"Date the recommendation was generated (YYYY-MM-DD)"},"recommendationAction":{"type":"string"},"recommendationText":{"type":"string"},"recommendationType":{"type":"string","enum":["content_gap","citation_improvement","competitor_insight","sentiment_improvement","ranking_opportunity"]},"priority":{"type":"integer","minimum":1,"maximum":5},"aiProviderContext":{"type":"string"},"isDismissed":{"type":"boolean"},"isArchived":{"type":"boolean"},"isOnBacklog":{"type":"boolean"}}},"RecommendationHistory":{"type":"object","properties":{"total":{"type":"integer","description":"Number of recommendations returned"},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/RecommendationHistoryItem"}}}},"BacklogItem":{"type":"object","description":"One entry in the Visibility Backlog.","properties":{"id":{"type":"integer"},"source":{"type":"string","enum":["recommendation","weekly_tip","manual"],"description":"Where the item originated"},"sourceId":{"type":"integer","description":"ID of the source record (e.g. the recommendation); omitted for manual items"},"sessionId":{"type":"string","description":"Analysis session of the source; omitted for manual items"},"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"aiProviderContext":{"type":"string","description":"Provider context, present for recommendation-sourced items"},"isCompleted":{"type":"boolean"},"completedOn":{"type":"string","format":"date","description":"Date the task was marked complete; present only for completed items"},"completedBy":{"type":"string","description":"Name of the user who marked the task complete; present only for completed items"},"createdAt":{"type":"string","format":"date"}}},"Backlog":{"type":"object","properties":{"total":{"type":"integer","description":"Number of backlog items returned"},"backlog":{"type":"array","items":{"$ref":"#/components/schemas/BacklogItem"}}}},"ImpactAction":{"type":"object","properties":{"key":{"type":"string","description":"Stable identifier for this piece of work. Assigned once and never rewritten, so it can be used to follow the same action across analyses."},"action":{"type":"string","description":"What to do, taken from the most recent analysis that raised it"},"text":{"type":"string","description":"The full recommendation text from that analysis"},"type":{"type":"string","enum":["content_gap","citation_improvement","competitor_insight","sentiment_improvement","ranking_opportunity"]},"priority":{"type":"integer","description":"1 most urgent, 5 lowest"},"aiProviders":{"type":"string","description":"Comma-separated assistants this applies to, or All"},"sessionCount":{"type":"integer","description":"How many SEPARATE analyses reached this same conclusion. The ranking key; read it against analysisCount."},"firstSeen":{"type":"string","format":"date","description":"Date of the earliest analysis that raised it"},"lastSeen":{"type":"string","format":"date","description":"Date of the most recent analysis that raised it"},"lane":{"type":"string","description":"recurring, new, or unconfirmed"},"isResolved":{"type":"boolean","description":"True when recent analyses have stopped raising it"}}},"ImpactActionsResponse":{"type":"object","properties":{"total":{"type":"integer","description":"Number of impact actions returned"},"analysisCount":{"type":"integer","description":"How many analyses the grouping spans. sessionCount is meaningless without it."},"rankingEnabled":{"type":"boolean","description":"False when there are too few analyses for the recurrence count to carry weight"},"minToRank":{"type":"integer","description":"Analyses required before recurrence is treated as evidence"},"impactActions":{"type":"array","items":{"$ref":"#/components/schemas/ImpactAction"}}}},"RecommendationsOverview":{"type":"object","description":"All four recommendation datasets returned together by the overview endpoint.","properties":{"latest":{"type":"array","description":"Active (non-archived, non-dismissed) recommendations","items":{"$ref":"#/components/schemas/Recommendation"}},"all":{"$ref":"#/components/schemas/RecommendationHistory"},"backlog":{"$ref":"#/components/schemas/Backlog"}}},"URLRefreshResult":{"type":"object","description":"Per-feed result of a URL list refresh.","properties":{"feedId":{"type":"integer","description":"Internal integer set ID for the refreshed feed."},"feedGuid":{"type":"string","description":"Customer-facing feed GUID."},"label":{"type":"string","description":"Display label for the feed."},"source":{"type":"string","description":"Discovery method recorded at feed creation.","enum":["sitemap","crawl","upload"]},"discoveredCount":{"type":"integer","description":"Number of URLs discovered in this refresh."},"previousCount":{"type":"integer","description":"Number of URLs the feed held before the refresh."},"added":{"type":"integer","description":"URLs present in the new discovery but not the previous list."},"removed":{"type":"integer","description":"URLs present previously but not in the new discovery."},"kept":{"type":"integer","description":"URLs that appeared in both lists."},"durationMs":{"type":"integer","format":"int64","description":"Time taken to refresh this feed's URL list, in milliseconds."}}},"URLRefreshFailure":{"type":"object","description":"Per-feed failure record from a URL list refresh.","properties":{"feedId":{"type":"integer"},"label":{"type":"string"},"source":{"type":"string"},"error":{"type":"string"}}},"URLRefreshSummary":{"type":"object","description":"Bulk URL list refresh summary across every eligible feed for the client.","properties":{"message":{"type":"string"},"clientId":{"type":"integer"},"refreshed":{"type":"integer","description":"Number of feeds whose URL list refreshed cleanly."},"failed":{"type":"integer","description":"Number of feeds that failed to refresh."},"results":{"type":"array","items":{"$ref":"#/components/schemas/URLRefreshResult"}},"failures":{"type":"array","items":{"$ref":"#/components/schemas/URLRefreshFailure"}},"durationMs":{"type":"integer","format":"int64"}}},"FeedRefreshSummary":{"type":"object","description":"Summary returned by the bulk feed refresh endpoint when no `format` is requested.","properties":{"message":{"type":"string"},"refreshed":{"type":"integer","description":"Number of feeds queued for regeneration."},"durationMs":{"type":"integer","format":"int64"},"includeUrls":{"type":"boolean","description":"Echoes the request flag. Only present when true."},"urlRefresh":{"$ref":"#/components/schemas/URLRefreshSummary"}}},"FeedRegenResult":{"type":"object","description":"Result of regenerating the JSON for a single feed.","properties":{"clientId":{"type":"integer"},"feedId":{"type":"integer"},"feedGuid":{"type":"string"},"label":{"type":"string"},"includeUrls":{"type":"boolean"},"status":{"type":"string","description":"Outcome status returned by the generator.","enum":["ok","partial","error"]},"message":{"type":"string"},"productCount":{"type":"integer","description":"Number of products in the regenerated feed."},"failedUrlCount":{"type":"integer","description":"Number of URLs that failed to parse during regeneration."},"urlRefresh":{"$ref":"#/components/schemas/URLRefreshResult"}}},"CitationConsistencyTrendResponse":{"type":"object","properties":{"client_id":{"type":"string","description":"Client ID echoed from the path parameter."},"window":{"type":"integer","description":"Trailing N analyses each anchor's score was computed over."},"min_prompt_samples":{"type":"integer","description":"Minimum analyses a prompt must appear in within the window before it contributes to an anchor's score. Filters single-run noise out."},"total":{"type":"integer","description":"Number of trend points in the response."},"trend":{"type":"array","items":{"$ref":"#/components/schemas/CitationConsistencyTrendPoint"}}}},"CitationConsistencyTrendPoint":{"type":"object","properties":{"analysis_date":{"type":"string","format":"date","description":"Date of the anchor analysis (YYYY-MM-DD)."},"session_id":{"type":"string","description":"Anchor analysis session_id. The same session_id may appear with both branding axes and across multiple providers."},"ai_provider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]},"is_prompt_branded":{"type":"boolean","description":"true for the branded-queries axis, false for non-branded."},"citation_score":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Average per-prompt citation consistency in the trailing window. 1.0 means every qualifying prompt was cited in every session in the window."},"reference_score":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Average per-prompt reference (mention) consistency in the trailing window."},"sample_size":{"type":"integer","description":"Number of analyses actually present in this anchor's trailing window (<= window)."},"prompt_count":{"type":"integer","description":"Number of qualifying prompts that contributed to this anchor's score (each had at least min_prompt_samples occurrences in the window)."}}},"CitationConsistencyPromptsResponse":{"type":"object","properties":{"client_id":{"type":"string"},"window":{"type":"integer"},"min_prompt_samples":{"type":"integer"},"branded":{"type":"string","enum":["true","false","all"]},"providers":{"type":"array","items":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]}},"total":{"type":"integer"},"prompts":{"type":"array","items":{"$ref":"#/components/schemas/CitationConsistencyPromptRow"}}}},"CitationConsistencyPromptRow":{"type":"object","properties":{"prompt":{"type":"string","description":"Free-text prompt sent to the AI provider."},"ai_provider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]},"is_prompt_branded":{"type":"boolean"},"sample_size":{"type":"integer","description":"Number of analyses in the window where this prompt ran (<= window)."},"citation_score":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Fraction of sampled analyses where the brand was cited for this prompt."},"reference_score":{"type":"number","format":"double","minimum":0,"maximum":1},"citation_pattern":{"type":"array","description":"Per-analysis citation flags across the FULL window (length = window), ordered oldest to\nlatest. Each cell carries:\n  `1`  cited\n  `0`  ran but not cited\n  `-1` did not run in this analysis slot\nAligned 1:1 with analysis_dates and session_ids.\n","items":{"type":"integer","enum":[-1,0,1]}},"reference_pattern":{"type":"array","description":"Per-analysis reference (mention) flags. Same shape and ordering as citation_pattern.","items":{"type":"integer","enum":[-1,0,1]}},"analysis_dates":{"type":"array","description":"Analysis dates (YYYY-MM-DD) aligned 1:1 with the pattern arrays.","items":{"type":"string","format":"date"}},"session_ids":{"type":"array","description":"Session IDs aligned 1:1 with the pattern arrays. Lets you cross-reference each cell to the analysis it came from.","items":{"type":"string"}}}},"ResponsesListResponse":{"type":"object","properties":{"total":{"type":"integer","description":"Number of response rows that match the filters, across every page."},"limit":{"type":"integer","description":"The page size applied (after clamping)."},"offset":{"type":"integer","description":"Rows skipped before this page."},"returned":{"type":"integer","description":"Number of rows in this page."},"responses":{"type":"array","items":{"$ref":"#/components/schemas/ResponseWithCitations"}}}},"ResponseWithCitations":{"type":"object","description":"One AI response with its full reply text, brand signals, and citation URLs.","properties":{"queryId":{"type":"string"},"aiProvider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]},"prompt":{"type":"string","description":"Final search prompt sent to the provider."},"isBranded":{"type":"boolean"},"reply":{"type":"string","description":"Full untruncated AI reply text."},"isBrandCited":{"type":"boolean"},"isBrandMentioned":{"type":"boolean"},"brandCitations":{"type":"integer"},"brandMentions":{"type":"integer"},"sentiment":{"type":"string","nullable":true,"description":"Sentiment classification (\"Positive\", \"Negative\", \"Neutral\")."},"sentimentScore":{"type":"number","format":"double","nullable":true,"description":"Sentiment score from -1 (Negative) to 1 (Positive)."},"sessionId":{"type":"string"},"analysisDate":{"type":"string","format":"date"},"citations":{"type":"array","items":{"$ref":"#/components/schemas/ResponseCitation"}}}},"ResponseDetail":{"type":"object","description":"Single-record form returned by `GET /clients/{clientID}/responses/{queryID}`. Same shape\nas `ResponseWithCitations` minus `sessionId` and `analysisDate` (the caller already\nidentified the row by `queryID`).\n","properties":{"queryId":{"type":"string"},"aiProvider":{"type":"string","enum":["Anthropic","ChatGPT","Gemini","Perplexity","Grok"]},"prompt":{"type":"string"},"isBranded":{"type":"boolean"},"reply":{"type":"string"},"isBrandCited":{"type":"boolean"},"isBrandMentioned":{"type":"boolean"},"brandCitations":{"type":"integer"},"brandMentions":{"type":"integer"},"sentiment":{"type":"string","nullable":true},"sentimentScore":{"type":"number","format":"double","nullable":true},"citations":{"type":"array","items":{"$ref":"#/components/schemas/ResponseCitation"}}}},"ResponseCitation":{"type":"object","description":"One citation URL belonging to an AI response, with ownership and competition flags.","properties":{"fullUrl":{"type":"string"},"competitorName":{"type":"string","description":"Normalised competitor / domain name (e.g. \"amazon.com\")."},"positionRank":{"type":"integer","description":"Order in which the citation appeared in the response (1 is first)."},"isClientOwned":{"type":"boolean","description":"True when the URL belongs to the client's own domain."},"isInfluentialSource":{"type":"boolean","description":"True when the URL matches one of the client's tracked influential sources."},"isCompetition":{"type":"boolean","description":"True when the URL belongs to a tracked competitor domain."},"pathContainsBrand":{"type":"boolean","description":"True when the client's brand name appears in the URL path."}}},"CitationDomainsResponse":{"type":"object","properties":{"total":{"type":"integer","description":"Number of distinct cited domains returned."},"domains":{"type":"array","items":{"$ref":"#/components/schemas/CitationDomainCount"}}}},"CitationDomainCount":{"type":"object","properties":{"domain":{"type":"string","description":"Normalised cited domain (e.g. \"us.gildedwardrobe.com\")."},"count":{"type":"integer","description":"Total citation count for this domain across the requested scope."}}},"RadarSubmitRequest":{"type":"object","required":["brand","website","country","brand_identifier","prompts"],"properties":{"brand":{"type":"string","description":"Brand / company name being analysed.","example":"The Gilded Wardrobe"},"website":{"type":"string","description":"Brand website(s), comma-separated. Drives brand (owned-domain) citations.","example":"gildedwardrobe.com, gildedwardrobe.co.uk"},"country":{"type":"string","description":"ISO 3166-1 alpha-2 country code targeting the run. Must be one of Martekio's supported countries (the enum below); an unsupported value returns 400 unsupported_country.\n","example":"GB","enum":["AE","AR","AT","AU","BE","BO","BR","CA","CH","CL","CN","CO","CR","CU","DE","DK","DO","EC","EG","ES","FI","FR","GB","GT","HK","HN","ID","IE","IL","IN","IT","JP","KE","KR","MA","MX","MY","NG","NI","NL","NO","NZ","PA","PE","PH","PL","PR","PT","PY","QA","SA","SE","SG","SV","TH","TN","TR","US","UY","VE","VN","ZA"]},"brand_identifier":{"type":"string","description":"Brand identifier(s) for mention detection, comma-separated.","example":"The Gilded Wardrobe, GildedWardrobe, TheGildedWardrobe"},"citation_key":{"type":"string","deprecated":true,"description":"Deprecated alias for `brand_identifier`; accepted for backward compatibility. Prefer `brand_identifier`.","example":"The Gilded Wardrobe, GildedWardrobe, TheGildedWardrobe"},"prompts":{"type":"array","minItems":1,"description":"Prompts to analyse. 1 to the key's per-request maximum, each at most 500 characters.","items":{"type":"string"},"example":["best luxury sleepwear brands","which department stores near me stock The Gilded Wardrobe party dresses"]}}},"RadarSubmitResponse":{"type":"object","properties":{"meta":{"type":"object","properties":{"generated_by":{"type":"string"},"api_version":{"type":"string"},"generated_at":{"type":"string","format":"date-time"}}},"job_id":{"type":"string","format":"uuid","description":"Identifier for this run; use it with the status and results endpoints."},"status":{"type":"string","enum":["submitted"]},"client_id":{"type":"integer","description":"Internal id of the analysis space for this brand. The same brand and website on the same key reuse one space, so this value repeats across requests; use `job_id` (and `session_id` once complete) to address a single run."},"prompts":{"type":"object","properties":{"charged":{"type":"integer","description":"Prompts charged for this request."},"remaining":{"type":"integer","description":"Prompts remaining in the annual allowance after this request."},"annual_allowance":{"type":"integer","description":"The key's total annual prompt allowance (base plus any Signal Boost packs)."},"resets_at":{"type":"string","format":"date-time","description":"When the annual allowance resets."}}}}},"RadarStatusResponse":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["submitted","running","completed","failed"]},"client_id":{"type":"integer"},"session_id":{"type":"string","description":"The completed analysis session id; empty until the run completes."},"brand":{"type":"string"},"prompt_count":{"type":"integer","description":"Number of prompts submitted in this request."},"analysis":{"allOf":[{"$ref":"#/components/schemas/RadarAnalysisResult"}],"description":"Present only once `status` is `completed`: the full analysis results inline (identical to the body of GET /radar/analyses/{jobId}/results). Absent while the run is still in progress.\n"}}},"RadarResultsResponse":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["completed"]},"client_id":{"type":"integer"},"session_id":{"type":"string"},"brand":{"type":"string"},"analysis":{"$ref":"#/components/schemas/RadarAnalysisResult"}}},"RadarAnalysisResult":{"type":"object","description":"Completed analysis results across every assistant that ran.","properties":{"best_provider_branded":{"type":"string","description":"The AI assistant that cited the brand most on prompts that NAMED it. Says whether the brand's own pages are what an assistant reaches for when asked about it directly. Empty when no assistant cited the brand on a branded prompt."},"best_provider_nonbranded":{"type":"string","description":"The AI assistant that cited the brand most on prompts that did NOT name it. This is organic AI visibility and the harder of the two. Frequently empty, because a brand's own domain is rarely cited on a question that does not name it; treat an empty string as \"no assistant stood out\", not as a missing value. For a denser signal use total_references and non_branded_references on each provider row, which count the brand being mentioned rather than linked. Both fields resolve a tie by the canonical assistant order."},"total_citations_all_providers":{"type":"integer"},"average_sentiment":{"type":"number"},"providers":{"type":"array","description":"One entry per assistant that ran, always in Martekio's canonical assistant order (Claude, ChatGPT, Gemini, Perplexity, Grok, Google AI Mode, Google AI Overview), never ordered by score. The order is stable across runs, so two analyses can be compared position by position; an assistant that did not run is simply absent.","items":{"type":"object","properties":{"ai_provider":{"type":"string"},"sentiment_score":{"type":"number"},"total_citations":{"type":"integer"},"total_references":{"type":"integer"},"total_competitive_citations":{"type":"integer"},"non_branded_citations":{"type":"integer"},"non_branded_references":{"type":"integer"}}}},"brand_citations":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","description":"The prompt whose answer carried this citation","exactly as submitted.":null},"url":{"type":"string"},"ai_provider":{"type":"string"},"position_rank":{"type":"integer"}}}},"competitor_citations":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","description":"The prompt whose answer carried this citation","exactly as submitted.":null},"url":{"type":"string"},"competitor":{"type":"string"},"ai_provider":{"type":"string"},"position_rank":{"type":"integer"}}}}}},"RadarUsageResponse":{"type":"object","description":"The partner's current prompt balance and recent Radar request history.","properties":{"meta":{"type":"object","properties":{"generated_by":{"type":"string"},"api_version":{"type":"string"},"generated_at":{"type":"string","format":"date-time"}}},"prompts":{"type":"object","properties":{"annual_allowance":{"type":"integer","description":"The key's total annual prompt allowance (base 10,500 plus 5,500 per Radar Signal Boost)."},"used":{"type":"integer","description":"Requests used so far in the current annual period."},"remaining":{"type":"integer","description":"Prompts remaining in the current annual period."},"num_signal_boosts":{"type":"integer","description":"Number of Radar Signal Boost packs on the key."},"resets_at":{"type":"string","format":"date-time","description":"When the current annual period resets and usage returns to zero."}}},"requests":{"type":"array","description":"Recent Radar requests, newest first.","items":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"brand":{"type":"string"},"status":{"type":"string","enum":["submitted","running","completed","failed","rejected"]},"prompts_charged":{"type":"integer","description":"Prompts charged for this request (0 for rejected requests, and 0 once a failed run has been refunded)."},"prompts_refunded":{"type":"integer","description":"Prompts charged for this request and later given back because the run failed terminally and its results can never be retrieved. Absent when nothing was refunded; when present, prompts_charged is 0."},"session_id":{"type":"string","description":"The analysis session id once the run produced results."},"error_message":{"type":"string","description":"Why a request was rejected; absent for accepted runs."},"created_at":{"type":"string","format":"date-time"},"request_payload":{"type":"object","nullable":true,"description":"The exact JSON body the partner sent for this request."},"response_payload":{"type":"object","nullable":true,"description":"The exact JSON body Martekio returned for this request."}}}}}},"RadarError":{"type":"object","description":"Structured error returned by every Radar endpoint.","properties":{"error":{"type":"string","description":"Machine-readable error code (e.g. invalid_request, key_disabled, not_found, not_ready, run_in_progress, rate_limit_exceeded, service_unavailable)."},"message":{"type":"string","description":"Human-readable explanation."}}},"RadarQuotaError":{"type":"object","description":"Returned with 402 when a request needs more prompts than remain. Nothing is charged or run.","properties":{"error":{"type":"string","enum":["quota_exceeded"]},"message":{"type":"string"},"prompts_remaining":{"type":"integer"},"prompts_required":{"type":"integer"},"resets_at":{"type":"string","format":"date-time"}}}}}}