{"components":{"parameters":{"ClientId":{"description":"Client ID","in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},"JobId":{"description":"Job ID","in":"path","name":"job_id","required":true,"schema":{"type":"integer"}}},"responses":{"BadRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request body or parameters"},"Forbidden":{"content":{"application/json":{"example":{"error":"Forbidden"},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Insufficient role permissions"},"NotFound":{"content":{"application/json":{"example":{"error":"Not found"},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Resource not found"},"RateLimited":{"description":"Rate limit exceeded \u2014 wait and retry"},"Unauthorized":{"content":{"application/json":{"example":{"error":"Unauthorized"},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid API token"}},"schemas":{"ActivityItem":{"properties":{"color":{"enum":["success","warning","danger"],"type":"string"},"icon":{"type":"string"},"text":{"type":"string"},"time":{"type":"number"},"url":{"type":"string"}},"type":"object"},"Added":{"properties":{"added":{"minimum":0,"type":"integer"}},"required":["added"],"type":"object"},"AeoCheck":{"properties":{"checked_prompts":{"type":"integer"},"client_id":{"type":"integer"},"created_at":{"type":"number"},"errors":{"type":"integer"},"finished_at":{"nullable":true,"type":"number"},"id":{"type":"integer"},"status":{"enum":["pending","running","done","error","none"],"type":"string"},"total_prompts":{"type":"integer"}},"type":"object"},"AeoHistoryPoint":{"properties":{"check_id":{"type":"integer"},"checked_at":{"type":"number"},"llm_provider":{"type":"string"},"visibility_score":{"type":"number"}},"type":"object"},"AeoPrompt":{"properties":{"category":{"type":"string"},"client_id":{"type":"integer"},"created_at":{"type":"number"},"enabled":{"enum":[0,1],"type":"integer"},"id":{"type":"integer"},"intent":{"type":"string"},"prompt_text":{"type":"string"}},"type":"object"},"AeoPromptInput":{"properties":{"category":{"type":"string"},"intent":{"type":"string"},"prompt_text":{"type":"string"}},"required":["prompt_text"],"type":"object"},"AeoResult":{"properties":{"check_id":{"type":"integer"},"created_at":{"type":"number"},"id":{"type":"integer"},"llm_provider":{"type":"string"},"mention_position":{"nullable":true,"type":"integer"},"our_domain_mentioned":{"type":"boolean"},"prompt_id":{"type":"integer"},"prompt_text":{"type":"string"},"response_text":{"type":"string"},"sentiment":{"enum":["positive","neutral","negative",""],"type":"string"},"visibility_score":{"type":"number"}},"type":"object"},"ApiKeyCreate":{"properties":{"name":{"example":"n8n workflow","maxLength":100,"type":"string"}},"required":["name"],"type":"object"},"ApiKeyCreated":{"properties":{"id":{"type":"integer"},"key":{"description":"Raw API key starting with sk-. Shown only once \u2014 store immediately.","example":"sk-AbCdEf\u2026","type":"string"},"name":{"type":"string"}},"type":"object"},"ApiKeyMeta":{"properties":{"created_at":{"description":"Unix timestamp","type":"number"},"expires_at":{"nullable":true,"type":"number"},"id":{"type":"integer"},"is_active":{"enum":[0,1],"type":"integer"},"last_used_at":{"description":"Unix timestamp, null if never used","nullable":true,"type":"number"},"name":{"example":"Zapier","type":"string"}},"type":"object"},"AsoActionResult":{"additionalProperties":true,"properties":{"errors":{"items":{"type":"string"},"type":"array"},"imported":{"type":"integer"},"listing_snapshots":{"type":"integer"},"message":{"type":"string"},"navigate":{"type":"string"},"ok":{"type":"boolean"},"rankings":{"type":"integer"},"reviews":{"type":"integer"},"task_id":{"type":"integer"}},"type":"object"},"AsoApp":{"additionalProperties":true,"properties":{"app_identifier":{"type":"string"},"app_name":{"type":"string"},"app_url":{"type":"string"},"category":{"type":"string"},"client_id":{"type":"integer"},"country":{"type":"string"},"created_at":{"format":"float","type":"number"},"developer":{"type":"string"},"id":{"type":"integer"},"language":{"type":"string"},"notes":{"type":"string"},"store":{"enum":["app_store","google_play"],"type":"string"},"updated_at":{"format":"float","type":"number"}},"required":["id","client_id","store","app_name","country","language"],"type":"object"},"AsoAppCreate":{"additionalProperties":false,"properties":{"app_identifier":{"description":"Bundle ID, package name, or App Store ID.","maxLength":220,"type":"string"},"app_name":{"maxLength":180,"type":"string"},"app_url":{"description":"Public App Store or Google Play URL.","type":"string"},"category":{"maxLength":160,"type":"string"},"country":{"default":"US","example":"US","type":"string"},"developer":{"maxLength":180,"type":"string"},"language":{"default":"en","example":"en","type":"string"},"notes":{"maxLength":2000,"type":"string"},"store":{"default":"app_store","enum":["app_store","google_play"],"type":"string"}},"required":["app_name"],"type":"object"},"AsoFullRefreshAction":{"additionalProperties":true,"properties":{"app_id":{"description":"Optional tracked ASO app profile ID.","type":"integer"},"limit":{"default":25,"maximum":100,"minimum":1,"type":"integer"},"max_apps":{"default":5,"maximum":10,"minimum":1,"type":"integer"},"max_keywords":{"default":25,"maximum":100,"minimum":1,"type":"integer"},"review_limit":{"maximum":100,"minimum":1,"type":"integer"},"store":{"enum":["app_store","google_play"],"type":"string"},"use_paid_fallback":{"description":"Use configured paid ASO fallback only when explicitly allowed.","type":"boolean"}},"type":"object"},"AsoKeyword":{"additionalProperties":true,"properties":{"client_id":{"type":"integer"},"country":{"type":"string"},"id":{"type":"integer"},"language":{"type":"string"},"latest_checked_at":{"format":"float","nullable":true,"type":"number"},"latest_position":{"nullable":true,"type":"integer"},"query":{"type":"string"},"store":{"type":"string"}},"required":["id","client_id","store","query","country","language"],"type":"object"},"AsoKeywordAction":{"additionalProperties":false,"properties":{"count":{"default":50,"maximum":300,"minimum":5,"type":"integer"},"country":{"default":"US","example":"GB","type":"string"},"language":{"default":"en","type":"string"},"store":{"default":"app_store","enum":["app_store","google_play"],"type":"string"}},"type":"object"},"AsoKeywordCreate":{"additionalProperties":false,"properties":{"cluster":{"type":"string"},"country":{"default":"US","type":"string"},"difficulty":{"type":"number"},"intent":{"type":"string"},"language":{"default":"en","type":"string"},"notes":{"type":"string"},"query":{"maxLength":300,"type":"string"},"search_volume":{"minimum":0,"type":"integer"},"source":{"default":"manual","type":"string"},"store":{"default":"app_store","enum":["app_store","google_play"],"type":"string"},"tags":{"type":"string"}},"required":["query"],"type":"object"},"AsoListingAuditAction":{"additionalProperties":true,"properties":{"app_id":{"description":"Optional tracked ASO app profile ID.","type":"integer"},"max_apps":{"default":5,"maximum":10,"minimum":1,"type":"integer"},"store":{"enum":["app_store","google_play"],"type":"string"},"use_paid_fallback":{"description":"Use configured paid ASO fallback only when explicitly allowed.","type":"boolean"}},"type":"object"},"AsoRankingAction":{"additionalProperties":true,"properties":{"count":{"maximum":100,"minimum":1,"type":"integer"},"max_keywords":{"default":25,"maximum":100,"minimum":1,"type":"integer"},"use_paid_fallback":{"description":"Use configured paid ASO fallback only when explicitly allowed.","type":"boolean"}},"type":"object"},"AsoReviewsAction":{"additionalProperties":false,"properties":{"app_id":{"description":"Optional tracked ASO app profile ID.","type":"integer"},"limit":{"default":25,"maximum":100,"minimum":1,"type":"integer"},"max_apps":{"default":3,"maximum":10,"minimum":1,"type":"integer"},"store":{"enum":["app_store","google_play"],"type":"string"}},"type":"object"},"AsoSummary":{"additionalProperties":true,"properties":{"app_count":{"type":"integer"},"competitor_count":{"type":"integer"},"countries":{"items":{"type":"string"},"type":"array"},"keyword_count":{"type":"integer"},"keyword_distribution":{"additionalProperties":true,"type":"object"},"languages":{"items":{"type":"string"},"type":"array"},"listing_audit":{"additionalProperties":true,"type":"object"},"paid_fallback_health":{"additionalProperties":true,"type":"object"},"prioritized_backlog":{"items":{"additionalProperties":true,"type":"object"},"type":"array"},"ranking_alerts":{"items":{"additionalProperties":true,"type":"object"},"type":"array"},"reviews":{"additionalProperties":true,"type":"object"},"stores":{"items":{"type":"string"},"type":"array"},"visibility":{"additionalProperties":true,"type":"object"},"visibility_trend":{"additionalProperties":true,"type":"object"}},"required":["app_count","competitor_count","keyword_count","visibility"],"type":"object"},"CheckStarted":{"properties":{"job_id":{"type":"integer"},"keywords":{"type":"integer"},"status":{"example":"started","type":"string"}},"type":"object"},"ClientCreate":{"properties":{"domain":{"example":"allright.com","type":"string"},"name":{"example":"Allright Casino","maxLength":120,"type":"string"}},"required":["name","domain"],"type":"object"},"ClientDetail":{"allOf":[{"$ref":"#/components/schemas/ClientSummary"},{"properties":{"competitors":{"items":{"$ref":"#/components/schemas/Competitor"},"type":"array"},"keywords":{"items":{"$ref":"#/components/schemas/Keyword"},"type":"array"}},"type":"object"}]},"ClientSummary":{"properties":{"created_at":{"description":"Unix timestamp","type":"number"},"domain":{"type":"string"},"id":{"type":"integer"},"is_archived":{"enum":[0,1],"type":"integer"},"keyword_count":{"type":"integer"},"name":{"type":"string"},"notes":{"type":"string"},"sitemap_url":{"type":"string"},"updated_at":{"description":"Unix timestamp","type":"number"},"user_id":{"type":"integer"}},"type":"object"},"ClientUpdate":{"additionalProperties":false,"properties":{"domain":{"example":"allright.com","type":"string"},"is_archived":{"description":"Archive or restore the client.","type":"boolean"},"name":{"example":"Allright Casino PL","maxLength":120,"type":"string"},"notes":{"description":"Internal analyst notes for this client.","type":"string"},"screenshot_proxy":{"description":"Optional proxy used for SERP screenshots.","type":"string"},"sitemap_url":{"example":"https://allright.com/sitemap.xml","type":"string"}},"type":"object"},"Competitor":{"properties":{"client_id":{"type":"integer"},"domain":{"type":"string"},"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"},"CompetitorCreate":{"additionalProperties":false,"properties":{"domain":{"example":"betsson.com","type":"string"},"name":{"example":"Betsson","type":"string"}},"required":["domain"],"type":"object"},"CompetitorInput":{"properties":{"domain":{"type":"string"},"name":{"type":"string"}},"required":["domain"],"type":"object"},"CompetitorUpdate":{"additionalProperties":false,"properties":{"domain":{"example":"betsson.com","type":"string"},"name":{"example":"Betsson","type":"string"}},"type":"object"},"CompetitorVisibility":{"properties":{"competitors":{"items":{"properties":{"domain":{"type":"string"},"name":{"type":"string"},"top10":{"type":"integer"},"top3":{"type":"integer"},"top30":{"type":"integer"},"total":{"type":"integer"},"visibility":{"type":"number"}},"type":"object"},"type":"array"}},"type":"object"},"ConflictError":{"properties":{"error":{"type":"string"},"job_id":{"description":"ID of the existing running job","type":"integer"}},"type":"object"},"Deleted":{"properties":{"deleted":{"example":true,"type":"boolean"}},"type":"object"},"Error":{"properties":{"error":{"description":"Human-readable error message","type":"string"}},"required":["error"],"type":"object"},"HealthResponse":{"properties":{"config":{"enum":["ok","missing or invalid"],"type":"string"},"config_errors":{"items":{"type":"string"},"type":"array"},"database":{"enum":["ok","error"],"type":"string"},"fallback_providers":{"items":{"type":"string"},"type":"array"},"provider":{"type":"string"},"scheduler":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"type":"object"},"IndexingStatus":{"properties":{"monitor_history":{"items":{"properties":{"checked_at":{"type":"number"},"new_pages":{"type":"integer"},"total_indexed":{"type":"integer"}},"type":"object"},"type":"array"},"recent_tasks":{"items":{"properties":{"finished_at":{"nullable":true,"type":"number"},"id":{"type":"integer"},"started_at":{"type":"number"},"status":{"description":"done = at least one channel accepted and none failed; partial = some accepted and some were rejected; failed = every attempt was rejected; skipped = nothing was attempted; error = the run itself raised.","enum":["running","done","partial","failed","skipped","error"],"type":"string"},"urls_collected":{"description":"How many distinct URLs the run gathered from the sitemap and managed content pages. This is the size of the batch we tried to submit, not a success count.","type":"integer"},"urls_submitted":{"description":"How many distinct URLs a submission channel actually accepted. Zero when every channel was skipped or rejected the batch, even though urls_collected is large.","type":"integer"}},"type":"object"},"type":"array"},"summary":{"properties":{"latest_monitor":{"description":"The most recent indexation monitoring sample, or null if monitoring has never run.","nullable":true,"type":"object"},"latest_task":{"description":"The most recent indexing run, or null if none has been started.","nullable":true,"properties":{"finished_at":{"nullable":true,"type":"number"},"id":{"type":"integer"},"started_at":{"type":"number"},"status":{"description":"done = at least one channel accepted and none failed; partial = some accepted and some were rejected; failed = every attempt was rejected; skipped = nothing was attempted (no IndexNow key, no Google credentials); error = the run itself raised.","enum":["running","done","partial","failed","skipped","error"],"type":"string"},"urls_collected":{"description":"How many distinct URLs the run gathered from the sitemap and managed content pages. This is the size of the batch we tried to submit, not a success count.","type":"integer"},"urls_submitted":{"description":"How many distinct URLs a submission channel actually accepted. Zero when every channel was skipped or rejected the batch, even though urls_collected is large.","type":"integer"}},"type":"object"},"total_tasks":{"description":"Number of indexing runs recorded for this client.","type":"integer"}},"type":"object"}},"type":"object"},"JobResults":{"properties":{"competitors":{"additionalProperties":{"type":"string"},"description":"Map of domain -> display name","type":"object"},"domain":{"type":"string"},"prev_job_id":{"nullable":true,"type":"integer"},"results":{"items":{"$ref":"#/components/schemas/KeywordResult"},"type":"array"}},"type":"object"},"JobStatus":{"allOf":[{"$ref":"#/components/schemas/JobSummary"},{"properties":{"progress_pct":{"example":75.5,"type":"number"},"results":{"description":"Only present when status=done","items":{"$ref":"#/components/schemas/KeywordResult"},"type":"array"}},"type":"object"}]},"JobSummary":{"properties":{"api_calls":{"type":"integer"},"cached_hits":{"type":"integer"},"checked_keywords":{"type":"integer"},"client_domain":{"type":"string"},"client_id":{"type":"integer"},"client_name":{"type":"string"},"created_at":{"type":"number"},"errors":{"type":"integer"},"finished_at":{"nullable":true,"type":"number"},"id":{"type":"integer"},"report_path":{"type":"string"},"share_token":{"type":"string"},"status":{"enum":["pending","running","done","cancelled","error: ..."],"type":"string"},"total_keywords":{"type":"integer"}},"type":"object"},"JsonRpcRequest":{"properties":{"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"jsonrpc":{"example":"2.0","type":"string"},"method":{"example":"tools/list","type":"string"},"params":{"additionalProperties":true,"type":"object"}},"required":["jsonrpc","method"],"type":"object"},"JsonRpcResponse":{"properties":{"error":{"additionalProperties":true,"type":"object"},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"jsonrpc":{"example":"2.0","type":"string"},"result":{"additionalProperties":true,"type":"object"}},"required":["jsonrpc","id"],"type":"object"},"Keyword":{"properties":{"client_id":{"type":"integer"},"cluster":{"type":"string"},"device":{"default":"desktop","enum":["desktop","mobile","both"],"type":"string"},"geo":{"example":"US","type":"string"},"id":{"type":"integer"},"intent":{"enum":["informational","commercial","transactional","navigational",""],"type":"string"},"lang":{"example":"en","type":"string"},"query":{"type":"string"},"source":{"type":"string"},"tags":{"type":"string"}},"type":"object"},"KeywordHistoryPoint":{"properties":{"checked_at":{"type":"number"},"date":{"example":"24 Mar","type":"string"},"delta":{"nullable":true,"type":"integer"},"is_lost":{"type":"integer"},"is_new":{"type":"integer"},"job_id":{"type":"integer"},"position":{"nullable":true,"type":"integer"}},"type":"object"},"KeywordInput":{"properties":{"cluster":{"type":"string"},"device":{"enum":["desktop","mobile","both"],"type":"string"},"geo":{"type":"string"},"intent":{"type":"string"},"lang":{"type":"string"},"query":{"type":"string"},"tags":{"type":"string"}},"required":["query"],"type":"object"},"KeywordPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Keyword"},"type":"array"},"page":{"type":"integer"},"pages":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}},"type":"object"},"KeywordResult":{"properties":{"cached":{"type":"boolean"},"cluster":{"type":"string"},"delta":{"nullable":true,"type":"integer"},"device":{"type":"string"},"geo":{"type":"string"},"is_lost":{"type":"boolean"},"is_new":{"type":"boolean"},"lang":{"type":"string"},"matched_page":{"type":"string"},"parasite_domains":{"items":{"type":"object"},"type":"array"},"position":{"nullable":true,"type":"integer"},"prev_position":{"nullable":true,"type":"integer"},"query":{"type":"string"},"serp_features":{"items":{"type":"string"},"type":"array"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"KeywordUpdate":{"additionalProperties":false,"properties":{"brand":{"type":"string"},"cluster":{"example":"bonus","type":"string"},"device":{"enum":["desktop","mobile","both"],"example":"desktop","type":"string"},"difficulty":{"type":"number"},"geo":{"example":"us","type":"string"},"intent":{"example":"commercial","type":"string"},"lang":{"example":"en","type":"string"},"model":{"type":"string"},"needs_dedicated_page":{"type":"boolean"},"query":{"example":"best casino bonuses","type":"string"},"reference_detected":{"type":"string"},"search_volume":{"minimum":0,"type":"integer"},"source":{"example":"api","type":"string"},"tags":{"example":"priority,bonus","type":"string"}},"type":"object"},"Message":{"properties":{"body":{"type":"string"},"created_at":{"type":"number"},"id":{"type":"integer"},"is_translated":{"type":"boolean"},"original_body":{"type":"string"},"sender_id":{"type":"integer"},"sender_name":{"type":"string"}},"type":"object"},"Movers":{"properties":{"gainers":{"items":{"properties":{"delta":{"type":"integer"},"geo":{"type":"string"},"lang":{"type":"string"},"position":{"type":"integer"},"prev_position":{"type":"integer"},"query":{"type":"string"}},"type":"object"},"type":"array"},"losers":{"items":{"properties":{"delta":{"type":"integer"},"position":{"nullable":true,"type":"integer"},"prev_position":{"type":"integer"},"query":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"OpportunityKeyword":{"properties":{"cluster":{"type":"string"},"geo":{"type":"string"},"lang":{"type":"string"},"opportunity_score":{"maximum":5,"minimum":2,"type":"integer"},"position":{"type":"integer"},"query":{"type":"string"}},"type":"object"},"ReportViewStats":{"properties":{"stats":{"properties":{"last_viewed":{"nullable":true,"type":"number"},"total_views":{"type":"integer"},"unique_ips":{"type":"integer"},"views_7d":{"type":"integer"},"views_today":{"type":"integer"}},"type":"object"},"timeline":{"items":{"type":"object"},"type":"array"}},"type":"object"},"SearchResult":{"properties":{"icon":{"type":"string"},"id":{"type":"integer"},"subtitle":{"type":"string"},"title":{"type":"string"},"type":{"enum":["client","job","nav"],"type":"string"},"url":{"type":"string"}},"type":"object"},"SerpFeatures":{"properties":{"history":{"items":{"type":"object"},"type":"array"},"summary":{"additionalProperties":{"type":"integer"},"example":{"Answer Overview":3,"Featured Snippet":5,"PAA":12},"type":"object"},"won_lost":{"properties":{"lost":{"items":{"type":"object"},"type":"array"},"won":{"items":{"type":"object"},"type":"array"}},"type":"object"}},"type":"object"},"SerperModes":{"properties":{"configured":{"properties":{"local_search":{"type":"boolean"},"local_verticals":{"type":"boolean"},"serper":{"type":"boolean"}},"type":"object"},"modes":{"items":{"properties":{"default_source":{"type":"string"},"methods":{"items":{"type":"string"},"type":"array"},"mode":{"type":"string"},"sources":{"items":{"type":"string"},"type":"array"}},"type":"object"},"type":"array"},"provider":{"example":"serper-surface","type":"string"}},"type":"object"},"SerperRequest":{"properties":{"autocorrect":{"type":"boolean"},"device":{"default":"desktop","enum":["desktop","mobile"],"type":"string"},"geo":{"description":"Country code (also accepts gl)","type":"string"},"lang":{"description":"Language code (also accepts hl)","type":"string"},"location":{"type":"string"},"num":{"default":10,"type":"integer"},"page":{"default":1,"type":"integer"},"q":{"type":"string"},"source":{"default":"auto","enum":["auto","local","serper"],"type":"string"}},"required":["q"],"type":"object"},"SerperResponse":{"properties":{"answerBox":{"type":"object"},"credits":{"type":"integer"},"knowledgeGraph":{"type":"object"},"organic":{"items":{"properties":{"domain":{"type":"string"},"link":{"type":"string"},"position":{"type":"integer"},"snippet":{"type":"string"},"title":{"type":"string"}},"type":"object"},"type":"array"},"peopleAlsoAsk":{"items":{"type":"object"},"type":"array"},"relatedSearches":{"items":{"type":"object"},"type":"array"},"searchParameters":{"properties":{"gl":{"type":"string"},"hl":{"type":"string"},"num":{"type":"integer"},"q":{"type":"string"},"type":{"type":"string"}},"type":"object"}},"type":"object"},"VisibilityPoint":{"properties":{"avg_position":{"nullable":true,"type":"number"},"checked_at":{"type":"number"},"date":{"example":"24 Mar","type":"string"},"job_id":{"type":"integer"},"not_found":{"type":"integer"},"top10":{"type":"integer"},"top3":{"type":"integer"},"top30":{"type":"integer"},"total":{"type":"integer"},"visibility":{"type":"number"}},"type":"object"}},"securitySchemes":{"apiKeyAuth":{"description":"Named API key created in Account \u2192 API Keys. Include as: X-API-Key: sk-YOUR_KEY","in":"header","name":"X-API-Key","type":"apiKey"},"bearerAuth":{"description":"Named API key (sk-\u2026) or legacy user token. Include as: Authorization: Bearer sk-YOUR_KEY","scheme":"bearer","type":"http"},"sessionAuth":{"description":"Flask session cookie (used by browser-based endpoints)","in":"cookie","name":"session","type":"apiKey"}}},"info":{"description":"REST API and MCP integration surface for SERP Checker. Use `X-API-Key: sk-...` or `Authorization: Bearer sk-...` for API and MCP calls. Legacy per-user bearer tokens are accepted for REST compatibility.","title":"SERP Checker / SEO Platform API","version":"1.0.0"},"openapi":"3.0.0","paths":{"/api/ai-chat/actions/clients":{"get":{"description":"Return the list of accessible clients for action pickers.","operationId":"get_ai_chat_action_clients","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return the list of accessible clients for action pickers.","tags":["Strategy Assistant"]}},"/api/ai-chat/execute":{"post":{"description":"Execute a system action triggered by the Strategy Assistant.\n\nRequires user confirmation on the frontend \u2014 this endpoint is called\nonly after the user approves the action in the UI.","operationId":"post_ai_chat_execute_action","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Execute a system action triggered by the Strategy Assistant.","tags":["Strategy Assistant"]}},"/api/ai-chat/kw-task/{task_id}":{"get":{"description":"Poll DB status of a background keyword generation task.","operationId":"get_ai_chat_kw_task_status","parameters":[{"in":"path","name":"task_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Poll DB status of a background keyword generation task.","tags":["Strategy Assistant"]}},"/api/ai-chat/kw-tasks/recent/{client_id}":{"get":{"description":"Return 5 most recent keyword gen tasks for a client (debug endpoint).","operationId":"get_ai_chat_kw_tasks_recent","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return 5 most recent keyword gen tasks for a client (debug endpoint).","tags":["Strategy Assistant"]}},"/api/ai-chat/sessions":{"get":{"description":"Returns all strategy assistant sessions for the authenticated user.","operationId":"listAiChatSessions","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Sessions list"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List strategy assistant sessions","tags":["Messaging"]},"post":{"description":"Creates a new strategy assistant session, optionally scoped to a specific client.","operationId":"createAiChatSession","requestBody":{"content":{"application/json":{"schema":{"properties":{"client_id":{"description":"Optional client scope","type":"integer"},"title":{"default":"New Chat","type":"string"}},"type":"object"}}}},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Session created"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Create strategy assistant session","tags":["Messaging"]}},"/api/ai-chat/sessions/{session_id}":{"delete":{"description":"Permanently deletes a strategy assistant session and all its messages.","operationId":"deleteAiChatSession","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Delete strategy assistant session","tags":["Messaging"]},"get":{"description":"Returns all messages in a strategy assistant session.","operationId":"getAiChatSession","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Session with messages"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get strategy assistant session","tags":["Messaging"]}},"/api/ai-chat/sessions/{session_id}/send":{"post":{"description":"Sends a message to the strategy assistant and returns the response. The assistant has full access to the client context if the session is client-scoped.","operationId":"sendAiChatMessage","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"message":{"description":"User message text","type":"string"}},"required":["message"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"reply":{"type":"string"},"session_id":{"type":"integer"}},"type":"object"}}},"description":"Assistant response"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Send strategy assistant message","tags":["Messaging"]}},"/api/ai-chat/sessions/{sid}/rename":{"post":{"description":"Renames a strategy assistant session owned by the authenticated user.","operationId":"renameAiChatSession","parameters":[{"in":"path","name":"sid","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"title":"May visibility review"},"schema":{"properties":{"title":{"maxLength":120,"type":"string"}},"required":["title"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Session renamed"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"sessionAuth":[]}],"summary":"Rename strategy assistant session","tags":["Strategy Assistant"]}},"/api/ai-chat/sessions/{sid}/scope":{"post":{"description":"Updates the client/report scope used by a strategy assistant session when answering questions or executing actions.","operationId":"updateAiChatScope","parameters":[{"in":"path","name":"sid","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"client_id":1,"report_id":42},"schema":{"additionalProperties":true,"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Scope updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"sessionAuth":[]}],"summary":"Update strategy assistant scope","tags":["Strategy Assistant"]}},"/api/ai-chat/sessions/{sid}/share":{"delete":{"description":"Revoke the public share link for a session.","operationId":"delete_ai_chat_revoke_share","parameters":[{"in":"path","name":"sid","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Revoke the public share link for a session.","tags":["Strategy Assistant"]},"post":{"description":"Generate (or renew) a public share link for a session.","operationId":"post_ai_chat_create_share","parameters":[{"in":"path","name":"sid","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Generate (or renew) a public share link for a session.","tags":["Strategy Assistant"]}},"/api/ai-chat/sessions/{sid}/system-message":{"post":{"description":"Save an action result or data-query response to chat history.\n\nCalled from the frontend after an action executes successfully so\nthe result persists across sessions and feeds the learning engine.","operationId":"post_ai_chat_add_system_message","parameters":[{"in":"path","name":"sid","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Save an action result or data-query response to chat history.","tags":["Strategy Assistant"]}},"/api/ai-chat/share/{token}":{"get":{"description":"Public JSON API for a shared session. Returns user+assistant messages only.","operationId":"get_ai_chat_public_chat_api","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Public JSON API for a shared session. Returns user+assistant messages only.","tags":["Strategy Assistant"]}},"/api/audit-deep/{lead_id}":{"get":{"description":"Poll the deep-audit result for a captured lead.\n\nGuarded by the lead's claim token, the same secret the browser already\nholds from /api/audit-lite \u2014 so one visitor can never read another's\nlive positions by walking lead ids.","operationId":"get_main_api_audit_deep","parameters":[{"in":"path","name":"lead_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Poll the deep-audit result for a captured lead.","tags":["Utility"]}},"/api/audit-lead":{"post":{"description":"Registration-gate form: attach contact details to an audit lead.\n\nLead binding is protected by a claim token: only the browser that created\nthe lead (it received claim_token from /api/audit-lite) may attach contact\ndetails to it or carry it into signup. Any id/token mismatch falls back to\ncreating a fresh cold lead, so the endpoint is not an existence oracle and\ncannot overwrite another prospect's contact details.","operationId":"post_main_api_audit_lead","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Registration-gate form: attach contact details to an audit lead.","tags":["Utility"]}},"/api/audit-lite":{"post":{"description":"Generated documentation for `POST /api/audit-lite`.","operationId":"post_main_api_audit_lite","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"POST /api/audit-lite","tags":["Utility"]}},"/api/clients/{client_id}/recommendations/refresh":{"post":{"description":"Invalidates and regenerates all cached recommendations for a client.","operationId":"refreshRecommendations","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Refreshed"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Refresh all recommendations","tags":["Analytics"]}},"/api/clients/{client_id}/recommendations/{rec_type}":{"get":{"description":"Returns cached recommendations for a client. Valid rec_type values: post_check, client_health, analytics_insight, keyword_strategy, competitor_insight, priority_actions, comprehensive_report, campaign_strategy.","operationId":"getRecommendations","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"path","name":"rec_type","required":true,"schema":{"enum":["post_check","client_health","analytics_insight","keyword_strategy","competitor_insight","priority_actions","comprehensive_report","campaign_strategy"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Recommendations"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Get recommendations","tags":["Analytics"]}},"/api/clients/{client_id}/recommendations/{rec_type}/generate":{"post":{"description":"Generates fresh recommendations for a client. Valid rec_type values: post_check, client_health, analytics_insight, keyword_strategy, competitor_insight, priority_actions, comprehensive_report, campaign_strategy.","operationId":"generateRecommendations","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"path","name":"rec_type","required":true,"schema":{"enum":["post_check","client_health","analytics_insight","keyword_strategy","competitor_insight","priority_actions","comprehensive_report","campaign_strategy"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Recommendations generated"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Generate recommendations","tags":["Analytics"]}},"/api/clients/{client_id}/report/snapshots":{"get":{"description":"Returns saved report snapshot metadata for a client.","operationId":"listReportSnapshots","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Snapshots list"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List report snapshots","tags":["Analytics"]}},"/api/clients/{client_id}/report/snapshots/save":{"post":{"description":"Saves the current comprehensive report as a named snapshot.","operationId":"saveReportSnapshot","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"example":{"days":30},"schema":{"properties":{"date_from":{"description":"Period start (YYYY-MM-DD).","type":"string"},"date_to":{"description":"Period end (YYYY-MM-DD).","type":"string"},"days":{"description":"Trailing window in days; used when no explicit dates are given.","type":"integer"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Snapshot saved"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Save report snapshot","tags":["Analytics"]}},"/api/clients/{client_id}/report/snapshots/{snap_id}/delete":{"post":{"description":"Deletes a saved report snapshot.","operationId":"deleteReportSnapshot","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"path","name":"snap_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Delete report snapshot","tags":["Analytics"]}},"/api/docs":{"get":{"description":"Authenticated Swagger UI page for exploring and trying REST API calls.","operationId":"swaggerUi","parameters":[],"responses":{"200":{"content":{"text/html":{"schema":{"type":"string"}}},"description":"Swagger UI HTML"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"sessionAuth":[]}],"summary":"Swagger UI","tags":["Utility"]}},"/api/examples":{"get":{"description":"Quick-start API examples with curl / Python / JS snippets.","operationId":"get_main_api_examples","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Quick-start API examples with curl / Python / JS snippets.","tags":["Utility"]}},"/api/messages/unread-count":{"get":{"description":"Returns the number of unread messages for the authenticated user.","operationId":"getUnreadCount","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"count":{"type":"integer"}},"type":"object"}}},"description":"Unread count"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Unread message count","tags":["Messaging"]}},"/api/messages/{conv_id}/poll":{"get":{"description":"Returns messages newer than a given timestamp. Pass ?since=<unix_ts>.","operationId":"pollMessages","parameters":[{"in":"path","name":"conv_id","required":true,"schema":{"type":"integer"}},{"in":"query","name":"since","schema":{"default":0,"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"messages":{"items":{"$ref":"#/components/schemas/Message"},"type":"array"}},"type":"object"}}},"description":"New messages"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Poll for new messages","tags":["Messaging"]}},"/api/messages/{msg_id}/translate":{"get":{"description":"Returns a translated message. Pass ?lang=en|ru|uk.","operationId":"translateMessage","parameters":[{"in":"path","name":"msg_id","required":true,"schema":{"type":"integer"}},{"in":"query","name":"lang","required":true,"schema":{"enum":["en","ru","uk"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"original_body":{"type":"string"},"translated_body":{"type":"string"}},"type":"object"}}},"description":"Translation"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Translate a message","tags":["Messaging"]}},"/api/openapi.json":{"get":{"description":"Returns the generated OpenAPI 3 document for the current deployment. Use this with Swagger UI, code generators, or MCP clients that need the REST endpoint inventory.","operationId":"downloadOpenApiJson","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"OpenAPI 3 document"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]},{"sessionAuth":[]}],"summary":"Download OpenAPI document","tags":["Utility"]}},"/api/recent-activity":{"get":{"description":"Returns up to 10 recent job events for the notification bell.","operationId":"recentActivity","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ActivityItem"},"type":"array"}}},"description":"Activity items"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Recent activity feed","tags":["Jobs"]}},"/api/search":{"get":{"description":"Searches clients, jobs, and navigation items. Requires session auth. Minimum 2 characters.","operationId":"globalSearch","parameters":[{"in":"query","name":"q","required":true,"schema":{"minLength":2,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SearchResult"},"type":"array"}}},"description":"Search results (max 20)"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Global search (command palette)","tags":["Clients"]}},"/api/v1/active-jobs":{"get":{"description":"Return all actively running/pending jobs for the current user across all job types.\n\nAggregates SERP check jobs, AEO discovery, and AEO checks. Used by the\nglobal job-status widget in base.html so users can track progress after\nclosing a modal or navigating away.","operationId":"get_api_v1_api_active_jobs","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return all actively running/pending jobs for the current user across all job types.","tags":["Utility"]}},"/api/v1/analytics-reports":{"get":{"description":"Returns all GA/GSC analytics reports visible to the user.","operationId":"listAnalyticsReports","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Reports list"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List analytics reports","tags":["AnalyticsReports"]}},"/api/v1/analytics-reports/{report_id}":{"get":{"description":"Returns a single GA/GSC analytics report with saved analysis.","operationId":"getAnalyticsReport","parameters":[{"in":"path","name":"report_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Report details"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get analytics report","tags":["AnalyticsReports"]}},"/api/v1/api-keys":{"get":{"description":"Returns metadata for the authenticated user's named API keys. Raw key values and key hashes are never returned.","operationId":"listApiKeys","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ApiKeyMeta"},"type":"array"}}},"description":"API key metadata for the current user"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List API keys","tags":["APIKeys"]},"post":{"description":"Creates a named API key for REST and MCP integrations. The raw `sk-...` secret is returned once in this response; store it immediately.","operationId":"createApiKey","parameters":[],"requestBody":{"content":{"application/json":{"example":{"name":"n8n production connector"},"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}},"required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}},"description":"API key created; raw key is shown once"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Create API key","tags":["APIKeys"]}},"/api/v1/api-keys/{key_id}":{"delete":{"description":"Permanently removes one of the authenticated user's API keys. Deleting a key immediately prevents REST and MCP authentication with it.","operationId":"deleteApiKey","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"API key deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete API key","tags":["APIKeys"]}},"/api/v1/clients":{"get":{"description":"Returns all client/site records visible to the authenticated user. Each object includes the client ID, name, domain, and summary counters.\n\nAll `/api/v1` list endpoints (clients, jobs, keywords, competitors) accept optional `limit` and `offset` query parameters for deterministic pagination and return `X-Total-Count`, `X-Limit`, `X-Offset`, and `X-Next-Offset` headers (`X-Next-Offset` is empty once the list is exhausted). With no parameters the full array is returned, for backward compatibility.","operationId":"listClients","parameters":[{"description":"Maximum rows to return for this page (1-1000).","in":"query","name":"limit","required":false,"schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"description":"Number of rows to skip before the page starts.","in":"query","name":"offset","required":false,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":[{"competitor_count":5,"domain":"allright.com","id":1,"keyword_count":142,"last_check":"2026-04-30T10:00:00Z","name":"Allright Casino"}],"schema":{"items":{"$ref":"#/components/schemas/ClientSummary"},"type":"array"}}},"description":"Array of client objects"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List clients","tags":["Clients"]}},"/api/v1/clients/create":{"post":{"description":"Creates a new client/site record for the authenticated user's organisation.","operationId":"createClient","parameters":[],"requestBody":{"content":{"application/json":{"example":{"domain":"allright.com","name":"Allright Casino"},"schema":{"$ref":"#/components/schemas/ClientCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"domain":"allright.com","id":7,"name":"Allright Casino"}}},"description":"Client created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Create client","tags":["Clients"]}},"/api/v1/clients/{client_id}":{"delete":{"description":"Deletes the client and its dependent records, including keywords, competitors, jobs, reports, screenshots, AEO data, schedules, and integrations visible to this application.","operationId":"deleteClient","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Client deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete client","tags":["Clients"]},"get":{"description":"Returns full detail for one client including keyword list and competitor domains.","operationId":"getClient","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"competitors":[{"domain":"betsson.com","id":3,"name":"Betsson"}],"domain":"allright.com","id":1,"keywords":[{"geo":"pl","id":10,"lang":"pl","query":"online casino poland"}],"name":"Allright Casino"}}},"description":"Client detail"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Get client","tags":["Clients"]},"patch":{"description":"Updates editable client/site fields: name, primary domain, sitemap URL, analyst notes, screenshot proxy, and archive state. Omitted fields keep their current values.","operationId":"updateClient","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"domain":"allright.com","name":"Allright Casino PL","sitemap_url":"https://allright.com/sitemap.xml"},"schema":{"$ref":"#/components/schemas/ClientUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Updated client"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Update client","tags":["Clients"]},"put":{"description":"Same contract as PATCH; supplied fields update the saved client and omitted fields are preserved.","operationId":"replaceClient","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Updated client"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Replace client fields","tags":["Clients"]}},"/api/v1/clients/{client_id}/ad-observations":{"post":{"description":"Record a competitor ad an operator saw in Provider 1.\n\nBody: {\"paste\": \"<what you copied from Provider 1>\", \"prompt\": \"<the query>\"}.\n\nThis is the recording end of manual observation, not a scraper: the operator\nran the prompt in their own session, saw a sponsored result, and pasted it.\nThe paste is parsed for Provider 1's ad tags; nothing here fetches Provider 1.","operationId":"post_api_v1_api_client_ad_observation","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Record a competitor ad an operator saw in Provider 1.","tags":["Clients"]}},"/api/v1/clients/{client_id}/ad-share-of-voice":{"get":{"description":"Which competitors were observed advertising in Provider 1, and how often.","operationId":"get_api_v1_api_client_ad_share_of_voice","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Which competitors were observed advertising in Provider 1, and how often.","tags":["Clients"]}},"/api/v1/clients/{client_id}/aeo/benchmark":{"get":{"description":"JSON: client's AEO prompts vs the anonymized cross-client Answer Overview trigger index.","operationId":"get_clients_client_aio_benchmark","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: client's AEO prompts vs the anonymized cross-client Answer Overview trigger index.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/check-status":{"get":{"description":"Returns the current status and progress of the most recent AEO check.","operationId":"getAeoCheckStatus","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Check status"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"AEO check status","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/checks":{"get":{"description":"Returns all AEO check runs for the client ordered by newest first.","operationId":"listAeoChecks","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"limit","schema":{"default":20,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"AEO checks list"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List AEO checks","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/checks/{check_id}/live-log":{"get":{"description":"Return result rows newer than after_id for live-log polling on the detail page.","operationId":"get_clients_aeo_check_live_log","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"check_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return result rows newer than after_id for live-log polling on the detail page.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/checks/{check_id}/status":{"get":{"description":"JSON status for a single check (polled by detail page while running/paused).","operationId":"get_clients_aeo_check_status_single","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"check_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON status for a single check (polled by detail page while running/paused).","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/gaps/brief":{"post":{"description":"JSON: generate a one-click content brief for an AEO gap.\n\nA gap = a query where strategy assistants cite competitors but not our domain.\nBody (optional): {\"prompt_text\": \"...\", \"competitors\": [\"a.com\"], \"lang\": \"en\"}.\nWhen prompt_text is omitted, the top opportunity prompt is used.","operationId":"post_clients_aeo_gap_content_brief","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: generate a one-click content brief for an AEO gap.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/history":{"get":{"description":"Returns time-series AEO visibility history for the client across configured providers.","operationId":"getAeoHistory","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"days","schema":{"default":90,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"AEO history"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"AEO visibility history","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/locales":{"get":{"description":"GET: list of client locales for the AEO locale matrix.","operationId":"get_clients_aeo_locales_list","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"GET: list of client locales for the AEO locale matrix.","tags":["AEO"]},"post":{"description":"POST: create a new locale for this client.","operationId":"post_clients_aeo_locale_create","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"POST: create a new locale for this client.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/locales/{locale_id}":{"delete":{"description":"DELETE: remove a locale.","operationId":"delete_clients_aeo_locale_delete","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"locale_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"DELETE: remove a locale.","tags":["AEO"]},"patch":{"description":"PATCH: update name/geo/lang/enabled/sort_order for a locale.","operationId":"patch_clients_aeo_locale_update","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"locale_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"PATCH: update name/geo/lang/enabled/sort_order for a locale.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/maximize":{"post":{"description":"Generates maximum prompt coverage for the client in one call: (1) bulk templates across all languages, (2) multilingual prompt generation, (3) query recommendations by channel. Rate-limited per user. Returns per-strategy counts.","operationId":"aeoMaximize","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"example":{"run_check":false},"schema":{"properties":{"run_check":{"description":"Run an AEO check immediately after generating the prompts.","type":"boolean"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"ai_prompts_added":{"type":"integer"},"recommendations_added":{"type":"integer"},"templates_added":{"type":"integer"},"total_added":{"type":"integer"}},"type":"object"}}},"description":"Coverage generated"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"summary":"Maximize answer-engine query coverage","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/mention-explorer":{"get":{"description":"Returns per-prompt responses with domain mention context and citation data.","operationId":"getAeoMentionExplorer","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"check_id","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Mention explorer data"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"AEO mention explorer","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/prompt-performance":{"get":{"description":"Returns performance metrics per prompt (mention rate, avg position, trend).","operationId":"getAeoPromptPerformance","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Prompt performance data"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"AEO prompt performance","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/prompts":{"get":{"description":"Returns all AEO prompts configured for the client.","operationId":"listAeoPrompts","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"AEO prompts"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List AEO prompts","tags":["AEO"]},"post":{"description":"Adds a new AEO prompt to the client.","operationId":"createAeoPrompt","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"type":"string"},"intent":{"type":"string"},"prompt_text":{"type":"string"}},"required":["prompt_text"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Prompt created"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Create AEO prompt","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/prompts/generate-ai":{"post":{"description":"Generates relevant AEO prompts for the client domain.","operationId":"generateAeoPrompts","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"type":"string"},"count":{"default":10,"type":"integer"}},"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"added":{"type":"integer"},"prompts":{"items":{"type":"string"},"type":"array"}},"type":"object"}}},"description":"Generated prompts"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Generate AEO prompts","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/query-recommendations":{"get":{"description":"JSON: Return saved query recommendations for a client.","operationId":"get_clients_aeo_query_recommendations_list","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: Return saved query recommendations for a client.","tags":["AEO"]},"post":{"description":"Generates recommended queries based on AEO check results.","operationId":"generateAeoQueryRecommendations","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Recommendations"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Generate AEO query recommendations","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/query-recommendations/bulk-apply":{"post":{"description":"Promote many recommendations to active AEO prompts in one call.\n\nBody shape (one or the other, ``rec_ids`` wins if both given):\n\n    {\n      \"rec_ids\": [12, 13, 17, ...]      # explicit list\n    }\n\nOr, to take the top-N by priority + freshness:\n\n    {\n      \"top\": 20,                         # default 10, max 200\n      \"priority\": \"high\"                 # high|medium|low|any (default any)\n    }\n\nSkips recommendations whose ``prompt_text`` already matches an existing\nprompt (case-insensitive). Returns counts.","operationId":"post_clients_aeo_recommendations_bulk_apply","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Promote many recommendations to active AEO prompts in one call.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/query-recommendations/export.csv":{"get":{"description":"Download all saved query recommendations as a Google Sheets-friendly CSV.","operationId":"get_clients_aeo_query_recommendations_export","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Download all saved query recommendations as a Google Sheets-friendly CSV.","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/query-recommendations/{rec_id}/add":{"post":{"description":"Promotes an AEO query recommendation to the tracked keyword list.","operationId":"addAeoRecommendationKeyword","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"path","name":"rec_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Keyword added"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Add AEO recommendation as keyword","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/recommendation-effectiveness":{"get":{"description":"JSON: did AEO recommendations actually move answer-engine visibility? (self-improving loop).","operationId":"get_clients_client_recommendation_effectiveness","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: did AEO recommendations actually move answer-engine visibility? (self-improving loop).","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/response":{"get":{"description":"Returns the full response text for a specific AEO result. Required: ?result_id=.","operationId":"aeoResponseText","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"result_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"AEO full response text"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Get AEO full response text","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/results":{"get":{"description":"Returns all AEO prompt results from the most recent check, including domain mentions, positions, and competitor scores.","operationId":"getAeoResults2","parameters":[{"$ref":"#/components/parameters/ClientId"},{"description":"Specific check ID; omit for latest","in":"query","name":"check_id","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"AEO results array"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Latest AEO check results","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/run-check":{"post":{"description":"Triggers a new AEO check against all active prompts for the client.","operationId":"runAeoCheck","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"example":{"aeo_mode":"economy","locale_ids":[]},"schema":{"properties":{"aeo_mode":{"description":"Check depth. Omit to use the client's configured mode.","enum":["economy","full"],"type":"string"},"locale_ids":{"description":"Restrict the run to these client locale IDs. Empty means all locales.","items":{"type":"integer"},"type":"array"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Check started"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Start AEO check (API v1)","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/screenshots/export-pdf":{"post":{"description":"Exports AEO screenshots into a downloadable PDF.","operationId":"exportAeoScreenshotsPdf","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"check_id":{"description":"AEO check to export. Omit for the latest check.","type":"integer"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/pdf":{"schema":{"format":"binary","type":"string"}}},"description":"PDF file"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Export AEO screenshots as PDF","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/screenshots/generate":{"post":{"description":"Generates screenshots of answer-engine responses for the client.","operationId":"generateAeoScreenshots","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"check_id":{"description":"AEO check to screenshot. Omit for the latest check.","type":"integer"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Screenshots generated"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Generate AEO screenshots","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/screenshots/list":{"get":{"description":"Returns available screenshot captures from AEO checks.","operationId":"listAeoScreenshots","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"check_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Screenshots list"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List AEO screenshots","tags":["AEO"]}},"/api/v1/clients/{client_id}/aeo/surfaces":{"get":{"description":"JSON: the pluggable AEO-surface registry + which live surfaces are enabled.","operationId":"get_clients_client_ai_surfaces","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: the pluggable AEO-surface registry + which live surfaces are enabled.","tags":["AEO"]}},"/api/v1/clients/{client_id}/agent/cycle":{"get":{"description":"JSON: one monitor->diagnose->recommend agent cycle for a client (read-only).","operationId":"get_clients_client_agent_cycle","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: one monitor->diagnose->recommend agent cycle for a client (read-only).","tags":["Clients"]}},"/api/v1/clients/{client_id}/aso/actions/full-refresh":{"post":{"description":"Refreshes ASO listing snapshots, public reviews, and live rankings in one workflow. This is the REST/MCP equivalent of the Strategy Assistant `run_aso_full_refresh` action.","operationId":"runAsoFullRefresh","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"limit":25,"max_apps":5,"max_keywords":25,"store":"app_store"},"schema":{"$ref":"#/components/schemas/AsoFullRefreshAction"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoActionResult"}}},"description":"ASO full refresh completed"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"One or more ASO providers failed"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Run ASO full refresh","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/actions/keywords":{"post":{"description":"Starts ASO keyword generation for the client and saves new ideas into the ASO keyword workspace. This is the REST/MCP equivalent of the Strategy Assistant `run_aso_keywords` action.","operationId":"runAsoKeywordGeneration","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"count":50,"country":"GB","language":"en","store":"app_store"},"schema":{"$ref":"#/components/schemas/AsoKeywordAction"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoActionResult"}}},"description":"ASO keyword generation task started"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Generate ASO keywords","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/actions/listing-audit":{"post":{"description":"Fetches public App Store or Google Play listing metadata for saved app profiles and stores ASO listing audit snapshots.","operationId":"runAsoListingAudit","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"max_apps":5,"store":"app_store"},"schema":{"$ref":"#/components/schemas/AsoListingAuditAction"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoActionResult"}}},"description":"ASO listing audit refresh completed"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Store listing provider failed"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Refresh ASO listing audit","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/actions/rankings":{"post":{"description":"Runs a bounded live App Store or Google Play ranking check for saved ASO keywords and persists ranking rows plus diagnostics.","operationId":"runAsoRankings","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"max_keywords":25},"schema":{"$ref":"#/components/schemas/AsoRankingAction"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoActionResult"}}},"description":"ASO ranking check completed"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Store ranking provider failed"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Run ASO live rankings","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/actions/reviews":{"post":{"description":"Fetches latest public store reviews for saved ASO app profiles and stores review intelligence rows.","operationId":"runAsoReviewsFetch","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"limit":25,"max_apps":3,"store":"app_store"},"schema":{"$ref":"#/components/schemas/AsoReviewsAction"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoActionResult"}}},"description":"ASO review fetch completed"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Store review provider failed"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Fetch ASO reviews","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/apps":{"get":{"description":"Lists tracked App Store and Google Play app profiles for the client.","operationId":"listAsoApps","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AsoApp"},"type":"array"}}},"description":"ASO app profiles"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List ASO apps","tags":["ASO"]},"post":{"description":"Creates a tracked ASO app profile. Use `store=app_store` or `store=google_play`.","operationId":"createAsoApp","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"app_identifier":"com.example.app","app_name":"Allright English","country":"US","language":"en","store":"google_play"},"schema":{"$ref":"#/components/schemas/AsoAppCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoApp"}}},"description":"ASO app created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Create ASO app","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/apps/{app_id}":{"delete":{"description":"Delete a tracked ASO app profile.","operationId":"deleteAsoApp","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"app_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete ASO app","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/competitors":{"get":{"description":"Lists competitor app profiles used for ASO share-of-voice, listing comparison, and keyword diagnostics.","operationId":"listAsoCompetitors","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AsoApp"},"type":"array"}}},"description":"ASO competitor profiles"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List ASO competitors","tags":["ASO"]},"post":{"description":"Creates a competitor app profile for ASO share-of-voice and listing comparison.","operationId":"createAsoCompetitor","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoAppCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoApp"}}},"description":"ASO competitor created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Create ASO competitor","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/competitors/{competitor_id}":{"delete":{"description":"Deletes one ASO competitor profile for the client.","operationId":"deleteAsoCompetitor","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"competitor_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete ASO competitor","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/keywords":{"get":{"description":"Lists tracked ASO keywords with store, country, language, and latest ranking metadata.","operationId":"listAsoKeywords","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AsoKeyword"},"type":"array"}}},"description":"ASO keywords"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List ASO keywords","tags":["ASO"]},"post":{"description":"Adds one or more App Store or Google Play keywords. Existing keyword/market rows are updated instead of duplicated.","operationId":"addAsoKeywords","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":[{"country":"US","language":"en","query":"learn english app","store":"google_play"}],"schema":{"items":{"$ref":"#/components/schemas/AsoKeywordCreate"},"type":"array"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Added"}}},"description":"ASO keywords added"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Add ASO keywords","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/keywords/{keyword_id}":{"delete":{"description":"Deletes one tracked ASO keyword row for the client.","operationId":"deleteAsoKeyword","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"keyword_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete ASO keyword","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/listing-audit":{"get":{"description":"Returns listing quality checks, missing metadata, and optimization recommendations for tracked ASO app profiles.","operationId":"getAsoListingAudit","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"ASO listing audit"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Get ASO listing audit","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/rankings":{"get":{"description":"Lists persisted ASO ranking rows. Optional query: `limit` from 1 to 500.","operationId":"listAsoRankings","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"additionalProperties":true,"type":"object"},"type":"array"}}},"description":"ASO ranking rows"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List ASO rankings","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/rankings/diagnostics":{"get":{"description":"Returns diagnostic runs, alert summary, filter options, and paid fallback health. Optional filters: `store`, `country`, `language`, `status`, `source`, `limit`.","operationId":"listAsoRankingDiagnostics","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"ASO ranking diagnostics"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List ASO ranking diagnostics","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/reviews":{"get":{"description":"Returns ASO review intelligence, sentiment signals, and prioritized review-response insights.","operationId":"getAsoReviews","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"ASO review intelligence summary"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Get ASO reviews summary","tags":["ASO"]}},"/api/v1/clients/{client_id}/aso/summary":{"get":{"description":"Returns app counts, keyword counts, visibility, listing audit, reviews, diagnostics, fallback health, and prioritized ASO backlog.","operationId":"getAsoSummary","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsoSummary"}}},"description":"ASO summary"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Get ASO summary","tags":["ASO"]}},"/api/v1/clients/{client_id}/check":{"post":{"description":"Enqueues a SERP rank-check job for all keywords belonging to the specified client. Poll `GET /api/v1/jobs/{job_id}` to track progress.","operationId":"startSerpCheck","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{},"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"example":{"job_id":56,"keyword_count":142,"status":"queued"}}},"description":"Job enqueued"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Start SERP check","tags":["Jobs"]}},"/api/v1/clients/{client_id}/compare-dates":{"get":{"description":"Returns keyword position deltas between two Unix timestamps.","operationId":"compareDates","parameters":[{"$ref":"#/components/parameters/ClientId"},{"description":"Unix timestamp for date A","in":"query","name":"date_a","required":true,"schema":{"type":"number"}},{"description":"Unix timestamp for date B","in":"query","name":"date_b","required":true,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Comparison data"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Compare positions between two dates","tags":["Analytics"]}},"/api/v1/clients/{client_id}/competitors":{"get":{"description":"Returns competitor domains tracked for the specified client. Supports optional `limit`/`offset` pagination with `X-Total-Count`/`X-Next-Offset` headers.","operationId":"listCompetitors","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"description":"Maximum rows to return for this page (1-1000).","in":"query","name":"limit","required":false,"schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"description":"Rows to skip before the page starts.","in":"query","name":"offset","required":false,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"additionalProperties":true,"type":"object"},"type":"array"}}},"description":"Array of competitors"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List competitors","tags":["Competitors"]},"post":{"description":"Adds a competitor domain to the specified client and backfills known positions from stored SERP snapshots when possible.","operationId":"addCompetitor","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"domain":"betsson.com","name":"Betsson"},"schema":{"$ref":"#/components/schemas/CompetitorCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Competitor added"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Add competitor","tags":["Competitors"]}},"/api/v1/clients/{client_id}/competitors/visibility":{"get":{"description":"Returns time-series visibility history for all tracked competitors.","operationId":"getCompetitorVisibility","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"limit","schema":{"default":10,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitorVisibility"}}},"description":"Competitor visibility data"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Competitor visibility history","tags":["Competitors"]}},"/api/v1/clients/{client_id}/competitors/{competitor_id}":{"delete":{"description":"Deletes one competitor and its stored competitor-position rows.","operationId":"deleteCompetitor","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"example":3,"in":"path","name":"competitor_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Competitor deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete competitor","tags":["Competitors"]},"patch":{"description":"Updates one competitor domain or display name.","operationId":"updateCompetitor","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"example":3,"in":"path","name":"competitor_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitorUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Updated competitor"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Update competitor","tags":["Competitors"]},"put":{"description":"Same contract as PATCH; supplied fields update the saved competitor and omitted fields are preserved.","operationId":"replaceCompetitor","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"example":3,"in":"path","name":"competitor_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitorUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Updated competitor"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Replace competitor fields","tags":["Competitors"]}},"/api/v1/clients/{client_id}/content-impact":{"get":{"description":"Return URL-level content impact metrics for reports and external dashboards.","operationId":"get_api_v1_api_content_impact","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return URL-level content impact metrics for reports and external dashboards.","tags":["Clients"]}},"/api/v1/clients/{client_id}/content/draft":{"post":{"description":"Content studio: generate a full SEO/AEO article draft for a target query.","operationId":"post_clients_client_content_draft","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Content studio: generate a full SEO/AEO article draft for a target query.","tags":["Clients"]}},"/api/v1/clients/{client_id}/crawler-analytics":{"get":{"description":"Which AEO crawlers came, what they took, and what they were refused.","operationId":"get_api_v1_api_client_crawler_analytics","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Which AEO crawlers came, what they took, and what they were refused.","tags":["Clients"]}},"/api/v1/clients/{client_id}/crawler-hits":{"post":{"description":"Ingest access-log lines and keep the AEO-crawler ones.\n\nBody: {\"log\": \"<raw lines>\"} or {\"lines\": [\"...\", \"...\"]}.\n\nIdempotent \u2014 re-posting an overlapping window reports duplicates rather\nthan inflating the counts, so a cron job can safely resend yesterday's tail.","operationId":"post_api_v1_api_client_crawler_ingest","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Ingest access-log lines and keep the AEO-crawler ones.","tags":["Clients"]}},"/api/v1/clients/{client_id}/export/bi.csv":{"get":{"description":"Flat long-format export: one row per (date, metric, entity, value).\n\nThis is the shape Looker Studio, Sheets and BigQuery consume without any\nreshaping, and it stays stable as competitors and answer engines come and\ngo \u2014 unlike bi.json, whose keys change with the data.","operationId":"get_clients_client_bi_export_csv","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Flat long-format export: one row per (date, metric, entity, value).","tags":["Clients"]}},"/api/v1/clients/{client_id}/export/bi.json":{"get":{"description":"BI / data-warehouse export: all of a client's visibility time-series in one\nJSON payload, ready to load into Google Sheets / Looker / BigQuery.","operationId":"get_clients_client_bi_export","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"BI / data-warehouse export: all of a client's visibility time-series in one","tags":["Clients"]}},"/api/v1/clients/{client_id}/gsc/data":{"get":{"description":"Returns GSC performance data (impressions, clicks, CTR, average position) for the client. Requires GSC integration to be connected.","operationId":"getGscData2","parameters":[{"$ref":"#/components/parameters/ClientId"},{"description":"Lookback window in days","in":"query","name":"days","schema":{"default":90,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"GSC data object"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Google Search Console data","tags":["Analytics"]}},"/api/v1/clients/{client_id}/gsc/suggestions":{"get":{"description":"Returns keyword suggestions derived from GSC query data.","operationId":"getGscSuggestions","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"GSC keyword suggestions"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"GSC keyword suggestions","tags":["Analytics"]}},"/api/v1/clients/{client_id}/history":{"get":{"description":"Returns time-series visibility history for a client (one data point per completed check).","operationId":"getVisibilityHistory","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"limit","schema":{"default":90,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Array of {checked_at, visibility, avg_position, top3, top10, top30, total}"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Visibility history","tags":["Analytics"]}},"/api/v1/clients/{client_id}/indexing/status":{"get":{"description":"Returns indexing summary, recent tasks, and monitor history.","operationId":"indexingStatus","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexingStatus"}}},"description":"Indexing status"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get indexing status","tags":["Indexing"]}},"/api/v1/clients/{client_id}/indexing/trigger":{"post":{"description":"Starts URL indexing acceleration for the client in the background.","operationId":"triggerIndexing","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"client_id":{"type":"integer"},"status":{"type":"string"}},"type":"object"}}},"description":"Indexing started"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Trigger indexing acceleration","tags":["Indexing"]}},"/api/v1/clients/{client_id}/keyword-history":{"get":{"description":"Returns position history for a specific keyword query across all checks.","operationId":"getKeywordHistory","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"query","required":true,"schema":{"type":"string"}},{"in":"query","name":"lang","schema":{"type":"string"}},{"in":"query","name":"device","schema":{"default":"desktop","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Keyword position history"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Keyword position history","tags":["Keywords"]}},"/api/v1/clients/{client_id}/keywords":{"get":{"description":"Returns all keywords tracked for the specified client. Supports optional `limit`/`offset` pagination with `X-Total-Count`/`X-Next-Offset` headers.","operationId":"listKeywords","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"description":"Maximum rows to return for this page (1-1000).","in":"query","name":"limit","required":false,"schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"description":"Rows to skip before the page starts.","in":"query","name":"offset","required":false,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":[{"cluster":"main","current_position":4,"device":"desktop","geo":"pl","id":10,"intent":"commercial","lang":"pl","query":"online casino poland"}]}},"description":"Array of keyword objects"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List keywords","tags":["Keywords"]},"post":{"description":"Adds one or more keywords to the specified client. Duplicates within the same client/lang/geo/device triplet are silently ignored.","operationId":"addKeywords","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":[{"device":"desktop","geo":"pl","intent":"commercial","lang":"pl","query":"online casino poland"},{"device":"mobile","geo":"pl","lang":"pl","query":"best casino bonuses"}],"schema":{"items":{"properties":{"cluster":{"type":"string"},"device":{"default":"desktop","enum":["desktop","mobile","both"],"type":"string"},"geo":{"default":"us","type":"string"},"intent":{"type":"string"},"lang":{"default":"en","type":"string"},"query":{"type":"string"},"tags":{"type":"string"}},"required":["query"],"type":"object"},"minItems":1,"type":"array"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"added":2}}},"description":"Keywords added"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Add keywords","tags":["Keywords"]}},"/api/v1/clients/{client_id}/keywords/table":{"get":{"description":"Server-side paginated keywords with latest positions.","operationId":"keywordsTable","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"page","schema":{"default":1,"type":"integer"}},{"in":"query","name":"per_page","schema":{"default":50,"maximum":200,"type":"integer"}},{"in":"query","name":"sort","schema":{"default":"query","enum":["query","position","lang","geo","device","cluster","tags","intent","source"],"type":"string"}},{"in":"query","name":"dir","schema":{"default":"asc","enum":["asc","desc"],"type":"string"}},{"in":"query","name":"search","schema":{"type":"string"}},{"in":"query","name":"tag","schema":{"type":"string"}},{"in":"query","name":"device","schema":{"type":"string"}},{"in":"query","name":"cluster","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordPage"}}},"description":"Paginated keyword list"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Paginated keyword table","tags":["Keywords"]}},"/api/v1/clients/{client_id}/keywords/{keyword_id}":{"delete":{"description":"Deletes one keyword from the specified client.","operationId":"deleteKeyword","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"example":10,"in":"path","name":"keyword_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Keyword deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete keyword","tags":["Keywords"]},"patch":{"description":"Updates one tracked keyword's query, locale, device, grouping, intent, tags, and optional SEO/Answer Overview metadata.","operationId":"updateKeyword","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"example":10,"in":"path","name":"keyword_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"device":"mobile","geo":"us","lang":"en","query":"best casino bonuses","tags":"priority,bonus"},"schema":{"$ref":"#/components/schemas/KeywordUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Updated keyword"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Update keyword","tags":["Keywords"]},"put":{"description":"Same contract as PATCH; supplied fields update the saved keyword and omitted fields are preserved.","operationId":"replaceKeyword","parameters":[{"example":1,"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}},{"example":10,"in":"path","name":"keyword_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Updated keyword"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Replace keyword fields","tags":["Keywords"]}},"/api/v1/clients/{client_id}/movers":{"get":{"description":"Returns keywords with the biggest position changes (gainers and losers) between the two most recent checks.","operationId":"getTopMovers","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"limit","schema":{"default":20,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Movers list"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Top position movers","tags":["Analytics"]}},"/api/v1/clients/{client_id}/opportunities":{"get":{"description":"Returns keywords in positions 4\u201330 that have improvement potential (low-hanging fruit).","operationId":"getOpportunities","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"limit","schema":{"default":20,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Opportunity keywords"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Ranking opportunities","tags":["Analytics"]}},"/api/v1/clients/{client_id}/playbooks/apply":{"post":{"description":"Apply a vertical playbook: seed a curated AEO prompt bundle for an industry.","operationId":"post_clients_client_apply_playbook","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Apply a vertical playbook: seed a curated AEO prompt bundle for an industry.","tags":["Clients"]}},"/api/v1/clients/{client_id}/reddit-opportunities":{"get":{"description":"Returns Reddit posts/threads ranked in Google where the client could participate.","operationId":"getRedditOpportunities","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Reddit opportunities"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Reddit opportunities","tags":["Analytics"]}},"/api/v1/clients/{client_id}/reddit/scan-status":{"get":{"description":"Return current Reddit scan progress for the client.","operationId":"get_clients_reddit_scan_status_api","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return current Reddit scan progress for the client.","tags":["Clients"]}},"/api/v1/clients/{client_id}/serp-features":{"get":{"description":"Returns a summary of SERP features (featured snippets, PAA, etc.) for the client's keywords.","operationId":"getSerpFeatures","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"SERP feature counts"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"SERP feature summary","tags":["Analytics"]}},"/api/v1/clients/{client_id}/serp-screenshot/install-chromium":{"post":{"description":"One-shot diagnostic: invoke `python -m playwright install chromium` and\nreturn its stdout/stderr so we can see why the deploy step silently fails.\nRestricted to admin users (this runs an OS subprocess on the server).","operationId":"post_api_v1_api_serp_screenshot_install_chromium","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"One-shot diagnostic: invoke `python -m playwright install chromium` and","tags":["SERP Surface"]}},"/api/v1/clients/{client_id}/serp-screenshot/test":{"post":{"description":"Synchronously capture a single SERP screenshot for diagnostics.\n\nBody JSON: {\"query\": \"...\", \"lang\": \"...\", \"geo\": \"...\", \"device\": \"desktop\"}\nReturns success info or full traceback so we can see why prod capture is failing.","operationId":"post_api_v1_api_serp_screenshot_test","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Synchronously capture a single SERP screenshot for diagnostics.","tags":["SERP Surface"]}},"/api/v1/clients/{client_id}/serp-screenshots":{"get":{"description":"List SERP screenshot rows currently registered for the client (debug).","operationId":"get_api_v1_api_serp_screenshots_list","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List SERP screenshot rows currently registered for the client (debug).","tags":["SERP Surface"]}},"/api/v1/clients/{client_id}/serp-screenshots/purge-stale":{"post":{"description":"Delete screenshot rows (and files) that are stale or small (CAPTCHA pages).\n\nPOST body JSON (all optional):\n  {\"before_ts\": <unix_float>,   # delete if captured_at < this (default: 30 days ago)\n   \"max_file_size\": <int>,      # also delete if file_size <= this (default: 50000 bytes)\n   \"all\": true}                 # delete ALL screenshots for client regardless of age/size\nRestricted to admin/superadmin users. Returns count of deleted rows.","operationId":"post_api_v1_api_serp_screenshots_purge_stale","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Delete screenshot rows (and files) that are stale or small (CAPTCHA pages).","tags":["SERP Surface"]}},"/api/v1/clients/{client_id}/serp-snapshots":{"get":{"description":"Returns raw SERP result snapshots for a client's keyword queries.","operationId":"getSerpSnapshots","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"query","required":true,"schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":20,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"SERP snapshots"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"SERP snapshots","tags":["Jobs"]}},"/api/v1/clients/{client_id}/value-attribution":{"get":{"description":"JSON: estimated organic traffic value now vs the previous check (outcome basis).","operationId":"get_clients_client_value_attribution","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"JSON: estimated organic traffic value now vs the previous check (outcome basis).","tags":["Clients"]}},"/api/v1/clients/{client_id}/visibility-index":{"get":{"description":"One comparable AEO score for the client, with its place in the base.\n\n``benchmark`` is null until enough clients have data to compare against,\nand only ever carries aggregates \u2014 never another client's identity.","operationId":"get_api_v1_api_client_visibility_index","parameters":[{"in":"path","name":"client_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"One comparable AEO score for the client, with its place in the base.","tags":["Clients"]}},"/api/v1/jobs":{"get":{"description":"Returns SERP check jobs visible to the authenticated user. Filter by `client_id` to scope the result. Supports optional `limit`/`offset` pagination with `X-Total-Count`/`X-Next-Offset` headers.","operationId":"listJobs","parameters":[{"description":"Filter jobs by client","in":"query","name":"client_id","required":false,"schema":{"type":"integer"}},{"description":"Maximum rows to return for this page (1-1000).","in":"query","name":"limit","required":false,"schema":{"maximum":1000,"minimum":1,"type":"integer"}},{"description":"Rows to skip before the page starts.","in":"query","name":"offset","required":false,"schema":{"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":[{"checked_count":142,"client_id":1,"created_at":"2026-04-30T10:00:00Z","finished_at":"2026-04-30T10:04:12Z","id":55,"keyword_count":142,"status":"done"}]}},"description":"Array of job objects"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List SERP check jobs","tags":["Jobs"]}},"/api/v1/jobs/{job_id}":{"delete":{"description":"Deletes a completed or failed job and its results. Returns 409 for running jobs.","operationId":"deleteJob","parameters":[{"$ref":"#/components/parameters/JobId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Cannot delete a running job"}},"summary":"Delete a job","tags":["Jobs"]},"get":{"description":"Returns current status, progress, and (when done) full results for a job.","operationId":"getJob","parameters":[{"$ref":"#/components/parameters/JobId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatus"}}},"description":"Job status and results"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get job status","tags":["Jobs"]}},"/api/v1/jobs/{job_id}/cancel":{"post":{"description":"Signal a running job to stop gracefully, or mark it cancelled if already stuck.","operationId":"post_api_v1_api_job_cancel","parameters":[{"in":"path","name":"job_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Signal a running job to stop gracefully, or mark it cancelled if already stuck.","tags":["Jobs"]}},"/api/v1/jobs/{job_id}/results":{"get":{"description":"Returns the full results JSON for a completed job. Returns 204 (no content) if the job is not yet done or has no results. Returns 404 if the job does not exist or is not accessible.","operationId":"getJobResults","parameters":[{"$ref":"#/components/parameters/JobId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResults"}}},"description":"Full results"},"204":{"description":"Job exists but has no results yet (still running, cancelled, or errored)"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get job results","tags":["Jobs"]}},"/api/v1/jobs/{job_id}/view-stats":{"get":{"description":"Returns view count and last-viewed timestamp for a shared job report.","operationId":"getJobViewStats","parameters":[{"$ref":"#/components/parameters/JobId"}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"last_viewed":{"type":"number"},"views":{"type":"integer"}},"type":"object"}}},"description":"View stats"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Report view statistics","tags":["Jobs"]}},"/api/v1/learning/benchmarks/{niche}":{"get":{"description":"Returns all learned benchmarks for a specific niche. Admin/superadmin only.","operationId":"learningBenchmarks","parameters":[{"in":"path","name":"niche","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Benchmarks"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Niche benchmarks","tags":["Learning"]}},"/api/v1/learning/client/{client_id}/niche-report":{"get":{"description":"Returns full niche benchmark comparison for a specific client. Admin/superadmin only.","operationId":"learningNicheReport","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Niche report"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Client niche benchmark report","tags":["Learning"]}},"/api/v1/learning/context/{client_id}":{"get":{"description":"Returns the full learning context injected for a specific client. Admin/superadmin only.","operationId":"learningContext","parameters":[{"$ref":"#/components/parameters/ClientId"},{"in":"query","name":"lang","schema":{"default":"en","type":"string"}},{"in":"query","name":"geo","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Learning context"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Client learning context (debug)","tags":["Learning"]}},"/api/v1/learning/patterns":{"get":{"description":"Returns top learned patterns. Admin/superadmin only.","operationId":"learningPatterns","parameters":[{"in":"query","name":"min_confidence","schema":{"default":0.3,"type":"number"}},{"in":"query","name":"type","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":50,"maximum":100,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Patterns list"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Cross-client learned patterns","tags":["Learning"]}},"/api/v1/learning/repass":{"post":{"description":"Triggers an immediate learning re-pass in the background. Admin/superadmin only.","operationId":"learningRepass","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"message":{"type":"string"},"status":{"type":"string"}},"type":"object"}}},"description":"Re-pass started"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Trigger learning re-pass","tags":["Learning"]}},"/api/v1/learning/repass/status":{"get":{"description":"Return live progress of the learning re-pass (admin/superadmin only).","operationId":"get_api_v1_api_learning_repass_status","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Return live progress of the learning re-pass (admin/superadmin only).","tags":["Learning"]}},"/api/v1/learning/stats":{"get":{"description":"Returns cross-client learning system statistics. Admin/superadmin only.","operationId":"learningStats","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Learning stats"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Learning system statistics","tags":["Learning"]}},"/api/v1/query-fanout":{"post":{"description":"Expand one prompt into the sub-questions an answer engine also fields.\n\nDeterministic and free \u2014 no provider call \u2014 so it can be run across a whole\nprompt list to find tracking gaps.","operationId":"post_api_v1_api_query_fanout","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Expand one prompt into the sub-questions an answer engine also fields.","tags":["Utility"]}},"/api/v1/self-hosting/capabilities":{"get":{"description":"What this installation can deliver, and what it cannot without keys.\n\nWritten for operators running self-hosted: the alternative to this is\ndiscovering the answer by watching charts fail to fill, which reads as a\nbroken product rather than an unconfigured one. Reports whether a gap is\nunconfigured, degraded, or impossible \u2014 the last of which no amount of\nself-hosting will change.","operationId":"get_api_v1_api_self_hosting_capabilities","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"What this installation can deliver, and what it cannot without keys.","tags":["Utility"]}},"/api/v1/serp/modes":{"get":{"description":"Alias for /api/v1/serper/modes.","operationId":"serpModes","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerperModes"}}},"description":"Available modes"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List SERP modes (alias)","tags":["SERP Surface"]}},"/api/v1/serp/{mode}":{"get":{"description":"Alias for /api/v1/serper/{mode}.","operationId":"serpModeGet","parameters":[{"in":"path","name":"mode","required":true,"schema":{"type":"string"}},{"in":"query","name":"q","required":true,"schema":{"type":"string"}},{"in":"query","name":"source","schema":{"default":"auto","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerperResponse"}}},"description":"Results"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Run search via /serp alias (GET)","tags":["SERP Surface"]},"post":{"operationId":"serpModePost","parameters":[{"in":"path","name":"mode","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerperRequest"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerperResponse"}}},"description":"Results"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Run search via /serp alias (POST)","tags":["SERP Surface"]}},"/api/v1/serper/modes":{"get":{"description":"Returns available Serper-like search modes with supported sources (auto, local, serper).","operationId":"serperModes","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerperModes"}}},"description":"Available modes"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"List Serper-compatible modes","tags":["SERP Surface"]}},"/api/v1/serper/{mode}":{"get":{"description":"Executes a Google SERP lookup using the Serper-compatible interface. Supports `search`, `images`, `news`, `videos`, `places`, `maps`, `shopping`, `patents`, `scholar`, `autocomplete`, `reviews`, `lens`, and `webpages` modes.","operationId":"serpSearch","parameters":[{"example":"online casino poland","in":"query","name":"q","required":true,"schema":{"type":"string"}},{"example":"search","in":"query","name":"mode","required":false,"schema":{"type":"string"}},{"example":"pl","in":"query","name":"lang","required":false,"schema":{"type":"string"}},{"example":"pl","in":"query","name":"geo","required":false,"schema":{"type":"string"}},{"example":10,"in":"query","name":"num","required":false,"schema":{"type":"integer"}},{"in":"query","name":"device","required":false,"schema":{"enum":["desktop","mobile"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"organic":[{"link":"https://example.com","position":1,"snippet":"\u2026","title":"Best Online Casino Poland 2026"}],"searchParameters":{"gl":"pl","hl":"pl","num":10,"q":"online casino poland"}}}},"description":"SERP results"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"SERP search (Serper-compatible)","tags":["SERP Surface"]},"post":{"description":"Same as GET but parameters can be passed as JSON body. Body fields are merged with query string.","operationId":"serperModePost","parameters":[{"in":"path","name":"mode","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"geo":"US","lang":"en","num":10,"q":"best seo tools 2026"},"schema":{"$ref":"#/components/schemas/SerperRequest"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerperResponse"}}},"description":"Serper-format results"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}},"summary":"Run Serper-compatible search (POST)","tags":["SERP Surface"]}},"/api/v1/settings/config-patch":{"post":{"description":"PATCH arbitrary top-level config.json keys. Superadmin only.\n\nBody: nested dict of config paths to update, e.g.:\n  {\"ai\": {\"Provider 2\": {\"api_key\": \"...\", \"model\": \"...\"},\n          \"Provider 3\": {\"model\": \"...\"}},\n   \"aeo\": {\"Provider 2\": {\"api_key\": \"...\", \"enabled\": true},\n           \"Provider 3\": {\"model\": \"...\"}}}\n\nOnly keys present in the body are updated; all other keys are preserved.","operationId":"post_api_v1_api_settings_config_patch","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"PATCH arbitrary top-level config.json keys. Superadmin only.","tags":["Utility"]}},"/health":{"get":{"description":"Returns database, config, scheduler, and provider status. No authentication required.","operationId":"healthCheck","responses":{"200":{"content":{"application/json":{"example":{"config":"ok","database":"ok","provider":"serper","scheduler":"running","timestamp":"2026-04-14T10:00:00+00:00"},"schema":{"$ref":"#/components/schemas/HealthResponse"}}},"description":"All systems operational"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}},"description":"One or more systems unavailable"}},"security":[],"summary":"Health check","tags":["Health"]}},"/mcp":{"get":{"description":"This deployment is stateless and does not keep standalone SSE streams open. Clients should use POST /mcp for Streamable HTTP JSON responses.","operationId":"mcpGet","responses":{"405":{"description":"Standalone SSE stream is not enabled; use POST /mcp."}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"MCP SSE stream probe","tags":["MCP"]},"post":{"description":"Receives MCP JSON-RPC 2.0 requests for integration tools. Supported methods: `initialize`, `tools/list`, `tools/call`, `resources/list`, `resources/read`, `prompts/list`, `prompts/get`.\n\n**Authentication:** same `sk-\u2026` API key as REST \u2014 via `Authorization: Bearer sk-\u2026` or `X-API-Key: sk-\u2026` header.\n\n**Protocol version:** `2025-11-25` (also accepts `2025-06-18`, `2025-03-26`).\n\n**Available tools:** `api_request`, `list_clients`, `get_client`, `create_client`, `list_keywords`, `add_keywords`, `list_competitors`, `add_competitor`, `list_jobs`, `get_job`, `start_serp_check`, `serp_search`, `list_analytics_reports`, `get_analytics_report`, `list_api_keys`, `create_api_key`, `delete_api_key`, `read_openapi_spec`.\n\nSee `/mcp-guide` for full connection instructions.","operationId":"mcpPost","parameters":[],"requestBody":{"content":{"application/json":{"examples":{"initialize":{"summary":"Initialize MCP session","value":{"id":1,"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"},"protocolVersion":"2025-11-25"}}},"list_clients":{"summary":"Call list_clients tool","value":{"id":3,"jsonrpc":"2.0","method":"tools/call","params":{"arguments":{},"name":"list_clients"}}},"serp_search":{"summary":"Run a SERP search","value":{"id":5,"jsonrpc":"2.0","method":"tools/call","params":{"arguments":{"geo":"pl","lang":"pl","num":10,"q":"online casino poland"},"name":"serp_search"}}},"start_serp_check":{"summary":"Start SERP check via MCP","value":{"id":4,"jsonrpc":"2.0","method":"tools/call","params":{"arguments":{"client_id":1},"name":"start_serp_check"}}},"tools_list":{"summary":"List available MCP tools","value":{"id":2,"jsonrpc":"2.0","method":"tools/list","params":{}}}},"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"id":2,"jsonrpc":"2.0","result":{"tools":[{"description":"List clients visible to the authenticated API key owner.","name":"list_clients"},{"description":"Start a SERP rank check for a client.","name":"start_serp_check"}]}},"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}},"description":"JSON-RPC response"},"202":{"description":"Notification accepted (no id in request)"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"MCP Streamable HTTP endpoint","tags":["MCP"]}},"/profile/api-keys":{"get":{"description":"Browser-facing profile endpoint used by the API key management panel. Requires a logged-in session.","operationId":"profileListApiKeys","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ApiKeyMeta"},"type":"array"}}},"description":"API key metadata for the current user"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"sessionAuth":[]}],"summary":"List API keys in profile UI","tags":["APIKeys"]},"post":{"description":"Browser-facing API key creation endpoint. It is CSRF-protected and returns the raw `sk-...` value once so the user can copy it.","operationId":"profileCreateApiKey","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}},"required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}},"description":"API key created; raw key is shown once"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"sessionAuth":[]}],"summary":"Create API key in profile UI","tags":["APIKeys"]}},"/profile/api-keys/{key_id}":{"delete":{"description":"Browser-facing CSRF-protected endpoint for deleting a named API key.","operationId":"profileDeleteApiKey","parameters":[{"description":"API key ID","in":"path","name":"key_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deleted"}}},"description":"API key deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"sessionAuth":[]}],"summary":"Delete API key in profile UI","tags":["APIKeys"]}},"/profile/api-keys/{key_id}/logs":{"get":{"description":"Paginated usage logs for one API key.","operationId":"get_profile_api_key_logs","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Paginated usage logs for one API key.","tags":["APIKeys"]}},"/profile/api-keys/{key_id}/stats":{"get":{"description":"Usage statistics summary for one API key.","operationId":"get_profile_api_key_stats","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Successful response"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Usage statistics summary for one API key.","tags":["APIKeys"]}}},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"servers":[{"description":"Current server","url":"https://serp.tsoden.ai"}],"tags":[{"description":"Service health and status","name":"Health"},{"description":"Login, logout, registration","name":"Auth"},{"description":"Client (site) management","name":"Clients"},{"description":"Keyword management, history, and bulk operations","name":"Keywords"},{"description":"Competitor tracking and discovery","name":"Competitors"},{"description":"SERP check jobs \u2014 start, monitor, results","name":"Jobs"},{"description":"Answer Engine Optimization: prompts, checks, visibility, screenshots","name":"AEO"},{"description":"Visibility history, GSC/GA data, movers, opportunities","name":"Analytics"},{"description":"Serper-compatible search proxy (search, news, images, etc.)","name":"SERP Surface"},{"description":"Cross-client learning system (admin only)","name":"Learning"},{"description":"URL indexing acceleration","name":"Indexing"},{"description":"Internal messaging and strategy assistant","name":"Messaging"},{"description":"Admin panel operations","name":"Admin"},{"description":"User profile and 2FA","name":"Profile"},{"description":"GA/GSC analytics report upload and analysis","name":"AnalyticsReports"},{"description":"Named API key management \u2014 create, list, delete keys for third-party integrations","name":"APIKeys"},{"description":"Model Context Protocol endpoint for integration tools","name":"MCP"},{"description":"Strategy assistant sessions and action execution","name":"Strategy Assistant"},{"description":"App Store Optimization apps, keywords, rankings, reviews, and diagnostics","name":"ASO"},{"description":"Internal JSON utility endpoints","name":"Utility"}]}
