{"openapi":"3.1.0","info":{"title":"SpiderIQ API Gateway","description":"Distributed Web Scraping Platform with RabbitMQ Job Queue","version":"2.10.0"},"servers":[{"url":"https://spideriq.ai","description":"Production API"}],"paths":{"/api/v1/jobs/submit":{"post":{"tags":["Jobs"],"summary":"Submit Job","description":"Submit a new scraping job to SpiderIQ distributed scraping platform\n\n## Supported Job Types\n\n### 1. SpiderMaps (`type: \"spiderMaps\"`)\nScrape business listings from maps services.\n\n- **Required**: Either `url` OR `search_query` in payload\n- **AI Usage**: None (0 tokens)\n- **Processing Time**: 30-90 seconds for 20 results\n- **Features**: Reviews, photos, multi-language support\n\n### 2. SpiderSite (`type: \"spiderSite\"`)\nIntelligent website crawling with AI-powered lead generation.\n\n- **Required**: `url` in payload\n- **AI Usage**: Opt-in (0 tokens by default, ~500-3,800 tokens if AI enabled)\n- **Processing Time**: 5-60 seconds depending on pages and AI features\n- **Features**:\n  - v2.7.0: AI Context Engine (smart markdown compendiums + R2 storage)\n  - v2.4.0: SPA auto-detection with Playwright\n  - v2.3.0: Sitemap-first crawling\n  - v2.2.1: AI opt-in defaults (zero cost unless enabled)\n  - v2.1.0: Multilingual (36+ European languages)\n\n## SpiderIQ Features\n\n- **Automatic deduplication**: Returns cached job if submitted within 24 hours\n- **Distributed processing**: Jobs queued to RabbitMQ, processed by workers across multiple VPS servers\n- **Priority support**: 0-10 (higher number = processed first)\n- **Async processing**: Returns job ID immediately (<100ms), poll for results\n\n## Authentication\n\nRequires Bearer token: `Authorization: Bearer <client_id>:<api_key>:<api_secret>`\n\nContact admin to register a client and receive credentials.\n\n## Queue Limits\n\n- SpiderMaps queue: 10,000 jobs maximum\n- SpiderSite queue: 5,000 jobs maximum\n\n## Returns\n\nJob ID and initial status. Use `/jobs/{job_id}/status` to check progress.\n\n**YAML Input (v2.60.0)**:\nAccepts Content-Type: text/yaml for AI agent submissions.","operationId":"submit_job_api_v1_jobs_submit_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/JobSubmit"},{"type":"null"}],"title":"Job Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/{job_id}/status":{"get":{"tags":["Jobs"],"summary":"Get status of a single job","description":"Retrieve queue/processing/completion status for a job owned by the authenticated client. Does NOT return results — use `/jobs/{job_id}/results` for completed data. Supports `?format=yaml|md` for AI-agent-friendly output.","operationId":"get_job_status_api_v1_jobs__job_id__status_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: JSON)","title":"Format"},"description":"Response format: yaml or md (default: JSON)"}],"responses":{"200":{"description":"Job status retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatusResponse"}}}},"400":{"description":"Invalid job ID format","content":{"application/json":{"example":{"detail":"Invalid job ID format"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"404":{"description":"Job not found or doesn't belong to the authenticated client","content":{"application/json":{"example":{"detail":"Job not found"}}}},"422":{"description":"Validation error in query parameters","content":{"application/json":{"example":{"detail":"Validation error in request parameters"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/jobs/{job_id}/results":{"get":{"tags":["Jobs"],"summary":"Get results for a completed job","description":"Retrieve the results payload for a job owned by the authenticated client. Returns 200 with `data` when the job is `completed`, 202 while `queued`/`processing` (poll again), 410 when `failed` or `cancelled`. Response shape is flat (v2.7.6+) — social platforms live at `data.linkedin`, `data.twitter`, etc. Supports `?format=yaml|md` for AI-agent-friendly output.","operationId":"get_job_results_api_v1_jobs__job_id__results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: JSON)","title":"Format"},"description":"Response format: yaml or md (default: JSON)"}],"responses":{"200":{"description":"Job completed successfully - results are available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnifiedJobResponse"},"example":{"success":true,"job_id":"974ceeda-84fe-4634-bdcd-adc895c6bc75","type":"spiderSite","status":"completed","processing_time_seconds":19.77,"worker_id":"spider-site-main-1","completed_at":"2025-10-27T11:42:20Z","data":{"url":"https://example.com","pages_crawled":10,"emails":["contact@example.com"],"linkedin":"https://linkedin.com/company/example","facebook":"https://facebook.com/example"}}}}},"202":{"description":"Job is queued or processing - results not ready yet. Poll this endpoint to check for completion.","content":{"application/json":{"examples":{"queued":{"summary":"Job waiting in queue","value":{"success":false,"job_id":"abc-123-xyz","type":"spiderSite","status":"queued","message":"Job is waiting in queue. Poll this endpoint to check for results."}},"processing":{"summary":"Job being processed by worker","value":{"success":false,"job_id":"def-456-uvw","type":"spiderMaps","status":"processing","message":"Job is being processed by worker. Poll this endpoint to check for results.","worker_id":"spider-maps-main-1"}}}}}},"400":{"description":"Invalid job ID format","content":{"application/json":{"example":{"detail":"Invalid job ID format. Expected UUID."}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_auth":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"404":{"description":"Job not found or doesn't belong to your account","content":{"application/json":{"example":{"detail":"Job not found"}}}},"410":{"description":"Job failed or was cancelled - no results available","content":{"application/json":{"examples":{"failed":{"summary":"Job failed","value":{"success":false,"job_id":"ghi-789-rst","type":"spiderSite","status":"failed","processing_time_seconds":3.5,"worker_id":"spider-site-llm4-2","completed_at":"2025-10-27T13:00:00Z","error_message":"Connection timeout after 30 seconds"}},"cancelled":{"summary":"Job cancelled by user","value":{"success":false,"job_id":"jkl-012-opq","type":"spiderMaps","status":"cancelled","completed_at":"2025-10-27T14:00:00Z"}}}}}},"422":{"description":"Internal validation error - Response serialization failed. This should NOT occur in normal operation. If you encounter this error, please contact support as it indicates a bug in the API.","content":{"application/json":{"example":{"detail":[{"type":"missing","loc":["response","success"],"msg":"Field required"}]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/jobs/{job_id}/workflow-results":{"get":{"tags":["Jobs","SpiderMaps"],"summary":"Get workflow results for SpiderMaps job","description":"Get complete workflow results for a SpiderMaps job submitted with workflow configuration.\n\nReturns aggregated data from SpiderMaps → SpiderSite → SpiderVerify chain.\n\n**Query Parameters:**\n- `wait=true` (default): Block until all businesses complete or timeout\n- `wait=false`: Return current status immediately (for polling)\n\n**Note:** Only works for SpiderMaps jobs submitted with `workflow` configuration.\nFor jobs without workflow, use the regular `/jobs/{job_id}/results` endpoint.\n\n**Timeouts:**\n- SpiderSite: 5 minutes per business\n- SpiderVerify: 2 minutes per business\n- Maximum total: 10 minutes","operationId":"get_job_workflow_results_api_v1_jobs__job_id__workflow_results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"wait","in":"query","required":false,"schema":{"type":"boolean","description":"Wait for completion (blocking). If false, returns current state immediately.","default":true,"title":"Wait"},"description":"Wait for completion (blocking). If false, returns current state immediately."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/list":{"get":{"tags":["Jobs"],"summary":"List jobs for the authenticated client","description":"Return a paginated list of jobs belonging to the authenticated client. Filter by `status_filter` (queued/processing/completed/failed/cancelled) and/or `type_filter` (spiderMaps/spiderSite/...). Supports `?format=yaml|md` for AI-agent-friendly output.","operationId":"list_jobs_api_v1_jobs_list_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/JobStatus"},{"type":"null"}],"description":"Filter by job status","title":"Status Filter"},"description":"Filter by job status"},{"name":"type_filter","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/JobType"},{"type":"null"}],"description":"Filter by job type","title":"Type Filter"},"description":"Filter by job type"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":50,"title":"Page Size"},"description":"Items per page"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: JSON)","title":"Format"},"description":"Response format: yaml or md (default: JSON)"}],"responses":{"200":{"description":"Paginated list of jobs for the authenticated client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobListResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Invalid query parameters (status, type, page, page_size, format)","content":{"application/json":{"example":{"detail":"Validation error in query parameters"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/jobs/{job_id}":{"delete":{"tags":["Jobs"],"summary":"Cancel a queued or processing job","description":"Cancel a job owned by the authenticated client. Only jobs in `queued` or `processing` state can be cancelled; `completed`, `failed`, or already-`cancelled` jobs return 400. Cancellation is best-effort — a worker that has already claimed the job may still complete it before seeing the flag.","operationId":"cancel_job_api_v1_jobs__job_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Job cancelled successfully","content":{"application/json":{"schema":{},"example":{"message":"Job cancelled","job_id":"660e8400-e29b-41d4-a716-446655440000"}}}},"400":{"description":"Invalid job ID format or job cannot be cancelled in its current state","content":{"application/json":{"examples":{"bad_uuid":{"summary":"Invalid UUID","value":{"detail":"Invalid job ID format"}},"wrong_status":{"summary":"Job already finished","value":{"detail":"Cannot cancel job with status 'completed'"}}}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"404":{"description":"Job not found or doesn't belong to the authenticated client","content":{"application/json":{"example":{"detail":"Job not found"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderCompanyData/submit":{"post":{"tags":["Jobs","SpiderCompanyData"],"summary":"Submit company registry lookup job","description":"Look up company records in US (SEC EDGAR), UK (Companies House), and EU business registries. Returns officers, filings, registered address, and structured metadata. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the aggregated registry payload.","operationId":"submit_spider_company_data_job_api_v1_jobs_spiderCompanyData_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderCompanyDataJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderVayapin/submit":{"post":{"tags":["Jobs","SpiderVayapin"],"summary":"Submit VayaPin profile / outreach job","description":"Create or update VayaPin business profiles from extracted leads. Triggers the VayaPin automation stack (profile enrichment, outreach scheduling). Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for per-profile outcomes.","operationId":"submit_spider_vayapin_job_api_v1_jobs_spiderVayapin_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderVayapinJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderMail/submit":{"post":{"tags":["Jobs","SpiderMail"],"summary":"Submit mail send / reply job","description":"Send an email or reply to an existing thread through the SpiderMail worker. Delivery goes through the client's configured mailbox (SMTP or provider API). Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the delivery outcome and message ID.","operationId":"submit_spider_mail_job_api_v1_jobs_spiderMail_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderMailJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderMaps/submit":{"post":{"tags":["Jobs","SpiderMaps"],"summary":"Submit Google Maps scraping job","description":"Scrape Google Maps business listings by search query or direct Maps URL. Optionally enrich via an attached `workflow` (chain SpiderSite + SpiderVerify). Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the flat response. Per-VPS rate limit applies (10 jobs/min).","operationId":"submit_spider_maps_job_api_v1_jobs_spiderMaps_submit_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SpiderMapsJobSubmit"},{"type":"null"}],"title":"Job Data"}}}},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"examples":{"missing_url_and_query":{"summary":"Missing URL and search_query","value":{"detail":"Invalid Google Maps payload: Either 'url' or 'search_query' must be provided"}},"invalid_max_results":{"summary":"max_results out of range","value":{"detail":[{"type":"less_than_equal","loc":["body","payload","max_results"],"msg":"Input should be less than or equal to 100","input":150}]}}}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderSite/submit":{"post":{"tags":["Jobs","SpiderSite"],"summary":"Submit website crawl job","description":"Crawl a website and extract contact information, company vitals, social links, and AI-enriched lead data. Supports single-URL and multi-page crawls (best-first / BFS / DFS). Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the flat (v2.7.6+) response.","operationId":"submit_spider_site_job_api_v1_jobs_spiderSite_submit_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SpiderSiteJobSubmit"},{"type":"null"}],"title":"Job Data"}}}},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderVerify/submit":{"post":{"tags":["Jobs","SpiderVerify"],"summary":"Submit email verification job","description":"Verify one or more email addresses against SMTP, MX, DNSBL, disposable-domain, catch-all, and Gravatar checks. Accepts `email` (single) or `emails` (bulk, up to payload limit). Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for per-address verdicts.","operationId":"submit_spider_verify_job_api_v1_jobs_spiderVerify_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderVerifyJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderPeople/submit":{"post":{"tags":["Jobs","SpiderPeople"],"summary":"Submit people-search / enrichment job","description":"Search for people by name, title, or company, or enrich an existing lead with email/phone/social. Operates in `search` or `enrich` mode depending on the payload. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for aggregated people records.","operationId":"submit_spider_people_job_api_v1_jobs_spiderPeople_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderPeopleJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderPhone/submit":{"post":{"tags":["Jobs","SpiderPhone"],"summary":"Submit phone lookup / outreach job","description":"Look up phone numbers from Google Maps listings or trigger phone-based outreach actions via the iPhone bridge. `platform` and `action` in the payload select the behaviour. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the result set.","operationId":"submit_spider_phone_job_api_v1_jobs_spiderPhone_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderPhoneJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderMapsEnrich/submit":{"post":{"tags":["Jobs","SpiderMapsEnrich"],"summary":"Submit Google Maps business-data enrichment job","description":"Enrich an existing Google Maps business (by place_id or search) with reviews, photos, opening hours, and extended attributes. Use when `/spiderMaps` results need deeper detail. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the enriched record.","operationId":"submit_spider_maps_enrich_job_api_v1_jobs_spiderMapsEnrich_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderMapsEnrichJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderFacebookPage/submit":{"post":{"tags":["Jobs","SpiderFacebookPage"],"summary":"Submit Facebook page scraping job","description":"Scrape a public Facebook Page for posts, about info, contact details, and engagement metadata. Accepts the page URL or numeric page ID. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the structured page payload.","operationId":"submit_spider_facebook_page_job_api_v1_jobs_spiderFacebookPage_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderFacebookPageJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderPublicInstagram/submit":{"post":{"tags":["Jobs","SpiderPublicInstagram"],"summary":"Submit Instagram profile scraping job","description":"Scrape a public Instagram profile for bio, recent posts, follower counts, and contact fields exposed via the `business_contact` schema. Accepts the profile URL or handle. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the structured profile payload.","operationId":"submit_spider_public_instagram_job_api_v1_jobs_spiderPublicInstagram_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderPublicInstagramJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderPublicLinkedin/submit":{"post":{"tags":["Jobs","SpiderPublicLinkedin"],"summary":"Submit LinkedIn scraping job","description":"Scrape LinkedIn profiles and companies using Voyager API. Requires mobile proxy.","operationId":"submit_spider_public_linkedin_job_api_v1_jobs_spiderPublicLinkedin_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderPublicLinkedinJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderLanding/submit":{"post":{"tags":["Jobs","SpiderLanding"],"summary":"Submit landing page capture job","description":"Capture landing pages with screenshots, HTML bundles, and AI-extracted marketing content.","operationId":"submit_spider_landing_job_api_v1_jobs_spiderLanding_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderLandingJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderVideo/submit":{"post":{"tags":["Jobs","SpiderVideo"],"summary":"Submit video stitching job","description":"Stitch AI-generated video scenes into final video using Remotion.","operationId":"submit_spider_video_job_api_v1_jobs_spiderVideo_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderVideoJobSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderVideo/extract-frames/submit":{"post":{"tags":["Jobs","SpiderVideo"],"summary":"Extract numbered image sequence from a video (scroll-sequence pipeline)","description":"Run ffmpeg against a source video to produce a numbered sequence of web-optimized WebP/JPEG frames suitable for canvas + GSAP ScrollTrigger scroll-linked image sequences. Output manifest plugs directly into the `sys-scroll-sequence` system component as `{base_url, pattern, count}`.","operationId":"submit_extract_frames_job_api_v1_jobs_spiderVideo_extract_frames_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractFramesSubmit"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/system/health":{"get":{"tags":["System"],"summary":"Health Check","description":"Health check endpoint for monitoring\n\nChecks:\n    - API server status\n    - PostgreSQL connection\n    - Redis connection\n    - RabbitMQ connection\n\nReturns:\n    Service status information","operationId":"health_check_api_v1_system_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/ready":{"get":{"tags":["System"],"summary":"Readiness Check","description":"Engine-honest readiness probe — the blue-green / Traefik health gate.\n\nUnlike ``/health`` and ``/api/gate/v1/models`` (which return 200 *before*\nthe litellm Router is built — LEARNINGS #37), this returns **503 until**\nthe SpiderGate engine has fully initialized AND a Router was built with at\nleast one deployment AND Postgres answers a live query. Traefik adds a new\n(green) container to the pool ONLY when this is 200, so the ~27 s engine\nboot happens on a slot no user is hitting yet — eliminating the deploy 502\nwindow (see docs/services/Infrastructure/blue-green-traefik-proxy.md §6.3).\n\nNOTE: this is the api-gateway readiness gate. content-api shares this image\nbut runs a minimal lifespan that does NOT build the Router, so it would\nreport not_ready here — its blue-green gate (phase 3) needs a content probe.\n\nReturns 200 ``{\"status\": \"ready\", ...}`` when serveable;\n503 ``{\"status\": \"not_ready\", ...}`` (Retry-After: 5) while booting.","operationId":"readiness_check_api_v1_system_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/queue-stats":{"get":{"tags":["System"],"summary":"Get Queue Stats","description":"Get queue statistics\n\nReturns:\n    Current queue depths for all job types","operationId":"get_queue_stats_api_v1_system_queue_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/credential-health":{"get":{"tags":["System"],"summary":"Credential Health","description":"Count active clients missing Fernet-encrypted credentials.\n\n`missing_encrypted_credentials` must stay at 0 — any non-zero value means\none or more active clients cannot have their Bearer token reconstructed\nby the scheduler, so their WindMill campaigns will be skipped (and the\nscheduler will emit ERROR logs every cycle until fixed).\nRun `python -m scripts.backfill_missing_encrypted_credentials` to rotate.","operationId":"credential_health_api_v1_system_credential_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/cal-health":{"get":{"tags":["System"],"summary":"Cal Health","description":"Probe the self-hosted Cal.com stack (SpiderBook P1.3).\n\nCal.com v6.2.0 does not ship a public ``/api/health`` endpoint — that\nlives on the separate NestJS v2 API image. We mirror the upstream\ncontainer healthcheck and probe the Next.js root, following the normal\nredirect chain until we hit a 2xx. Same logic as\n``scripts/cal_health_check.sh``.\n\nReturns ``{\"status\": \"healthy\"}`` on success. Raises 503 with a\n``Retry-After: 30`` header if Cal.com is unreachable or returns an\nerror — callers should back off at least 30s before retrying.","operationId":"cal_health_api_v1_system_cal_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/info":{"get":{"tags":["System"],"summary":"Get System Info","description":"Get system information\n\nReturns:\n    API configuration and version information","operationId":"get_system_info_api_v1_system_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/booking/{flow_id}/hold-slot":{"post":{"tags":["booking"],"summary":"Reserve a booking slot for 10 minutes","description":"Create a 10-minute hold for `(flow_id, slot_start, staff_id)`.\n\nConcurrency: `create_hold()` serializes through a per-slot advisory lock,\nso two simultaneous calls for the same slot resolve deterministically —\nfirst wins with 201, second gets 409.","operationId":"hold_slot_api_v1_booking__flow_id__hold_slot_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldSlotRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldSlotResponse"}}}},"409":{"description":"An active hold already exists for this slot."},"404":{"description":"Flow not found or inactive."},"429":{"description":"IP rate limit exceeded."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/holds/{hold_id}/release":{"post":{"tags":["booking"],"summary":"Release a slot hold (internal)","description":"Idempotent release. Called from the P3.3 public submit endpoint after\na successful Cal.com booking-create — returning the slot to availability\nimmediately instead of waiting for the 10-minute TTL or the cleanup cron.","operationId":"release_slot_hold_api_v1_booking_holds__hold_id__release_post","parameters":[{"name":"hold_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Hold Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseHoldResponse"}}}},"404":{"description":"Hold not found."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/{flow_id}/submit":{"post":{"tags":["SpiderBook"],"summary":"Public submit — confirm a booking OR persist a form submission (no auth)","description":"Submit against an active flow.\n\nSpiderFlow P1.W (2026-05-13) + P2.1 (2026-05-14) — the body schema is\ndispatched at runtime based on the flow's ``kind`` discriminator\n(mig 233):\n\n* ``kind='booking'`` / ``'commerce'`` — body must satisfy\n  :class:`BookingAnswers` (slot_start / slot_end / hold_id / contact /\n  consent + optional service_id / staff_id / extra). The Cal.com booking\n  path + Turnstile + GDPR-booking-consent gates run as before.\n\n* ``kind='form'`` — body is a flat ``{field_id: value}`` map (or nested\n  ``{step_id: {field_id: value}}``); validated against the flow's\n  FormStep fields. Cal.com / hold / Turnstile / booking-consent gates\n  are NOT applied (forms are not bookings). The submission is persisted\n  to ``public.results`` with ``worker_type='booking', phase='final',\n  data.kind='form'`` so analytics / CRM-sync handlers can pick it up\n  without a new migration.\n\n* ``kind='funnel'`` (P2.1) — the public /submit endpoint is not\n  applicable. Funnels sequence SpiderPublish pages and don't carry a\n  single terminal submission event. Callers receive a structured 422\n  ``submit_not_applicable_for_funnel`` envelope. To collect data from a\n  funnel visitor, embed a form sub-flow whose own ``/submit`` persists\n  the data.\n\nRoute-level dependencies are intentionally light; every check happens\ninside the handler so one handler serves the whole flow (G2→G15) and\ntests can exercise the full chain without wrangling FastAPI's\ndependency tree.","operationId":"submit_booking_api_v1_booking__flow_id__submit_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Consent missing, invalid payload, or unknown / missing required form field."},"403":{"description":"Turnstile verification failed."},"404":{"description":"Flow not found or inactive."},"409":{"description":"Slot hold expired or Cal.com reported slot taken."},"422":{"description":"Body fails BookingAnswers validation (kind='booking' / 'commerce' only) OR kind='funnel' submit is not applicable."},"429":{"description":"IP rate limit exceeded."},"503":{"description":"Cal.com temporarily unavailable — retry."}}}},"/api/v1/booking/{flow_id}":{"get":{"tags":["SpiderBook"],"summary":"Fetch a booking flow descriptor (agent-facing)","description":"Returns a minimal {flow_id, kind, name, status} descriptor for the given flow_id. Intended for agents (Claude Code, MCP tools) to discover a flow's kind before driving the full /render endpoint. On wrong kind, returns 409 with a suggested_url pointing at the right endpoint. On unknown id, returns 404 RESOURCE_NOT_FOUND.","operationId":"get_booking_flow_descriptor_api_v1_booking__flow_id__get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDescriptorResponse"}}}},"404":{"description":"Flow not found (RESOURCE_NOT_FOUND envelope)"},"409":{"description":"Flow is not a booking (WRONG_FLOW_KIND envelope)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/{flow_id}/render":{"get":{"tags":["SpiderBook"],"summary":"Fetch the public flow payload (localized via Accept-Language)","description":"Returns the flow JSON the booking component mounts. Honours Accept-Language: labels/descriptions/button_label are swapped via translations[locale] with fallback to English. Unknown flows → 404.","operationId":"render_flow_api_v1_booking__flow_id__render_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderFlowResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Record a flow-render event (public, no auth)","description":"Fire-and-forget analytics write called by the booking component on mount and step transitions. Never returns 5xx — analytics is non-critical.","operationId":"record_render_event_api_v1_booking__flow_id__render_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderEventBody"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderEventResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/manage/{token}":{"get":{"tags":["SpiderBook"],"summary":"Customer self-service — load booking details (no auth; signed token)","description":"Return booking details for the holder of a valid signed token.","operationId":"manage_get_booking_api_v1_booking_manage__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageBookingResponse"}}}},"403":{"description":"Token invalid, expired, or tenant mismatch."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/manage/{token}/reschedule":{"post":{"tags":["SpiderBook"],"summary":"Customer self-service — reschedule booking (no auth; signed token)","description":"Reschedule a booking. Calls Cal.com, appends a ``rescheduled`` row,\nand sends the customer a follow-up email.","operationId":"manage_reschedule_booking_api_v1_booking_manage__token__reschedule_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageRescheduleBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageBookingResponse"}}}},"400":{"description":"Invalid payload or slot_start in the past."},"403":{"description":"Token invalid, expired, or tenant mismatch."},"409":{"description":"Booking is already cancelled or in a terminal state."},"503":{"description":"Cal.com temporarily unavailable — retry."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/manage/{token}/cancel":{"post":{"tags":["SpiderBook"],"summary":"Customer self-service — cancel booking (no auth; signed token)","description":"Cancel a booking. Calls Cal.com, appends a ``cancelled`` row, and\nsends the customer a confirmation of cancellation.","operationId":"manage_cancel_booking_api_v1_booking_manage__token__cancel_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageCancelBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageBookingResponse"}}}},"403":{"description":"Token invalid, expired, or tenant mismatch."},"409":{"description":"Booking is already cancelled or in a terminal state."},"503":{"description":"Cal.com temporarily unavailable — retry."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/booking/{flow_id}/upload-presign":{"post":{"tags":["SpiderBook"],"summary":"Get a signed URL to upload a form file_upload field's file","description":"Issue a presigned SeaweedFS PUT URL for one ``file_upload`` field.\n\nValidation order: rate limit → flow exists+active → ``kind='form'`` →\nfield exists and is ``file_upload`` → content-type allowed (skipped when\nthe field has no ``accept`` whitelist) → size within the field's\n``max_size_mb`` → presign.","operationId":"create_upload_presign_api_v1_booking__flow_id__upload_presign_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadPresignRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadPresignResponse"}}}},"404":{"description":"Flow not found, inactive, or draft."},"422":{"description":"Validation failed — non-form flow, unknown / wrong field, content-type or size rejected."},"429":{"description":"IP rate limit exceeded."},"503":{"description":"File uploads not available for this form."}}}},"/api/v1/forms/places-autocomplete":{"post":{"tags":["SpiderFlow Forms"],"summary":"Google Places Autocomplete proxy","description":"Forward the typeahead query to Google Places and return normalised\npredictions.","operationId":"places_autocomplete_api_v1_forms_places_autocomplete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacesAutocompleteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacesAutocompleteResponse"}}}},"429":{"description":"Per-IP rate limit exceeded."},"502":{"description":"Upstream Places API error."},"503":{"description":"Places integration not configured for this deployment."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/forms/places-details":{"post":{"tags":["SpiderFlow Forms"],"summary":"Google Places Details proxy","description":"Fetch full Place Details for the given place_id. Called by the\nrenderer when a user picks a suggestion.","operationId":"places_details_api_v1_forms_places_details_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacesDetailsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacesDetailsResponse"}}}},"404":{"description":"Place id not found."},"429":{"description":"Per-IP rate limit exceeded."},"502":{"description":"Upstream Places API error."},"503":{"description":"Places integration not configured for this deployment."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/forms/{flow_id}":{"get":{"tags":["SpiderFlow Forms"],"summary":"Fetch a form flow descriptor (agent-facing)","description":"Returns a minimal {flow_id, kind, name, status} descriptor for the given flow_id. Intended for agents (Claude Code, MCP tools) to discover a flow's kind before driving form-specific tooling. On wrong kind, returns 409 with suggested_url=/api/v1/booking/<id>. On unknown id, returns 404 RESOURCE_NOT_FOUND.","operationId":"get_form_flow_descriptor_api_v1_forms__flow_id__get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormFlowDescriptorResponse"}}}},"404":{"description":"Flow not found (RESOURCE_NOT_FOUND envelope)"},"409":{"description":"Flow is not a form (WRONG_FLOW_KIND envelope)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/visual-check":{"post":{"tags":["MCP — Visual Check"],"summary":"Open a URL in a real browser and report what an end-user sees.","description":"Proxies to the visual-check Playwright sidecar. Returns screenshot URL (R2-hosted), final URL, status code, DOM info, console errors, failed requests, and per-assertion results. URL must be on the allowlist (spideriq.ai subdomains + tenant verified domains). 20 req/min/client. 30s default timeout, 60s hard cap.","operationId":"visual_check_api_v1_mcp_visual_check_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualCheckRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualCheckResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/u/{token}":{"get":{"tags":["notifications:public"],"summary":"Unsubscribe Confirm","description":"Confirmation landing page. NEVER mutates state — link-scanners\nthat prefetch the URL must see only a page.","operationId":"unsubscribe_confirm_u__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["notifications:public"],"summary":"Unsubscribe Apply","description":"Apply the unsubscribe and record an audit row.\n\nIdempotent: re-POSTing the same token flips already-FALSE rows to\nFALSE (no-op) and inserts a second audit row. Audit rows reflect\nuser *intents*, not state transitions.\n\nAll DB work happens in ONE transaction so the audit row and the\npreference UPSERTs commit together.","operationId":"unsubscribe_apply_u__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/preferences":{"get":{"tags":["notifications"],"summary":"Get Notification Preferences","description":"Return the per-(user, brand) subscription matrix, grouped by section.\n\nSections + events come from the in-process catalog (1.1's\n`services.notifications.catalog.CATALOG`); per-event values are\nthe user's overrides where set, otherwise the catalog defaults.","operationId":"get_notification_preferences_api_v1_notifications_preferences_get","parameters":[{"name":"brand_id","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Brand to load preferences for","title":"Brand Id"},"description":"Brand to load preferences for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferencesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["notifications"],"summary":"Patch Notification Preferences","description":"Bulk UPSERT partial overrides for one (user, brand).\n\nRequest body:\n    {\"updates\": [\n        {\"event_key\": \"campaign.terminal\", \"email\": false},\n        {\"event_key\": \"job.failed\", \"delivery\": \"digest-daily\"}\n    ]}\n\nFields absent from a per-event update preserve the existing row's\nvalue (or fall back to the catalog default if no row exists yet).\n`standalone_only=True` events force `delivery='standalone'` even if\nthe caller sends another value — the catalog wins.\n\nUnknown `event_key` or invalid `delivery` → 400.\nNon-member brand → 403.","operationId":"patch_notification_preferences_api_v1_notifications_preferences_patch","parameters":[{"name":"brand_id","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Brand the preferences apply to","title":"Brand Id"},"description":"Brand the preferences apply to"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferencesPatchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferencesPatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/feed":{"get":{"tags":["notifications"],"summary":"Get Notification Feed","description":"Return the bell dropdown payload for ``(user, brand)``.\n\nThe unread branch is always top-N most recent; the read branch is\nkeyset-paginated by ``(created_at, id)`` for the rare case the user\nscrolls past 20.","operationId":"get_notification_feed_api_v1_notifications_feed_get","parameters":[{"name":"brand_id","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Brand to load the bell feed for","title":"Brand Id"},"description":"Brand to load the bell feed for"},{"name":"unread_only","in":"query","required":false,"schema":{"type":"boolean","description":"Skip the read mix","default":false,"title":"Unread Only"},"description":"Skip the read mix"},{"name":"unread_limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Cap on unread rows returned","default":30,"title":"Unread Limit"},"description":"Cap on unread rows returned"},{"name":"read_limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":0,"description":"Cap on read rows returned (0 disables)","default":20,"title":"Read Limit"},"description":"Cap on read rows returned (0 disables)"},{"name":"before_created_at","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Keyset cursor (created_at half) — pass the last read row's created_at to page deeper","title":"Before Created At"},"description":"Keyset cursor (created_at half) — pass the last read row's created_at to page deeper"},{"name":"before_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Keyset cursor (id half) — pass alongside before_created_at","title":"Before Id"},"description":"Keyset cursor (id half) — pass alongside before_created_at"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/unread-count":{"get":{"tags":["notifications"],"summary":"Get Notification Unread Count","description":"Single-number response for the bell badge.","operationId":"get_notification_unread_count_api_v1_notifications_unread_count_get","parameters":[{"name":"brand_id","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Brand to count unread for","title":"Brand Id"},"description":"Brand to count unread for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/{notification_id}/read":{"post":{"tags":["notifications"],"summary":"Mark Notification Read","description":"Flip ``read_at`` on one row. Idempotent: re-marking a read row is a\nno-op (the COALESCE in feed_service keeps the original timestamp).\n404 if the row doesn't exist OR doesn't belong to ``(user, brand)``\n— same response shape for both because we never reveal which.","operationId":"mark_notification_read_api_v1_notifications__notification_id__read_post","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"integer","title":"Notification Id"}},{"name":"brand_id","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Brand the notification belongs to","title":"Brand Id"},"description":"Brand the notification belongs to"},{"name":"created_at","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Row's created_at (from the feed response) — required for partition pruning on the partitioned notification_log","title":"Created At"},"description":"Row's created_at (from the feed response) — required for partition pruning on the partitioned notification_log"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/read-all":{"post":{"tags":["notifications"],"summary":"Mark All Notifications Read","description":"Bulk-clear unread for ``(user, brand)``. Returns the count flipped.","operationId":"mark_all_notifications_read_api_v1_notifications_read_all_post","parameters":[{"name":"brand_id","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Brand to clear unread for","title":"Brand Id"},"description":"Brand to clear unread for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkAllReadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auto-search":{"post":{"tags":["Auto-Search"],"summary":"Submit new auto-search job","description":"Submit a new auto-search job to automatically collect unique business results\n    across a geographic area.\n\n    The system will:\n    1. Generate location-specific queries based on the provided geography\n    2. Submit queries to SpiderMaps workers in batches\n    3. Deduplicate results by place_id\n    4. Stop when the total_limit is reached or all locations are exhausted\n    5. Upload results to R2 CDN\n\n    **Examples:**\n    - Search entire US: `{\"keyword\": \"dentist\", \"country\": \"US\"}`\n    - Search California: `{\"keyword\": \"pizza\", \"country\": \"US\", \"state\": \"CA\"}`\n    - Search Los Angeles: `{\"keyword\": \"hotel\", \"country\": \"US\", \"state\": \"CA\", \"city\": \"Los Angeles\"}`","operationId":"submit_auto_search_api_v1_auto_search_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSearchSubmitRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSearchSubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Auto-Search"],"summary":"List auto-search jobs","description":"Get paginated list of all auto-search jobs for the authenticated client","operationId":"list_auto_searches_api_v1_auto_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/AutoSearchStatus"},{"type":"null"}],"description":"Filter by status","title":"Status Filter"},"description":"Filter by status"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSearchListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auto-search/{auto_search_id}":{"get":{"tags":["Auto-Search"],"summary":"Get auto-search status","description":"Get detailed status and progress information for an auto-search job","operationId":"get_auto_search_status_api_v1_auto_search__auto_search_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"auto_search_id","in":"path","required":true,"schema":{"type":"string","title":"Auto Search Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSearchStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Auto-Search"],"summary":"Cancel auto-search job","description":"Cancel a running auto-search job.\n\n    This will:\n    1. Delete the job-specific RabbitMQ queues (cancels pending queries)\n    2. Update the job status to 'cancelled'\n    3. Clean up Redis tracking data\n\n    Already completed queries will remain in the results.","operationId":"cancel_auto_search_api_v1_auto_search__auto_search_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"auto_search_id","in":"path","required":true,"schema":{"type":"string","title":"Auto Search Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSearchCancelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auto-search/{auto_search_id}/results":{"get":{"tags":["Auto-Search"],"summary":"Get auto-search results","description":"Get download URL and metadata for auto-search results","operationId":"get_auto_search_results_api_v1_auto_search__auto_search_id__results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"auto_search_id","in":"path","required":true,"schema":{"type":"string","title":"Auto Search Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSearchResultsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/locations/import":{"post":{"tags":["Locations"],"summary":"Import locations","description":"Import multiple locations into the database.\n\n    **Use Cases:**\n    - Add postcodes for a specific city (e.g., Paris postcodes 75001-75020)\n    - Add custom locations for a region\n    - Bulk import city data\n\n    **Example - Add Paris postcodes:**\n    ```json\n    {\n      \"country_code\": \"FR\",\n      \"parent_city\": \"Paris\",\n      \"location_type\": \"postcode\",\n      \"locations\": [\n        {\"search_string\": \"75001, France\", \"display_name\": \"Paris 1er\", \"latitude\": 48.86, \"longitude\": 2.34},\n        {\"search_string\": \"75002, France\", \"display_name\": \"Paris 2ème\", \"latitude\": 48.87, \"longitude\": 2.34}\n      ]\n    }\n    ```","operationId":"import_locations_api_v1_locations_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationImportRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationImportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/locations":{"get":{"tags":["Locations"],"summary":"List locations","description":"List locations with filtering and pagination.\n\n    **Filter Options:**\n    - `country_code`: Filter by ISO 2-letter country code (e.g., FR, DE, US)\n    - `location_type`: Filter by type (city or postcode)\n    - `parent_city`: Filter postcodes by parent city\n    - `admin_region`: Filter by state/province/region\n    - `min_population` / `max_population`: Filter by population range\n    - `needs_postcodes`: Filter big cities that need postcode breakdown\n    - `search`: Search in display_name or search_string","operationId":"list_locations_api_v1_locations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"description":"ISO 2-letter country code","title":"Country Code"},"description":"ISO 2-letter country code"},{"name":"location_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(city|postcode)$"},{"type":"null"}],"description":"Location type","title":"Location Type"},"description":"Location type"},{"name":"parent_city","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Parent city (for postcodes)","title":"Parent City"},"description":"Parent city (for postcodes)"},{"name":"admin_region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"State/Province/Region","title":"Admin Region"},"description":"State/Province/Region"},{"name":"min_population","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Minimum population","title":"Min Population"},"description":"Minimum population"},{"name":"max_population","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Maximum population","title":"Max Population"},"description":"Maximum population"},{"name":"needs_postcodes","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter big cities needing postcodes","title":"Needs Postcodes"},"description":"Filter big cities needing postcodes"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search in display_name or search_string","title":"Search"},"description":"Search in display_name or search_string"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Items per page","default":50,"title":"Page Size"},"description":"Items per page"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/locations/countries":{"get":{"tags":["Locations"],"summary":"List available countries","description":"Get list of all countries with location counts.","operationId":"list_countries_api_v1_locations_countries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CountryListResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/locations/regions":{"get":{"tags":["Locations"],"summary":"List admin regions (states) for a country","description":"List the admin regions (US states / provinces) of a country, each with its city/postcode split. For US ZIP campaigns the state is the selection unit — pick one region and run its postcodes (each ZIP is its own Maps search). 'All of US' is not selectable for a ZIP campaign (the volume guard 422s ~46K locations / >10K cap).","operationId":"list_regions_api_v1_locations_regions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"country_code","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":2,"description":"ISO country code, e.g. US","title":"Country Code"},"description":"ISO country code, e.g. US"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/locations/selectable-units":{"get":{"tags":["Locations"],"summary":"List selectable geo units (countries + US states)","description":"Flat, alphabetically merged list of selectable geo units for a typeahead picker. Each unit is a whole country (kind='country') or a US state (kind='state'). With states_as_units=true (default) the US row is dropped and its 50 states appear as top-level units — so an agent targets 'Florida' the same way it targets 'Germany', and 'all of USA' cannot be picked for a ZIP campaign. Set states_as_units=false for plain countries (US included) when the country is the correct unit.","operationId":"list_selectable_units_api_v1_locations_selectable_units_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"states_as_units","in":"query","required":false,"schema":{"type":"boolean","description":"When true (default), replace US with its 50 states as top-level units.","default":true,"title":"States As Units"},"description":"When true (default), replace US with its 50 states as top-level units."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelectableUnitsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/locations/stats":{"get":{"tags":["Locations"],"summary":"Get location statistics","description":"Get overall location database statistics.\n\n    Returns:\n    - Total locations, countries, cities, postcodes\n    - Count of cities needing postcode breakdown\n    - Top 20 countries by location count","operationId":"get_location_stats_api_v1_locations_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationStats"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/locations/{location_id}":{"get":{"tags":["Locations"],"summary":"Get location by ID","description":"Get details of a specific location.","operationId":"get_location_api_v1_locations__location_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"location_id","in":"path","required":true,"schema":{"type":"integer","title":"Location Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/submit":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Create a new SpiderMaps campaign","description":"Create a new SpiderMaps scraping campaign for a specific country and query.\n\n    **What happens:**\n    1. Campaign is created with specified filters\n    2. Matching locations are pre-computed and stored\n    3. Campaign is ready for `/next` calls\n\n    **Filter Modes:**\n    - `all` - All locations in the country (default)\n    - `population` - Filter by min/max population\n    - `cities_only` - Only cities, no postcodes\n    - `custom` - Specific location_ids\n    - `regions` - Specific admin regions\n\n    **Example - Scrape restaurants in France:**\n    ```json\n    {\n      \"query\": \"restaurants\",\n      \"country_code\": \"FR\",\n      \"name\": \"France Restaurants 2024\",\n      \"filter\": {\n        \"mode\": \"population\",\n        \"min_population\": 50000\n      }\n    }\n    ```\n\n    **Returns:**\n    - `campaign_id` - Use this for subsequent calls\n    - `total_locations` - Number of locations to scrape\n    - `next_location_id` - First location to be processed","operationId":"create_campaign_api_v1_jobs_spiderMaps_campaigns_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}":{"patch":{"tags":["SpiderMaps Campaigns"],"summary":"Update campaign configuration","description":"Update an existing campaign's configuration.\n\n    **What can be updated:**\n    - `search_query` - Changes search term for future jobs\n    - `name` - Campaign display name\n    - SpiderMaps options (max_results, extract_reviews, extract_photos, lang, etc.)\n    - `workflow` - Workflow configuration (merged with existing)\n\n    **What cannot be updated:**\n    - `country_code` - Would change location set\n    - `filter` - Locations already computed at creation\n\n    **Restrictions:**\n    - Only active or stopped/paused campaigns can be updated\n    - Completed campaigns cannot be modified\n    - Changes only affect PENDING locations (already submitted jobs unaffected)\n\n    **Example - Update search query and max_results:**\n    ```json\n    {\n      \"search_query\": \"coffee shops\",\n      \"max_results\": 200\n    }\n    ```\n\n    **Example - Enable workflow after campaign creation:**\n    ```json\n    {\n      \"workflow\": {\n        \"spidersite\": {\n          \"enabled\": true,\n          \"extract_team\": true\n        }\n      }\n    }\n    ```\n\n    **Returns:**\n    Full campaign status with updated values (same as GET /status endpoint).","operationId":"update_campaign_api_v1_jobs_spiderMaps_campaigns__campaign_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderMaps Campaigns"],"summary":"Delete a campaign","description":"Delete a campaign and all of its related data.\n\n    The campaign must be **stopped first** — this returns 409 if it still has\n    active jobs (`submitted`/`processing`). Cascade removes campaign locations,\n    workflow jobs, and run-tracking rows. Returns 404 if the campaign does not\n    exist or is not owned by the caller.","operationId":"delete_campaign_api_v1_jobs_spiderMaps_campaigns__campaign_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCampaignResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/next":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Get next location and submit job","description":"Get the next location for a campaign and automatically submit a SpiderMaps job.\n\n    **What happens:**\n    1. Finds the next pending location in the campaign\n    2. Submits a SpiderMaps job for that location\n    3. Updates campaign progress\n    4. Returns job details and progress\n\n    **N8N/Xano Workflow:**\n    ```\n    Loop while has_more == true:\n        POST /campaigns/{id}/next\n        # Wait for job to complete (optional)\n        # Process results\n    ```\n\n    **Response Fields:**\n    - `has_more` - true if more locations remain\n    - `current_task` - Details of the submitted job\n    - `progress` - Campaign progress statistics\n\n    **When `has_more` is false:**\n    - Campaign is complete\n    - No more locations to process","operationId":"get_next_and_submit_api_v1_jobs_spiderMaps_campaigns__campaign_id__next_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignNextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/status":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Get campaign status","description":"Get detailed status and statistics for a campaign.\n\n    **Returns:**\n    - Campaign configuration (query, country, filters)\n    - Progress statistics (completed, failed, pending)\n    - Job statistics by status (queued, processing, completed, failed)\n    - Total businesses found\n\n    **Query Parameters:**\n    - format: Response format (yaml, md). Default: JSON (v2.60.0)","operationId":"get_campaign_status_api_v1_jobs_spiderMaps_campaigns__campaign_id__status_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: JSON)","title":"Format"},"description":"Response format: yaml or md (default: JSON)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/stage-progress":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Get per-stage campaign progress","description":"Per-stage breakdown of campaign progress with a velocity-based ETA.\n\n    Replaces the misleading \"0/N 0%\" rollup with location-level\n    completed / active / queued / failed counters per pipeline stage\n    (Maps / Site / Verify / Vayapin). The stage list is derived from the\n    campaign's `workflow_config` — Site/Verify/Vayapin only appear when\n    their config block has enabled=True.\n\n    Cached in Redis for 30s. Bearer-authenticated mirror of\n    `/api/v1/dashboard/client/campaigns/{id}/stage-progress`.","operationId":"get_campaign_stage_progress_api_v1_jobs_spiderMaps_campaigns__campaign_id__stage_progress_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/runs":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Get campaign Inngest runs","description":"Get all Inngest workflow runs for a campaign.\n\n    **Returns:**\n    - List of runs with status, timing, and Inngest correlation info\n    - Useful for visualizing campaign workflow execution (waterfall diagram)\n\n    **Note:** Run IDs are correlated asynchronously after event submission.\n    New runs may take a few seconds to appear with full run_id.\n\n    v2.54.0: Added for Inngest run visualization feature.","operationId":"get_campaign_runs_api_v1_jobs_spiderMaps_campaigns__campaign_id__runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":20,"title":"Page Size"},"description":"Items per page"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignRunsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/runs/{run_id}":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Get campaign run detail with steps","description":"Get detailed information about a specific Inngest run including all steps.\n\n    **Returns:**\n    - Full run details with timing and status\n    - Steps array for waterfall visualization\n    - Pre-computed waterfall_data for frontend rendering\n\n    Use this endpoint to display the Inngest run waterfall trace.","operationId":"get_campaign_run_detail_api_v1_jobs_spiderMaps_campaigns__campaign_id__runs__run_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"integer","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignRunDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/workflow-progress":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Get workflow progress and health metrics","description":"Get real-time workflow progress and health metrics for a campaign.\n\n    **Returns:**\n    - Active/completed/failed run counts\n    - Details of currently running workflows with current step\n    - Location progress statistics\n    - Timing statistics (avg, min, max workflow duration)\n    - Health metrics for stuck workflow detection\n\n    **Use Cases:**\n    - Dashboard progress monitoring\n    - Detecting stuck workflows\n    - Understanding workflow performance\n\n    v2.60.0: Added for campaign workflow monitoring feature.","operationId":"get_workflow_progress_api_v1_jobs_spiderMaps_campaigns__campaign_id__workflow_progress_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowProgressResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/abort-workflow":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Abort all running workflows for a campaign","description":"Abort all running Inngest workflow runs for a campaign.\n\n    **What happens:**\n    1. Campaign workflow_status is set to 'aborted'\n    2. All running/queued orchestrated_campaign_runs are marked as aborted\n    3. Submitted campaign_locations are marked as skipped\n    4. Running workflow steps will check for abort and stop early\n\n    **Use Cases:**\n    - Emergency stop when workflows are stuck\n    - Cancel long-running campaigns\n    - Clear stuck workflow runs before retry\n\n    **Note:** This does NOT stop already-running worker jobs (SpiderMaps, SpiderSite, etc).\n    It only prevents new workflow steps from starting.\n\n    v2.60.0: Added for campaign workflow safeguards.","operationId":"abort_workflow_api_v1_jobs_spiderMaps_campaigns__campaign_id__abort_workflow_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"reason","in":"query","required":false,"schema":{"type":"string","description":"Reason for abort","default":"User requested abort","title":"Reason"},"description":"Reason for abort"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/stop":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Stop a campaign","description":"Stop an active campaign.\n\n    Stopped campaigns can be resumed later with `/continue`.\n    Jobs already submitted will continue processing.","operationId":"stop_campaign_api_v1_jobs_spiderMaps_campaigns__campaign_id__stop_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/continue":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Continue a stopped campaign","description":"Resume a stopped or paused campaign.\n\n    Campaign will continue from where it left off.","operationId":"continue_campaign_api_v1_jobs_spiderMaps_campaigns__campaign_id__continue_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/locations/{location_id}/retry":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Retry a campaign location","description":"Re-run a failed or completed location (dispatches worker jobs again).\n\n    - `retry_mode=\"full\"`: re-run the whole workflow from SpiderMaps\n    - `retry_mode=\"site\"`: keep Maps results, re-run SpiderSite + SpiderVerify\n    - `retry_mode=\"verify\"`: keep Site results, re-run SpiderVerify only\n\n    `location_id` is the `id` returned by `GET /{campaign_id}/jobs`\n    (campaign_locations.id). Max 3 retries per location — returns 400 once\n    that limit is hit.","operationId":"retry_campaign_location_api_v1_jobs_spiderMaps_campaigns__campaign_id__locations__location_id__retry_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"location_id","in":"path","required":true,"schema":{"type":"integer","title":"Location Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryLocationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryLocationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/retry-failed":{"post":{"tags":["SpiderMaps Campaigns"],"summary":"Retry all failed locations","description":"Re-run every failed location in the campaign (up to `max_locations`).\n    Locations that already hit the 3-retry limit are skipped, not errored.","operationId":"retry_failed_locations_api_v1_jobs_spiderMaps_campaigns__campaign_id__retry_failed_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"max_locations","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Max locations to retry","default":10,"title":"Max Locations"},"description":"Max locations to retry"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkRetryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"List campaigns","description":"List all campaigns for the current client.\n\n    **Filter Options:**\n    - `status`: Filter by campaign status (active, paused, completed, stopped)\n    - `country_code`: Filter by country\n\n    **Query Parameters:**\n    - format: Response format (yaml, md). Default: JSON (v2.60.0)","operationId":"list_campaigns_api_v1_jobs_spiderMaps_campaigns_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(active|paused|completed|stopped)$"},{"type":"null"}],"description":"Campaign status","title":"Status"},"description":"Campaign status"},{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"description":"Country code","title":"Country Code"},"description":"Country code"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":20,"title":"Page Size"},"description":"Items per page"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: JSON)","title":"Format"},"description":"Response format: yaml or md (default: JSON)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/jobs":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"List jobs for a campaign","description":"Get a paginated list of all jobs submitted for a campaign.\n\n    **Filter Options:**\n    - `status`: Filter by campaign_location status (pending, submitted, completed, failed, skipped)\n    - `job_status`: Filter by actual job status (queued, processing, completed, failed)\n\n    **Includes:**\n    - Job IDs linked to each location\n    - Job status from both campaign_locations and jobs tables\n    - Results count and error messages\n    - Submission and completion timestamps\n\n    **Use Cases:**\n    - Monitor job progress within a campaign\n    - Identify failed jobs for retry\n    - Get job IDs for fetching detailed results via `/{campaign_id}/jobs/{job_id}/results`","operationId":"list_campaign_jobs_api_v1_jobs_spiderMaps_campaigns__campaign_id__jobs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"location_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(pending|submitted|completed|failed|skipped)$"},{"type":"null"}],"description":"Filter by campaign_location status","title":"Location Status"},"description":"Filter by campaign_location status"},{"name":"job_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(queued|processing|completed|failed|cancelled)$"},{"type":"null"}],"description":"Filter by actual job status","title":"Job Status"},"description":"Filter by actual job status"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":50,"title":"Page Size"},"description":"Items per page"},{"name":"include_summary","in":"query","required":false,"schema":{"type":"boolean","description":"Include status summary counts","default":true,"title":"Include Summary"},"description":"Include status summary counts"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignJobsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/workflow-results":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Get aggregated workflow results","description":"Get complete aggregated results from the workflow chain:\n    SpiderMaps → SpiderSite → SpiderVerify\n\n    **Returns data from all three services combined:**\n    - SpiderMaps: Business listings (name, address, phone, rating)\n    - SpiderSite: Website data (emails, social media, company info)\n    - SpiderVerify: Email verification results (validity, score)\n\n    **Note:** Only available for campaigns created with workflow configuration.","operationId":"get_workflow_results_api_v1_jobs_spiderMaps_campaigns__campaign_id__workflow_results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"include_filtered","in":"query","required":false,"schema":{"type":"boolean","description":"Include businesses that were filtered out","default":false,"title":"Include Filtered"},"description":"Include businesses that were filtered out"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowResultsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/jobs/spiderMaps/campaigns/{campaign_id}/jobs/{job_id}/results":{"get":{"tags":["SpiderMaps Campaigns"],"summary":"Wait for workflow job completion (blocking)","description":"Blocks until all workflow stages complete for a specific SpiderMaps job,\n    then returns aggregated results from SpiderMaps + SpiderSite + SpiderVerify.\n\n    **Timeouts:**\n    - SpiderSite: 5 minutes per business\n    - SpiderVerify: 2 minutes per business\n    - Maximum total wait: 10 minutes\n\n    **Partial Results:**\n    If SpiderVerify times out but SpiderSite completed, returns partial results\n    with SpiderSite data (compendium, emails_found, company_info, etc.)\n\n    **Query Parameters:**\n    - `wait=true` (default): Block until all businesses complete or timeout\n    - `wait=false`: Return current status immediately (for polling)\n\n    **Status Values:**\n    - `queued` - Waiting for workers\n    - `processing` - Currently being processed\n    - `completed` - All stages finished successfully\n    - `failed` - Processing failed\n    - `partial` - SpiderVerify timed out but SpiderSite data available","operationId":"get_workflow_job_results_api_v1_jobs_spiderMaps_campaigns__campaign_id__jobs__job_id__results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"wait","in":"query","required":false,"schema":{"type":"boolean","description":"Wait for completion (blocking). If false, returns current state immediately.","default":true,"title":"Wait"},"description":"Wait for completion (blocking). If false, returns current state immediately."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowJobResultsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/types":{"get":{"tags":["workflows"],"summary":"List Workflow Types","description":"List available workflow types.\n\nReturns all workflow configurations that can be used for campaigns.\nEach workflow type has different stages and capabilities.","operationId":"list_workflow_types_api_v1_workflows_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowTypesResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/workflows/{job_id}/status":{"get":{"tags":["workflows"],"summary":"Get Workflow Status","description":"Get the status of a workflow job.\n\nArgs:\n    job_id: WindMill job UUID\n\nReturns:\n    Workflow job status including current step and progress.","operationId":"get_workflow_status_api_v1_workflows__job_id__status_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/{job_id}/result":{"get":{"tags":["workflows"],"summary":"Get Workflow Result","description":"Get the result of a completed workflow job.\n\nArgs:\n    job_id: WindMill job UUID\n\nReturns:\n    Workflow job result including aggregated data.","operationId":"get_workflow_result_api_v1_workflows__job_id__result_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowResultResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/selection":{"get":{"tags":["workflows"],"summary":"Get Workflow Selection Info","description":"Get which workflow would be selected based on configuration.\n\nThis endpoint helps users understand which workflow will be used\nfor their campaign configuration.\n\nArgs:\n    spidersite_enabled: Whether website crawling is enabled\n    spiderverify_enabled: Whether email verification is enabled\n\nReturns:\n    Selected workflow information and reasoning.","operationId":"get_workflow_selection_info_api_v1_workflows_selection_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"spidersite_enabled","in":"query","required":false,"schema":{"type":"boolean","description":"Whether SpiderSite is enabled","default":true,"title":"Spidersite Enabled"},"description":"Whether SpiderSite is enabled"},{"name":"spiderverify_enabled","in":"query","required":false,"schema":{"type":"boolean","description":"Whether SpiderVerify is enabled","default":true,"title":"Spiderverify Enabled"},"description":"Whether SpiderVerify is enabled"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Workflow Selection Info Api V1 Workflows Selection Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lead-search":{"post":{"tags":["Lead Search"],"summary":"Submit a single-location lead search","description":"Run the full lead generation pipeline for a single search query. SpiderMaps → SpiderSite → SpiderVerify → VayaPin. Returns a `job_id` — poll `/api/v1/jobs/{job_id}/results` for the aggregated pipeline output. Unlike campaigns (which fan out across a country), this triggers one WindMill flow for one location. Disable pipeline stages via the `workflow` field in the request body.","operationId":"submit_lead_search_api_v1_lead_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadSearchRequest"}}},"required":true},"responses":{"201":{"description":"Job created and queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadSearchResponse"},"example":{"job_id":"660e8400-e29b-41d4-a716-446655440000","type":"spiderMaps","status":"queued","created_at":"2025-10-25T10:30:00Z","from_cache":false,"message":"Job submitted successfully and queued for processing"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"examples":{"missing_credentials":{"summary":"Missing authentication","value":{"detail":"Missing authentication credentials"}},"invalid_format":{"summary":"Invalid token format","value":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}},"invalid_credentials":{"summary":"Invalid credentials","value":{"detail":"Invalid client credentials"}}}}}},"403":{"description":"Client account is inactive","content":{"application/json":{"example":{"detail":"Client account is inactive"}}}},"422":{"description":"Validation error - Invalid payload","content":{"application/json":{"example":{"detail":"Validation error in request payload"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Internal server error"}}}},"503":{"description":"Queue service unavailable","content":{"application/json":{"example":{"detail":"Failed to queue job. Please try again later."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/company-intel":{"post":{"tags":["Company Intel"],"summary":"Submit single company intel request","description":"Research a single company through the full intelligence pipeline.\nPerplexity → SpiderSite → CompanyData → Verify → People.\nPoll GET /api/v1/jobs/{job_id}/results for results.","operationId":"submit_company_intel_api_v1_company_intel_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntelRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/company-intel/batch":{"post":{"tags":["Company Intel"],"summary":"Submit batch company intel request","description":"Research multiple companies in parallel (up to 50).\nPoll GET /api/v1/jobs/{job_id}/results for aggregated results.","operationId":"submit_company_intel_batch_api_v1_company_intel_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntelBatchRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyIntelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/flows":{"get":{"tags":["Flows"],"summary":"List Flows","description":"List flows in the marketplace. Filters: category, search (name+description).","operationId":"list_flows_api_v1_flows_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Category"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Search"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FlowSummary"},"title":"Response List Flows Api V1 Flows Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/flows/{slug}":{"get":{"tags":["Flows"],"summary":"Get Flow","operationId":"get_flow_api_v1_flows__slug__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/flows/{slug}/run":{"post":{"tags":["Flows"],"summary":"Run Flow","description":"Dispatch a flow run. Body shape determines mode (see FlowRunRequest).","operationId":"run_flow_api_v1_flows__slug__run_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRunRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/runs":{"get":{"tags":["Flow Runs"],"summary":"List Runs","operationId":"list_runs_api_v1_runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"flow","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filter by flow slug","title":"Flow"},"description":"Filter by flow slug"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Status"}},{"name":"mode","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"description":"single|batch|campaign","title":"Mode"},"description":"single|batch|campaign"},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filter to runs whose payload._campaign_id matches","title":"Campaign Id"},"description":"Filter to runs whose payload._campaign_id matches"},{"name":"batch_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filter to runs whose payload._batch_id matches","title":"Batch Id"},"description":"Filter to runs whose payload._batch_id matches"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RunRecord"},"title":"Response List Runs Api V1 Runs Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/runs/{run_id}":{"get":{"tags":["Flow Runs"],"summary":"Get Run","operationId":"get_run_api_v1_runs__run_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/runs/{run_id}/cancel":{"post":{"tags":["Flow Runs"],"summary":"Cancel Run","description":"Cancel a queued or processing run owned by the caller.","operationId":"cancel_run_api_v1_runs__run_id__cancel_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/run-groups/{group_id}":{"get":{"tags":["Flow Run Groups"],"summary":"Get Run Group","description":"Aggregate view for a batch (or, when wired, campaign) group.\n\nPhase 1 only resolves batch groups (jobs sharing payload._batch_id).\nCampaign groups will resolve from scraping_campaigns when campaign-mode\ndispatch is wired.","operationId":"get_run_group_api_v1_run_groups__group_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunGroupDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/flows":{"get":{"tags":["Dashboard Flows"],"summary":"List Flows","operationId":"list_flows_api_v1_dashboard_flows_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Category"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Search"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardFlowsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/flows/{slug}":{"get":{"tags":["Dashboard Flows"],"summary":"Get Flow","operationId":"get_flow_api_v1_dashboard_flows__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/flows/{slug}/analytics":{"get":{"tags":["Dashboard Flows"],"summary":"Get Flow Analytics","description":"Per-flow + per-client analytics: KPIs, time series, status breakdown,\ntop scenarios, recent failures.\n\nTwo paths:\n\n1. **Single-service flows** (siteScraper, mapsSearch, emailVerify, …) —\n   aggregate from `public.jobs` filtered by `type = flow.dispatch_type`.\n   One job row per run.\n\n2. **Pipeline flows** (localSeo / leadSearch / companyIntel /\n   linkedinExtract) — aggregate from `public.scraping_campaigns`. The\n   campaign row is the unit of work; sub-jobs (one per location × stage)\n   link back via campaign_locations. Pre-`wave3-sweep` campaigns lack\n   the `_flow_dispatch_type` tag so a workflow_config heuristic\n   backfills the membership (see `_PIPELINE_LEGACY_HEURISTIC`).\n\nCost (§C8) is not yet wired; `kpis.total_cost_cents` is always None today.","operationId":"get_flow_analytics_api_v1_dashboard_flows__slug__analytics_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"range","in":"query","required":false,"schema":{"enum":["7d","30d","90d"],"type":"string","default":"30d","title":"Range"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowAnalyticsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/flows/{slug}/run":{"post":{"tags":["Dashboard Flows"],"summary":"Run Flow","description":"Dispatch a flow run from the dashboard. Same behavior as the public\n/api/v1/flows/{slug}/run endpoint — see api/v1/flows.py for the body\ncontract and mode rules.","operationId":"run_flow_api_v1_dashboard_flows__slug__run_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRunRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/runs":{"get":{"tags":["Dashboard Flow Runs"],"summary":"List Runs","operationId":"list_runs_api_v1_dashboard_runs_get","parameters":[{"name":"flow","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Flow"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Status"}},{"name":"mode","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Mode"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Campaign Id"}},{"name":"batch_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Batch Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRunsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/runs/{run_id}":{"get":{"tags":["Dashboard Flow Runs"],"summary":"Get Run","operationId":"get_run_api_v1_dashboard_runs__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Dashboard Flow Runs"],"summary":"Delete Run","description":"Soft-delete a run from the user's view.\n\nSets ``jobs.hidden_at = NOW()`` so the row vanishes from list_runs\nand get_run returns 410 Gone. Hard delete is not used because the\ncascade through ``campaign_workflow_jobs`` (3 FK columns ON DELETE\nCASCADE) would mangle finished campaigns and orphan rows in\n``public.results`` / ``crm_sync_log`` (no FK guards). Soft-hide\nkeeps audit trails and analytics intact while honoring \"remove\nfrom my list\" UX.\n\nConstraints:\n  - Only terminal-status runs (succeeded / failed / cancelled) can\n    be hidden — in-flight runs (queued / processing) return 422 so\n    the user is steered to ``/cancel`` first.\n  - Tenant-scoped via ``client_id`` from the cookie auth context.\n  - Idempotent at the row level: hiding an already-hidden row is a\n    no-op (rowcount=0 → 404 like any other not-found run).","operationId":"delete_run_api_v1_dashboard_runs__run_id__delete","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/runs/{run_id}/cancel":{"post":{"tags":["Dashboard Flow Runs"],"summary":"Cancel Run","operationId":"cancel_run_api_v1_dashboard_runs__run_id__cancel_post","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/run-groups/{group_id}":{"get":{"tags":["Dashboard Flow Run Groups"],"summary":"Get Run Group","operationId":"get_run_group_api_v1_dashboard_run_groups__group_id__get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunGroupDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/leads":{"get":{"tags":["Leads"],"summary":"List Leads","description":"List leads with pagination, search, and filters.","operationId":"list_leads_api_v1_leads_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Search"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(campaign|company_intel)$"},{"type":"null"}],"title":"Source"}},{"name":"has_email","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Email"}},{"name":"has_verified_email","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Verified Email"}},{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Country Code"}},{"name":"workflow_stage","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workflow Stage"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","pattern":"^(asc|desc)$","default":"desc","title":"Sort Dir"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/leads/stats":{"get":{"tags":["Leads"],"summary":"Lead Stats","description":"KPI counts.","operationId":"lead_stats_api_v1_leads_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/leads/export":{"get":{"tags":["Leads"],"summary":"Export Leads","description":"Export leads as CSV or JSON.","operationId":"export_leads_api_v1_leads_export_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(csv|json)$","default":"csv","title":"Format"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(campaign|company_intel)$"},{"type":"null"}],"title":"Source"}},{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Country Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/leads/{lead_id}":{"get":{"tags":["Leads"],"summary":"Get Lead","description":"Full lead detail.","operationId":"get_lead_api_v1_leads__lead_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"lead_id","in":"path","required":true,"schema":{"type":"integer","title":"Lead Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/fuzziq/check":{"post":{"tags":["FuzzIQ Deduplication"],"summary":"Check single record for duplicates","description":"Check if a single record is a duplicate. Optionally add to canonical if unique.","operationId":"check_single_api_v1_fuzziq_check_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckSingleRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckSingleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/fuzziq/check-batch":{"post":{"tags":["FuzzIQ Deduplication"],"summary":"Check batch of records for duplicates","description":"Check multiple records (up to 100) for duplicates in a single request.","operationId":"check_batch_api_v1_fuzziq_check_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckBatchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckBatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/fuzziq/canonical":{"get":{"tags":["FuzzIQ Deduplication"],"summary":"List canonical records","description":"List canonical records for the authenticated client with optional filtering.","operationId":"list_canonical_api_v1_fuzziq_canonical_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"record_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Record Type"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanonicalListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["FuzzIQ Deduplication"],"summary":"Add record to canonical","description":"Manually add a record to the canonical database.","operationId":"add_canonical_api_v1_fuzziq_canonical_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanonicalAddRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanonicalAddResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/fuzziq/canonical/import":{"post":{"tags":["FuzzIQ Deduplication"],"summary":"Bulk import records","description":"Import multiple records to the canonical database (up to 1000).","operationId":"import_canonical_api_v1_fuzziq_canonical_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanonicalImportRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanonicalImportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/fuzziq/canonical/stats":{"get":{"tags":["FuzzIQ Deduplication"],"summary":"Get FuzzIQ statistics","description":"Get deduplication statistics for the authenticated client.","operationId":"get_stats_api_v1_fuzziq_canonical_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FuzzIQStatsResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/events/stream":{"get":{"tags":["Events"],"summary":"Stream Events","description":"Server-Sent Events stream for real-time job monitoring.\n\nConnect to this endpoint to receive live job events for your client account.\nEvents are streamed as they happen - no polling required.\n\n**Authentication:**\nPass your credentials as a query parameter:\n```\nGET /api/v1/events/stream?token=client_id:api_key:api_secret\n```\n\n**Optional server-side filtering:**\nAppend `?campaign_id=<id>` or `?job_id=<id>` to receive only the matching\nevents (plus the `connected` handshake and `heartbeat` keep-alives). With\nno filter you get the full client-scoped firehose (unchanged, default).\nFilters are a union — supplying both shows events matching either id.\n```\nGET /api/v1/events/stream?token=...&campaign_id=camp_abc\nGET /api/v1/events/stream?token=...&job_id=550e8400-...\n```\n\n**Event Types:**\n- `connected`: Sent when connection is established\n- `job.queued`: Job submitted and queued for processing\n- `job.started`: Worker picked up the job\n- `job.completed`: Job finished successfully\n- `job.failed`: Job encountered an error\n- `campaign.terminal`: A campaign reached a terminal state (the definitive\n  \"done\" signal — carries the chosen status + success_pct)\n- `heartbeat`: Keep-alive signal (every 30 seconds)\n\n**Event Format (SSE):**\n```\nevent: job.completed\ndata: {\"job_id\": \"abc-123\", \"processing_time\": 45.2, \"results_count\": 15}\n\n```\n\n**JavaScript Example:**\n```javascript\nconst token = 'cli_xxx:sk_xxx:secret_xxx';\nconst eventSource = new EventSource(\n  `https://spideriq.ai/api/v1/events/stream?token=${token}`\n);\n\neventSource.addEventListener('job.completed', (e) => {\n  const data = JSON.parse(e.data);\n  console.log('Job completed:', data.job_id);\n});\n```\n\nReturns:\n    SSE stream of job events","operationId":"stream_events_api_v1_events_stream_get","parameters":[{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}],"description":"Optional. Deliver only events for this campaign (plus connected/heartbeat). Note: per-job events carry no campaign id — use job_id to watch a single job.","title":"Campaign Id"},"description":"Optional. Deliver only events for this campaign (plus connected/heartbeat). Note: per-job events carry no campaign id — use job_id to watch a single job."},{"name":"job_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}],"description":"Optional. Deliver only events for this job (plus connected/heartbeat).","title":"Job Id"},"description":"Optional. Deliver only events for this job (plus connected/heartbeat)."},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Auth token (client_id:api_key:api_secret)","title":"Token"},"description":"Auth token (client_id:api_key:api_secret)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/events/status":{"get":{"tags":["Events"],"summary":"Get Event Status","description":"Get event service status.\n\nReturns:\n    Event service health and active subscription count","operationId":"get_event_status_api_v1_events_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/dashboard/events/stream":{"get":{"tags":["Dashboard Events"],"summary":"Dashboard Events Stream","description":"Server-Sent Events stream for the Live Activity Theater.\n\nConnect from the dashboard via ``new EventSource('/api/v1/dashboard/events/stream',\n{ withCredentials: true })`` — Better Auth session cookie\nauthenticates; X-Selected-Client-Id (set by api-client.ts on\nsuper_admin impersonation) determines the channel.\n\nSubscribed channel is ``events:{client_id}``; same Redis pubsub\nchannel as the public Bearer endpoint at ``/api/v1/events/stream``\nso any ``event_service.publish_event(client_id, …)`` call reaches\nboth consumer types.\n\nEvent types streamed (live as published — not exhaustive, evolves\nwith backend hooks):\n  - job.queued / job.started / job.completed / job.failed\n  - campaign.created / campaign.location.completed /\n    campaign.business.found / campaign.email.verified /\n    campaign.vayapin.exported / campaign.completed\n  - playbook.company.completed\n  - spiderSite.scraped / spiderPeople.enriched /\n    spiderCompanyData.fetched / spiderPublic.{instagram,linkedin,facebook}.crawled\n  - resources.changed / resource.flagged\n  - booking.created / booking.confirmed / booking.cancelled\n  - content.page.published / content.post.published /\n    content.site.deployed / content.template.applied\n  - mail.outreach.reply / mail.security.flag\n  - gate.key.created / gate.rate_limit.hit\n  - heartbeat (every 30s — keep-alive)\n\nThe frontend hook (apps/web/src/hooks/useLiveEventStream.ts) parses\neach event type against a Zod schema in apps/web/src/routes/client/live/cards/types.ts\nand dispatches it to the appropriate card component. Unknown event\ntypes fall back to a generic JsonCard renderer — the page never\nbreaks when the backend adds new types.","operationId":"dashboard_events_stream_api_v1_dashboard_events_stream_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/proxy/request":{"post":{"tags":["Proxy"],"summary":"Get a proxy from the pool","description":"Request a proxy from the pool for making requests.\n\n    **Selection Logic:**\n    1. If `sticky_session_id` is provided, returns the same proxy if still valid\n    2. Filters by `country_code` or `location_id` if specified\n    3. Excludes any `exclude_modem_ids`\n    4. Selects from healthy modems using round-robin\n\n    **Example:**\n    ```python\n    response = requests.post(\n        \"/api/v1/proxy/request\",\n        json={\"country_code\": \"UA\"},\n        headers={\"Authorization\": f\"Bearer {token}\"}\n    )\n    proxy_url = response.json()[\"proxy_url\"]\n    ```","operationId":"request_proxy_api_v1_proxy_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/proxy/pool/roundrobin":{"get":{"tags":["Proxy"],"summary":"Get next proxy (round-robin)","description":"Simple round-robin proxy selection.\n    Each call returns the next available proxy in rotation.\n\n    Optionally filter by country code.\n\n    **Like Proxidize's Proxy Pooling feature.**","operationId":"get_proxy_roundrobin_api_v1_proxy_pool_roundrobin_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"description":"Filter by country","title":"Country Code"},"description":"Filter by country"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/pool":{"get":{"tags":["Proxy"],"summary":"List available proxies","description":"List all available proxies in the pool.\n\n    Similar to Proxidize's `/api/getinfo` endpoint.","operationId":"list_pool_api_v1_proxy_pool_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"title":"Country Code"}},{"name":"location_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Location Id"}},{"name":"healthy_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only return healthy modems","default":true,"title":"Healthy Only"},"description":"Only return healthy modems"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProxyModemBrief"},"title":"Response List Pool Api V1 Proxy Pool Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/stats":{"get":{"tags":["Proxy"],"summary":"Get pool statistics","description":"Get aggregate statistics about the proxy pool.","operationId":"get_pool_stats_api_v1_proxy_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyPoolStats"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/proxy/rotate":{"post":{"tags":["Proxy"],"summary":"Request IP rotation","description":"Request IP rotation for a specific modem.\n\n    The rotation is queued and executed by the agent.\n    Returns immediately with the command status.\n\n    **Rotation Methods:**\n    - `reconnect`: Disconnect and reconnect (fastest, ~5-15 seconds)\n    - `airplane`: Toggle airplane mode (~10-20 seconds)\n    - `reboot`: Full modem reboot (slowest, ~30-60 seconds)","operationId":"rotate_ip_api_v1_proxy_rotate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/proxy/rotate-all":{"post":{"tags":["Proxy"],"summary":"Rotate all modems","description":"Request IP rotation for all online modems.\n\n    Optionally filter by location.\n\n    Similar to Proxidize's `/api/rotate` endpoint.","operationId":"rotate_all_api_v1_proxy_rotate_all_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filter by location","title":"Location Id"},"description":"Filter by location"},{"name":"method","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/RotationMethod"}],"default":"reconnect","title":"Method"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Rotate All Api V1 Proxy Rotate All Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/report":{"post":{"tags":["Proxy"],"summary":"Report proxy failure","description":"Report a proxy failure.\n\n    After 3 failures within 5 minutes, the modem is marked as unhealthy.\n    Call this when a proxy request fails (timeout, blocked, etc.).","operationId":"report_failure_api_v1_proxy_report_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"modem_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Modem Id"}},{"name":"error","in":"query","required":true,"schema":{"type":"string","maxLength":500,"description":"Error description","title":"Error"},"description":"Error description"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Failure Api V1 Proxy Report Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/report-success":{"post":{"tags":["Proxy"],"summary":"Report proxy success","description":"Report a successful proxy request. Resets failure counter.","operationId":"report_success_api_v1_proxy_report_success_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"modem_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Modem Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Success Api V1 Proxy Report Success Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/agents/heartbeat":{"post":{"tags":["Proxy Agents"],"summary":"Agent heartbeat","description":"Submit agent heartbeat with modem statuses.\n\n    **Should be called every 30 seconds.**\n\n    The response includes:\n    - Pending commands to execute\n    - Config updates (if any)\n\n    **Headers required:**\n    - `X-Location-Code`: Location code (e.g., 'ua-odessa-1')\n    - `X-Agent-Key`: Agent API key","operationId":"agent_heartbeat_api_v1_proxy_agents_heartbeat_post","parameters":[{"name":"x-location-code","in":"header","required":true,"schema":{"type":"string","description":"Location code","title":"X-Location-Code"},"description":"Location code"},{"name":"x-agent-key","in":"header","required":true,"schema":{"type":"string","description":"Agent API key","title":"X-Agent-Key"},"description":"Agent API key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentHeartbeat"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentHeartbeatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/agents/commands/{command_id}/complete":{"post":{"tags":["Proxy Agents"],"summary":"Complete a command","description":"Report command completion.\n\n    Call this after executing a command received in the heartbeat response.\n\n    **Headers required:**\n    - `X-Location-Code`: Location code\n    - `X-Agent-Key`: Agent API key","operationId":"complete_command_api_v1_proxy_agents_commands__command_id__complete_post","parameters":[{"name":"command_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Command Id"}},{"name":"x-location-code","in":"header","required":true,"schema":{"type":"string","description":"Location code","title":"X-Location-Code"},"description":"Location code"},{"name":"x-agent-key","in":"header","required":true,"schema":{"type":"string","description":"Agent API key","title":"X-Agent-Key"},"description":"Agent API key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandComplete"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Complete Command Api V1 Proxy Agents Commands  Command Id  Complete Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/agents/modems/register":{"post":{"tags":["Proxy Agents"],"summary":"Register a new modem","description":"Register a new modem discovered by the agent.\n\n    Called when a new USB modem is detected.\n\n    **Headers required:**\n    - `X-Location-Code`: Location code\n    - `X-Agent-Key`: Agent API key","operationId":"register_modem_api_v1_proxy_agents_modems_register_post","parameters":[{"name":"imei","in":"query","required":true,"schema":{"type":"string","minLength":14,"maxLength":20,"title":"Imei"}},{"name":"proxy_port","in":"query","required":true,"schema":{"type":"integer","maximum":65535,"minimum":3001,"title":"Proxy Port"}},{"name":"iccid","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":22},{"type":"null"}],"title":"Iccid"}},{"name":"phone_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Phone Number"}},{"name":"carrier","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Carrier"}},{"name":"usb_port","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Usb Port"}},{"name":"x-location-code","in":"header","required":true,"schema":{"type":"string","description":"Location code","title":"X-Location-Code"},"description":"Location code"},{"name":"x-agent-key","in":"header","required":true,"schema":{"type":"string","description":"Agent API key","title":"X-Agent-Key"},"description":"Agent API key"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Register Modem Api V1 Proxy Agents Modems Register Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/proxy/agents/sms/log":{"post":{"tags":["Proxy Agents"],"summary":"Log an SMS message","description":"Log an incoming or outgoing SMS message.\n\n    **Headers required:**\n    - `X-Location-Code`: Location code\n    - `X-Agent-Key`: Agent API key","operationId":"log_sms_api_v1_proxy_agents_sms_log_post","parameters":[{"name":"modem_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Modem Id"}},{"name":"direction","in":"query","required":true,"schema":{"type":"string","pattern":"^(incoming|outgoing)$","title":"Direction"}},{"name":"message","in":"query","required":true,"schema":{"type":"string","maxLength":500,"title":"Message"}},{"name":"phone_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Phone Number"}},{"name":"sms_id_on_device","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sms Id On Device"}},{"name":"x-location-code","in":"header","required":true,"schema":{"type":"string","description":"Location code","title":"X-Location-Code"},"description":"Location code"},{"name":"x-agent-key","in":"header","required":true,"schema":{"type":"string","description":"Agent API key","title":"X-Agent-Key"},"description":"Agent API key"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Log Sms Api V1 Proxy Agents Sms Log Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/catalog/assets":{"get":{"tags":["Media Library"],"summary":"List Catalog Assets","description":"List the tenant's media assets, newest first (soft-delete aware).\n\nFilters are AND-combined. The effective ``client_id`` is resolved by\n``require_content_scoped_user_or_api_client`` (session user or API client;\nheader/brand/super-admin selection). Use ``?format=yaml`` or ``?format=md``\nfor agent-friendly responses.","operationId":"list_catalog_assets_api_v1_dashboard_media_catalog_assets_get","parameters":[{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(image|video|doc)$"},{"type":"null"}],"description":"Filter by asset class: image | video | doc","title":"Kind"},"description":"Filter by asset class: image | video | doc"},{"name":"folder","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Filter by exact DAM folder path","title":"Folder"},"description":"Filter by exact DAM folder path"},{"name":"tags","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter to assets carrying ALL given tags","title":"Tags"},"description":"Filter to assets carrying ALL given tags"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(pending|processing|ready|failed)$"},{"type":"null"}],"description":"Filter by status: pending | processing | ready | failed","title":"Status"},"description":"Filter by status: pending | processing | ready | failed"},{"name":"storage_tier","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(seaweedfs|r2|peertube)$"},{"type":"null"}],"description":"Filter by storage tier: seaweedfs | r2 | peertube","title":"Storage Tier"},"description":"Filter by storage tier: seaweedfs | r2 | peertube"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Page size (1-500)","default":50,"title":"Limit"},"description":"Page size (1-500)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset","default":0,"title":"Offset"},"description":"Pagination offset"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/catalog/counts":{"get":{"tags":["Media Library"],"summary":"Count Catalog Assets","description":"Aggregate counts over the tenant's WHOLE catalog (soft-delete aware).\n\nReturns ``{total, by_kind, by_tier}`` — the TRUE totals the Library's filter\nchips need (FX2a). The dashboard previously computed chip counts client-side\nover a newest-200 page, so anything past the first page was uncounted (the\n\"Images 0\" bug when the newest 200 rows were all videos). This server-side\nsurface counts every matching row in one GROUPING SETS pass.\n\nFilters are AND-combined and mirror the list endpoint exactly, so the counts\nhonour the same filter the grid is paging under. The effective ``client_id``\nis resolved by ``require_content_scoped_user_or_api_client``.","operationId":"count_catalog_assets_api_v1_dashboard_media_catalog_counts_get","parameters":[{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(image|video|doc)$"},{"type":"null"}],"description":"Filter by asset class: image | video | doc","title":"Kind"},"description":"Filter by asset class: image | video | doc"},{"name":"folder","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"Filter by exact DAM folder path","title":"Folder"},"description":"Filter by exact DAM folder path"},{"name":"tags","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter to assets carrying ALL given tags","title":"Tags"},"description":"Filter to assets carrying ALL given tags"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(pending|processing|ready|failed)$"},{"type":"null"}],"description":"Filter by status: pending | processing | ready | failed","title":"Status"},"description":"Filter by status: pending | processing | ready | failed"},{"name":"storage_tier","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(seaweedfs|r2|peertube)$"},{"type":"null"}],"description":"Filter by storage tier: seaweedfs | r2 | peertube","title":"Storage Tier"},"description":"Filter by storage tier: seaweedfs | r2 | peertube"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetCountsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/catalog/search":{"get":{"tags":["Media Library"],"summary":"Search Catalog Assets","description":"Search the tenant's media assets.\n\n``q`` substring-matches key/folder; ``tags`` overlaps (ANY); ``kind``\nnarrows by type. All optional + AND-combined, newest first. Mirrors the 1.1\nsearch surface (metadata containment stays service-only — it needs a typed\nJSON body; ``q`` + ``tags`` + ``kind`` cover the dashboard discovery path).","operationId":"search_catalog_assets_api_v1_dashboard_media_catalog_search_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Case-insensitive substring match on key/folder","title":"Q"},"description":"Case-insensitive substring match on key/folder"},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(image|video|doc)$"},{"type":"null"}],"description":"Filter by asset class: image | video | doc","title":"Kind"},"description":"Filter by asset class: image | video | doc"},{"name":"tags","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter to assets sharing ANY given tag","title":"Tags"},"description":"Filter to assets sharing ANY given tag"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Page size (1-500)","default":50,"title":"Limit"},"description":"Page size (1-500)"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/catalog/assets/{asset_id}":{"get":{"tags":["Media Library"],"summary":"Get Catalog Asset","description":"Fetch a single media asset by id. 404 if it does not exist for the tenant.","operationId":"get_catalog_asset_api_v1_dashboard_media_catalog_assets__asset_id__get","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Media Library"],"summary":"Update Catalog Asset","description":"Patch a media asset's mutable fields (folder / tags / status / metadata).\n\nTrue PATCH — only the fields present in the body are written. ``alt_text``\nand ``metadata`` both fold into a shallow JSONB merge (editing alt-text\npreserves ``content_hash`` etc.). 200 + the updated asset; 404 if no live\nasset with that id exists for the tenant.","operationId":"update_catalog_asset_api_v1_dashboard_media_catalog_assets__asset_id__patch","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Media Library"],"summary":"Delete Catalog Asset","description":"Soft-delete a media asset (``deleted_at = now()``). 204 on success, 404 if\nno live asset with that id exists.\n\nFor an R2 image (``storage_tier='r2'``) the delete is COMPLETE, not just\nobservational: an R2 image is dual-written (R2 object + legacy\n``content_media`` row + this catalog row, all sharing one ``r2_key``), so\ndeleting only the catalog row would orphan the other two — leaving the image\nvisible in the legacy page-image picker and the object billing in R2. We\nmirror the create and clear the matching ``content_media`` row + R2 object\n(best-effort — a cleanup miss never fails the delete; the catalog row is\nalready hidden). SeaweedFS/PeerTube tiers keep the observational-only\nbehaviour (their bytes live on LLM11, which app/ never mutates — Rule 3).","operationId":"delete_catalog_asset_api_v1_dashboard_media_catalog_assets__asset_id__delete","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/catalog/assets/{asset_id}/derivative":{"get":{"tags":["Media Library"],"summary":"Get Asset Derivative","description":"Resolve (or lazily create + cache) an image derivative for this asset.\n\nOn the first request for a given spec the image is derived with Pillow and\nstored in R2 under a deterministic ``derivatives/{tenant}/{hash}.{ext}`` key;\nsubsequent identical requests return the cached URL with ``cache_hit=true`` and\nno work. **422** for a non-image asset or one not on the r2 tier\n(``unsupported_source``); **404** if the asset doesn't exist for the tenant.\nDerivatives are an evictable cache regenerable from the original (Rule 7).","operationId":"get_asset_derivative_api_v1_dashboard_media_catalog_assets__asset_id__derivative_get","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"w","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4096,"minimum":1},{"type":"null"}],"description":"Target width in px (1-4096)","title":"W"},"description":"Target width in px (1-4096)"},{"name":"h","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":4096,"minimum":1},{"type":"null"}],"description":"Target height in px (1-4096)","title":"H"},"description":"Target height in px (1-4096)"},{"name":"fit","in":"query","required":false,"schema":{"type":"string","pattern":"^(cover|contain|fill|crop16x9|crop1x1|crop4x3)$","description":"cover | contain | fill | crop16x9 | crop1x1 | crop4x3","default":"cover","title":"Fit"},"description":"cover | contain | fill | crop16x9 | crop1x1 | crop4x3"},{"name":"fmt","in":"query","required":false,"schema":{"type":"string","pattern":"^(webp|avif|jpeg|png|auto)$","description":"webp | avif | jpeg | png | auto","default":"webp","title":"Fmt"},"description":"webp | avif | jpeg | png | auto"},{"name":"q","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Encode quality 1-100 (png ignores it)","default":82,"title":"Q"},"description":"Encode quality 1-100 (png ignores it)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaDerivativeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/analytics/overview":{"get":{"tags":["Media Analytics"],"summary":"Media Analytics Overview","description":"Library analytics rollup: video totals + most-viewed assets + the catalog\nkind/tier breakdown, joined to PeerTube views/viewers via one batched call.\n\nIf PeerTube is unreachable the catalog data still returns with\n``total_views`` null and ``source=\"unavailable\"`` — never a 500.","operationId":"media_analytics_overview_api_v1_dashboard_media_analytics_overview_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAnalyticsOverview"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/analytics/assets/{asset_id}":{"get":{"tags":["Media Analytics"],"summary":"Media Asset Analytics","description":"Per-asset engagement. 404 if the asset doesn't exist for the tenant.\n\nVideo assets carry PeerTube views/viewers (``source=\"peertube\"``, or\n``\"unavailable\"`` if the fetch failed — views null, never a 500). Non-video\nassets return ``engagement_available=false`` + ``source=\"not_applicable\"``\n(graceful — analytics is video-centric for now), NOT a 404/500.","operationId":"media_asset_analytics_api_v1_dashboard_media_analytics_assets__asset_id__get","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetAnalytics"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/analytics/assets/{asset_id}/trend":{"get":{"tags":["Media Analytics"],"summary":"Media Asset Trend","description":"Per-asset view/viewer TREND over time, from the daily snapshot rollup\n(``public.media_stats_daily``). 404 if the asset doesn't exist for the tenant.\n\n``points`` is the ``[{date, views, viewers}]`` series ascending by date. It's\n``[]`` for a non-video asset, a video never snapshotted, or a brand-new video\non day-0 — trends accrue FORWARD (PeerTube has no historical per-day series),\nso day-1 is a single point and fills in nightly. Unlike the live-PeerTube\nanalytics routes, this reads only the local rollup, so it never depends on\nPeerTube being reachable at request time.","operationId":"media_asset_trend_api_v1_dashboard_media_analytics_assets__asset_id__trend_get","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","pattern":"^(7d|30d|90d)$","description":"Window: 7d | 30d | 90d","default":"30d","title":"Range"},"description":"Window: 7d | 30d | 90d"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetTrendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/analytics/assets/{asset_id}/watchtime":{"get":{"tags":["Media Analytics"],"summary":"Media Asset Watchtime","description":"Per-asset watch-time / retention (4.3b), proxied from PeerTube's owner-stats\nvia the LLM11 import-api. 404 if the asset doesn't exist for the tenant.\n\nVideo assets carry an ``overall`` block (watch-time in seconds, viewers, peak,\ncountries) + a daily ``timeseries`` (viewers + watch-time-seconds). A non-video\nasset (or a video with no ``peertube_uuid``) returns\n``engagement_available=false`` + ``overall=null``/``timeseries=null`` (graceful\nempty-state, NOT a 404/500). **RESILIENCE:** if LLM11/PeerTube is unreachable\nthe response is ``source=\"unavailable\"`` with null blocks — NEVER a 500.","operationId":"media_asset_watchtime_api_v1_dashboard_media_analytics_assets__asset_id__watchtime_get","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default json)","title":"Format"},"description":"Response format: yaml or md (default json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAssetWatchtime"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/upload":{"post":{"tags":["Media Upload"],"summary":"Upload Media Image","description":"Upload a single image to Cloudflare R2 (``storage_tier='r2'``).\n\nValidates → transcodes to WebP (5 MB cap) → R2 → ``content_media`` row →\ncatalogs into ``media_assets``. 201 + ``{success, media, asset}`` where\n``media`` is the legacy record and ``asset`` is the catalog row (null only if\nthe non-blocking catalog write missed — the upload still succeeded).","operationId":"upload_media_image_api_v1_dashboard_media_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_image_api_v1_dashboard_media_upload_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/upload-batch":{"post":{"tags":["Media Upload"],"summary":"Upload Media Batch","description":"Upload one or many files to SeaweedFS (``storage_tier='seaweedfs'``).\n\nPer-file (20 MB / 500 MB video) + batch (500 MB) weight caps and an extension\nallowlist run BEFORE any upload — a hard violation is a 413 and nothing\nuploads. Each file catalogs on success. 201 + ``{success, uploaded, failed,\ntotals}`` (``success`` is true only when nothing failed).","operationId":"upload_media_batch_api_v1_dashboard_media_upload_batch_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_batch_api_v1_dashboard_media_upload_batch_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/media/import-video":{"post":{"tags":["Media Upload"],"summary":"Import Media Video","description":"Import a remote video into the tenant's PeerTube channel\n(``storage_tier='peertube'``).\n\nLONG-RUNNING (encode takes minutes) — this does NOT block-wait the encode.\nThe import is accepted (uuid minted, status ``processing``, catalog row\nwritten) and returns **202** immediately; the client polls the existing\nstatus path (``GET /api/v1/media/videos/{uuid}/status``) for completion.\n\n202 + ``{success, peertube_uuid, video_id, status, watch_url, embed_url,\nplayer_url, message}``. 502 if the upstream import service rejects the kick-off.","operationId":"import_media_video_api_v1_dashboard_media_import_video_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoImportRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/members":{"get":{"tags":["dashboard-members"],"summary":"List Members","operationId":"list_members_api_v1_dashboard_members_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/members/quota":{"get":{"tags":["dashboard-members"],"summary":"Get Member Quota","operationId":"get_member_quota_api_v1_dashboard_members_quota_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/members/invite":{"post":{"tags":["dashboard-members"],"summary":"Invite Member","operationId":"invite_member_api_v1_dashboard_members_invite_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteMemberRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/members/{user_id}/ban":{"post":{"tags":["dashboard-members"],"summary":"Ban Member","operationId":"ban_member_api_v1_dashboard_members__user_id__ban_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/members/{user_id}/unban":{"post":{"tags":["dashboard-members"],"summary":"Unban Member","operationId":"unban_member_api_v1_dashboard_members__user_id__unban_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnbanRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/members/{user_id}/role":{"post":{"tags":["dashboard-members"],"summary":"Set Member Role","operationId":"set_member_role_api_v1_dashboard_members__user_id__role_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/members/{user_id}":{"patch":{"tags":["dashboard-members"],"summary":"Patch Site Member","operationId":"patch_site_member_api_v1_dashboard_members__user_id__patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteMemberPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/member-groups":{"get":{"tags":["dashboard-members"],"summary":"List Groups","operationId":"list_groups_api_v1_dashboard_member_groups_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["dashboard-members"],"summary":"Create Group","operationId":"create_group_api_v1_dashboard_member_groups_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupCreateRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/member-groups/{group_id}":{"patch":{"tags":["dashboard-members"],"summary":"Update Group","operationId":"update_group_api_v1_dashboard_member_groups__group_id__patch","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Group Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dashboard-members"],"summary":"Delete Group","operationId":"delete_group_api_v1_dashboard_member_groups__group_id__delete","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Group Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/pages/{page_id}/access":{"get":{"tags":["dashboard-members"],"summary":"Get Page Access","operationId":"get_page_access_api_v1_dashboard_pages__page_id__access_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Page Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["dashboard-members"],"summary":"Set Page Access","operationId":"set_page_access_api_v1_dashboard_pages__page_id__access_put","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Page Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageAccessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/data-restrictions":{"get":{"tags":["dashboard-members"],"summary":"List Data Restrictions","operationId":"list_data_restrictions_api_v1_dashboard_data_restrictions_get","parameters":[{"name":"source_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Source Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dashboard-members"],"summary":"Create Data Restriction","operationId":"create_data_restriction_api_v1_dashboard_data_restrictions_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataRestrictionCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/data-restrictions/{restriction_id}":{"patch":{"tags":["dashboard-members"],"summary":"Update Data Restriction","operationId":"update_data_restriction_api_v1_dashboard_data_restrictions__restriction_id__patch","parameters":[{"name":"restriction_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Restriction Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataRestrictionUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dashboard-members"],"summary":"Delete Data Restriction","operationId":"delete_data_restriction_api_v1_dashboard_data_restrictions__restriction_id__delete","parameters":[{"name":"restriction_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Restriction Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/sso-config":{"get":{"tags":["dashboard-members"],"summary":"Get Sso Config","operationId":"get_sso_config_api_v1_dashboard_sso_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"put":{"tags":["dashboard-members"],"summary":"Set Sso Config","operationId":"set_sso_config_api_v1_dashboard_sso_config_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSOConfigRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/sso-providers":{"get":{"tags":["dashboard-members"],"summary":"List Sso Providers","operationId":"list_sso_providers_api_v1_dashboard_sso_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/sso-providers/{provider_id}":{"get":{"tags":["dashboard-members"],"summary":"Get Sso Provider","operationId":"get_sso_provider_api_v1_dashboard_sso_providers__provider_id__get","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"Provider Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["dashboard-members"],"summary":"Upsert Sso Provider","operationId":"upsert_sso_provider_api_v1_dashboard_sso_providers__provider_id__put","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"Provider Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSOProviderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dashboard-members"],"summary":"Delete Sso Provider","operationId":"delete_sso_provider_api_v1_dashboard_sso_providers__provider_id__delete","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"Provider Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/mailboxes":{"get":{"tags":["SpiderMail"],"summary":"List Mailboxes","description":"List all mailboxes for the authenticated client (passwords excluded).","operationId":"list_mailboxes_api_v1_mail_mailboxes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MailboxResponse"},"type":"array","title":"Response List Mailboxes Api V1 Mail Mailboxes Get"}}}}}},"post":{"tags":["SpiderMail"],"summary":"Register Mailbox","description":"Register a new email mailbox. Passwords are encrypted at rest.\n\nResource-quota check (Plans Initiative F2, migration 248): a client\nmay own at most `max_mailboxes` mailboxes. NULL = unlimited (the\ndefault for every existing client at F2 ship time). 403 returns a\nstructured `error=\"resource_quota_exceeded\"` body so dashboard /\nagent callers can surface the cap value directly.","operationId":"register_mailbox_api_v1_mail_mailboxes_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/mailboxes/stats":{"get":{"tags":["SpiderMail"],"summary":"Get Mailbox Stats","description":"Per-mailbox aggregates for the Master Inbox sidebar.\n\nAt 200 mailboxes per client the naive pattern — one COUNT query per\nmailbox from the dashboard — costs 200 round-trips per page load.\nThis endpoint returns the full stats row for every mailbox the client\nowns in a single aggregate query, plus a totals block so the sidebar\nheader can render client-wide counts without a second call.\n\nHealth rollup:\n  * `ok`        — poll ran within the last hour, no error\n  * `warning`   — last poll_error is set\n  * `stale`     — no last_poll_at (never polled) or last_poll_at > 1h ago\n  * `disabled`  — is_active = false","operationId":"get_mailbox_stats_api_v1_mail_mailboxes_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/mail/mailboxes/{email}":{"delete":{"tags":["SpiderMail"],"summary":"Delete Mailbox","description":"Delete a mailbox and all its messages.","operationId":"delete_mailbox_api_v1_mail_mailboxes__email__delete","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderMail"],"summary":"Update Mailbox","description":"Update mailbox settings including template defaults.\n\nSupports updating:\n- display_name: Display name for From header\n- is_active: Enable/disable mailbox polling\n- default_template_id: Default template for outbound emails\n- template_variables: Default variable values (merged with job-level data)","operationId":"update_mailbox_api_v1_mail_mailboxes__email__patch","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","title":"Email"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/mailboxes/{email}/test":{"post":{"tags":["SpiderMail"],"summary":"Test Mailbox","description":"Test IMAP and SMTP connectivity for a mailbox.","operationId":"test_mailbox_api_v1_mail_mailboxes__email__test_post","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxTestResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/folders":{"get":{"tags":["SpiderMail"],"summary":"Get Folders","description":"Get folder list with message counts for a mailbox.","operationId":"get_folders_api_v1_mail_folders_get","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Mailbox email address","title":"Email"},"description":"Mailbox email address"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/inbox":{"get":{"tags":["SpiderMail"],"summary":"Get Inbox","description":"Get inbox messages.\n\nThree scopes, resolved in priority order:\n    1. `?email=alice@...`     → single mailbox (legacy; byte-identical response).\n    2. `?view_id=N`           → apply saved view's filter_config from /mail/views.\n    3. neither                → Master Inbox (client-wide), streams from the\n                                denormalized idx_mail_messages_client_*_date indexes\n                                introduced in migration 139.\n\nDirect query params (unread_only, folder, direction) override the saved\nview's equivalents. Use ?format=yaml for agent-friendly output.","operationId":"get_inbox_api_v1_mail_inbox_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Mailbox email address. Omit for Master Inbox (all client mailboxes).","title":"Email"},"description":"Mailbox email address. Omit for Master Inbox (all client mailboxes)."},{"name":"view_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Apply a saved view's filter_config (see /mail/views). Direct query params override the view's defaults.","title":"View Id"},"description":"Apply a saved view's filter_config (see /mail/views). Direct query params override the view's defaults."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of messages to return","default":20,"title":"Limit"},"description":"Number of messages to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"},{"name":"unread_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only return unread messages","default":false,"title":"Unread Only"},"description":"Only return unread messages"},{"name":"folder","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Folder to filter by (INBOX, Sent, Drafts, Trash)","title":"Folder"},"description":"Folder to filter by (INBOX, Sent, Drafts, Trash)"},{"name":"direction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Direction filter: inbound or outbound","title":"Direction"},"description":"Direction filter: inbound or outbound"},{"name":"format","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/OutputFormat"}],"description":"Output format: json or yaml","default":"json","title":"Format"},"description":"Output format: json or yaml"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/messages/{message_id}":{"get":{"tags":["SpiderMail"],"summary":"Get Message","description":"Get full message by database ID. Use ?format=yaml for agent-friendly output. Also marks as read.","operationId":"get_message_api_v1_mail_messages__message_id__get","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"integer","title":"Message Id"}},{"name":"format","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/OutputFormat"}],"description":"Output format: json or yaml","default":"json","title":"Format"},"description":"Output format: json or yaml"},{"name":"include_attachments","in":"query","required":false,"schema":{"type":"boolean","description":"Include attachment summaries in response","default":true,"title":"Include Attachments"},"description":"Include attachment summaries in response"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderMail"],"summary":"Update Message Flags","description":"Update message flags (is_read, is_starred, labels).","operationId":"update_message_flags_api_v1_mail_messages__message_id__patch","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"integer","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailMessageUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailMessageFull"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/threads/{thread_id}":{"get":{"tags":["SpiderMail"],"summary":"Get Thread","description":"Get all messages in a thread. Use ?format=yaml for agent-friendly output.","operationId":"get_thread_api_v1_mail_threads__thread_id__get","parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"type":"string","title":"Thread Id"}},{"name":"format","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/OutputFormat"}],"description":"Output format: json or yaml","default":"json","title":"Format"},"description":"Output format: json or yaml"},{"name":"include_attachments","in":"query","required":false,"schema":{"type":"boolean","description":"Include attachment summaries in response","default":true,"title":"Include Attachments"},"description":"Include attachment summaries in response"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/search":{"get":{"tags":["SpiderMail"],"summary":"Search Messages","description":"Search messages using full-text search and filters.","operationId":"search_messages_api_v1_mail_search_get","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Mailbox email address","title":"Email"},"description":"Mailbox email address"},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Full-text search query","title":"Q"},"description":"Full-text search query"},{"name":"from_addr","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by sender address","title":"From Addr"},"description":"Filter by sender address"},{"name":"subject","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by subject (substring match)","title":"Subject"},"description":"Filter by subject (substring match)"},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Messages since this date (ISO 8601)","title":"Since"},"description":"Messages since this date (ISO 8601)"},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Messages before this date (ISO 8601)","title":"Before"},"description":"Messages before this date (ISO 8601)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/messages/bulk":{"post":{"tags":["SpiderMail"],"summary":"Bulk Update Messages","description":"Bulk update messages: mark_read, mark_unread, archive, delete, add_label.","operationId":"bulk_update_messages_api_v1_mail_messages_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpdateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/messages/{message_id}/snooze":{"post":{"tags":["SpiderMail"],"summary":"Snooze Message","description":"Snooze a message until a specific time.","operationId":"snooze_message_api_v1_mail_messages__message_id__snooze_post","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"integer","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnoozeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnoozeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderMail"],"summary":"Unsnooze Message","description":"Unsnooze a message, returning it to its original folder.","operationId":"unsnooze_message_api_v1_mail_messages__message_id__snooze_delete","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"integer","title":"Message Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnoozeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/snoozed":{"get":{"tags":["SpiderMail"],"summary":"List Snoozed Messages","description":"List all snoozed messages for the client.","operationId":"list_snoozed_messages_api_v1_mail_snoozed_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by mailbox email","title":"Email"},"description":"Filter by mailbox email"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnoozedListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/labels":{"get":{"tags":["SpiderMail"],"summary":"List Labels","description":"List all label definitions for the client.","operationId":"list_labels_api_v1_mail_labels_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelListResponse"}}}}}},"post":{"tags":["SpiderMail"],"summary":"Create Label","description":"Create a new label definition.","operationId":"create_label_api_v1_mail_labels_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/labels/{label_id}":{"patch":{"tags":["SpiderMail"],"summary":"Update Label","description":"Update a label definition (name and/or color).","operationId":"update_label_api_v1_mail_labels__label_id__patch","parameters":[{"name":"label_id","in":"path","required":true,"schema":{"type":"integer","title":"Label Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderMail"],"summary":"Delete Label","description":"Delete a label definition.","operationId":"delete_label_api_v1_mail_labels__label_id__delete","parameters":[{"name":"label_id","in":"path","required":true,"schema":{"type":"integer","title":"Label Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/templates":{"get":{"tags":["SpiderMail Templates"],"summary":"List Templates","description":"List all email templates for the client.\n\nFilter by template_type (signature, header, layout, full) or get all.","operationId":"list_templates_api_v1_mail_templates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/MailTemplateType"},{"type":"null"}],"description":"Filter by template type","title":"Template Type"},"description":"Filter by template type"},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only return active templates","default":true,"title":"Active Only"},"description":"Only return active templates"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderMail Templates"],"summary":"Create Template","description":"Create a new email template.\n\nTemplates use Jinja2 syntax: {{ variable }}, {% if condition %}, etc.\nVariables are auto-detected if not provided.","operationId":"create_template_api_v1_mail_templates_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/templates/{template_id}":{"get":{"tags":["SpiderMail Templates"],"summary":"Get Template","description":"Get a specific template by ID.","operationId":"get_template_api_v1_mail_templates__template_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderMail"],"summary":"Update Template","description":"Update an existing template.","operationId":"update_template_api_v1_mail_templates__template_id__patch","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderMail Templates"],"summary":"Delete Template","description":"Delete a template.","operationId":"delete_template_api_v1_mail_templates__template_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["SpiderMail Templates"],"summary":"Update Template","description":"Update an existing template.\n\nOnly provided fields are updated.","operationId":"update_template_api_v1_mail_templates__template_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/security/events":{"get":{"tags":["SpiderMail"],"summary":"List Security Events","description":"List security events for this client's mailboxes.\n\nSecurity events are logged when:\n- Prompt injection patterns are detected in inbound emails\n- Credential leaks are blocked in outbound emails\n- Messages are quarantined or released","operationId":"list_security_events_api_v1_mail_security_events_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by mailbox email address","title":"Email"},"description":"Filter by mailbox email address"},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by event type (injection_detected, credential_blocked, etc.)","title":"Event Type"},"description":"Filter by event type (injection_detected, credential_blocked, etc.)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Number of events to return","default":50,"title":"Limit"},"description":"Number of events to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/quarantine":{"get":{"tags":["SpiderMail"],"summary":"List Quarantined Messages","description":"List quarantined messages for this client's mailboxes.\n\nQuarantined messages are inbound emails flagged with potential\nprompt injection attacks. They are stored but marked for admin review.","operationId":"list_quarantined_messages_api_v1_mail_quarantine_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by mailbox email address","title":"Email"},"description":"Filter by mailbox email address"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of messages to return","default":20,"title":"Limit"},"description":"Number of messages to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/messages/{message_id}/release":{"post":{"tags":["SpiderMail"],"summary":"Release From Quarantine","description":"Release a message from quarantine after admin review.\n\nThis marks the message as safe and logs a security event.\nThe message will then be visible to agents.","operationId":"release_from_quarantine_api_v1_mail_messages__message_id__release_post","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"integer","title":"Message Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/session":{"get":{"tags":["SpiderMail"],"summary":"Get Session","description":"Get session context for AI agent bootstrap.\n\nReturns mailbox info, unread count, and recent messages in a single call.\nThis is optimized for agents that need to quickly understand mailbox state.\n\nUse ?format=yaml for token-efficient agent consumption (~60% token savings).","operationId":"get_session_api_v1_mail_session_get","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Mailbox email address","title":"Email"},"description":"Mailbox email address"},{"name":"include_recent","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":0,"description":"Number of recent messages to include","default":10,"title":"Include Recent"},"description":"Number of recent messages to include"},{"name":"format","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/OutputFormat"}],"description":"Output format: json or yaml","default":"json","title":"Format"},"description":"Output format: json or yaml"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/compose/assist":{"post":{"tags":["SpiderMail"],"summary":"Compose Assist","description":"AI-powered email composition assistant.\n\nActions:\n- write: Generate new email from subject\n- rewrite: Improve existing content\n- expand: Make content longer\n- shorten: Condense to key points\n- formal: Make more professional\n- casual: Make more relaxed\n- fix_grammar: Correct errors\n\nUses LiteLLM proxy for model routing.","operationId":"compose_assist_api_v1_mail_compose_assist_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIComposeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIComposeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/templates/{template_id}/preview":{"post":{"tags":["SpiderMail Templates"],"summary":"Preview Template","description":"Preview a template with sample data.\n\nRenders the template with provided variables and returns the result.\nUseful for testing templates before using them in emails.","operationId":"preview_template_api_v1_mail_templates__template_id__preview_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"integer","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplatePreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailTemplatePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/views":{"get":{"tags":["SpiderMail Views"],"summary":"List Views","description":"List every view visible to the caller:\n  * views the caller created (`created_by = current_user_id`), AND\n  * NULL-owned views (client-wide, Bearer-created), AND\n  * `is_shared = TRUE` views if `include_shared=true`.\n\nSession users see their own views plus team-shared ones by default.\nBearer-token callers see all NULL-owned views (there is no per-user scope).","operationId":"list_views_api_v1_mail_views_get","parameters":[{"name":"include_shared","in":"query","required":false,"schema":{"type":"boolean","description":"Include team-shared views.","default":true,"title":"Include Shared"},"description":"Include team-shared views."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailViewListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderMail Views"],"summary":"Create View","description":"Create a new saved view. `is_shared` defaults to false (default-private).\n`created_by` is filled from the session user; Bearer callers get NULL.","operationId":"create_view_api_v1_mail_views_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailViewCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailViewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/views/{view_id}":{"get":{"tags":["SpiderMail Views"],"summary":"Get View","operationId":"get_view_api_v1_mail_views__view_id__get","parameters":[{"name":"view_id","in":"path","required":true,"schema":{"type":"integer","title":"View Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailViewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderMail Views"],"summary":"Update View","operationId":"update_view_api_v1_mail_views__view_id__patch","parameters":[{"name":"view_id","in":"path","required":true,"schema":{"type":"integer","title":"View Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailViewUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailViewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderMail Views"],"summary":"Delete View","operationId":"delete_view_api_v1_mail_views__view_id__delete","parameters":[{"name":"view_id","in":"path","required":true,"schema":{"type":"integer","title":"View Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/attachments/{attachment_id}":{"get":{"tags":["SpiderMail Attachments"],"summary":"Get Attachment Content","description":"Retrieve attachment content by attachment ID.\n\nThis endpoint supports the three-layer progressive disclosure pattern:\n- **Layer 1 (summary)**: LLM-generated summary (~100 tokens)\n- **Layer 2 (preview)**: First 1500 characters of extracted text\n- **Layer 3 (full)**: Complete extracted text content (default)\n\nThe attachment must belong to a mailbox owned by the authenticated client.\nReturns `text/plain` response for easy agent consumption.\n\nUse this endpoint when an agent needs deeper analysis of an attachment\nthat was summarized in the email payload.","operationId":"get_attachment_content_api_v1_mail_attachments__attachment_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"layer","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Content layer to retrieve: 'summary' (Layer 1), 'preview' (Layer 2), or 'full' (Layer 3, default)","title":"Layer"},"description":"Content layer to retrieve: 'summary' (Layer 1), 'preview' (Layer 2), or 'full' (Layer 3, default)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/attachments/by-key/{storage_key}":{"get":{"tags":["SpiderMail Attachments"],"summary":"Get Attachment By Storage Key","description":"Retrieve attachment content by storage key.\n\nThis is an alternative endpoint for agents that have the storage_key\nfrom the email payload's `retrieve_key` field.\n\nThe storage key must correspond to an attachment owned by the client.","operationId":"get_attachment_by_storage_key_api_v1_mail_attachments_by_key__storage_key__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"storage_key","in":"path","required":true,"schema":{"type":"string","title":"Storage Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mail/attachments/message/{message_id}":{"get":{"tags":["SpiderMail Attachments"],"summary":"List Message Attachments","description":"List all attachments for a message.\n\nReturns Layer 1 (summary) and Layer 2 (preview) for each attachment,\nalong with metadata and retrieve keys for fetching full content.","operationId":"list_message_attachments_api_v1_mail_attachments_message__message_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"integer","title":"Message Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/me":{"get":{"tags":["Dashboard"],"summary":"Get Current User","description":"Return the current authenticated dashboard user's info and role.","operationId":"get_current_user_api_v1_dashboard_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUserResponse"}}}}}}},"/api/v1/dashboard/me/preferences":{"patch":{"tags":["Dashboard"],"summary":"Update My Preferences","description":"Update per-user UI preferences for the current dashboard user.\n\nToday the only field is `event_sounds_enabled`, the Live Theater\naudio toggle (Stage D). Returns the full updated `/me` payload so\nthe client can refresh its cache from a single response.","operationId":"update_my_preferences_api_v1_dashboard_me_preferences_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPreferencesUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/users":{"get":{"tags":["Dashboard"],"summary":"List Dashboard Users","description":"List all dashboard users with their brand memberships. Super admin only.","operationId":"list_dashboard_users_api_v1_dashboard_users_get","parameters":[{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUserListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Dashboard"],"summary":"Create Dashboard User","description":"Provision a Better Auth user for dashboard access. Super admin only.","operationId":"create_dashboard_user_api_v1_dashboard_users_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUserCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/users/{user_id}":{"patch":{"tags":["Dashboard"],"summary":"Update Dashboard User","description":"Update a dashboard user's role, permissions, or active status. Super admin only.","operationId":"update_dashboard_user_api_v1_dashboard_users__user_id__patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Dashboard"],"summary":"Delete Dashboard User","description":"Remove dashboard access for a user. Super admin only.","operationId":"delete_dashboard_user_api_v1_dashboard_users__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/users/{user_id}/brands":{"post":{"tags":["Dashboard"],"summary":"Add User To Brand","description":"Directly add a user to a brand. Super admin only.\n\nUnlike the invitation flow, this immediately adds the user as a member.","operationId":"add_user_to_brand_api_v1_dashboard_users__user_id__brands_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUserToBrandRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/credentials/regenerate":{"post":{"tags":["Dashboard"],"summary":"Regenerate Client Credentials","description":"Regenerate API credentials for the current user's client.\n\n- brand_admin and client_user: Regenerates their own client's credentials\n- super_admin: Must not use this endpoint (they don't have a client)\n\nReturns new credentials. Save them immediately - they cannot be retrieved again.","operationId":"regenerate_client_credentials_api_v1_dashboard_credentials_regenerate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientCredentialsResponse"}}}}}}},"/api/v1/dashboard/users/{user_id}/generate-password":{"get":{"tags":["Dashboard"],"summary":"Generate Password For User","description":"Generate a secure random password. Super admin only.\n\nReturns a 16-character password with mixed case, digits, and symbols.\nThe password is NOT automatically set - use /set-password to apply it.","operationId":"generate_password_for_user_api_v1_dashboard_users__user_id__generate_password_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePasswordResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/users/{user_id}/set-password":{"post":{"tags":["Dashboard"],"summary":"Set User Password","description":"Set a user's password directly. Super admin only.\n\nUpdates the password in Better Auth's account table using scrypt hashing.\nOptionally sends the new password to the user via email.","operationId":"set_user_password_api_v1_dashboard_users__user_id__set_password_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswordRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswordResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/users/{user_id}/trigger-reset":{"post":{"tags":["Dashboard"],"summary":"Trigger Password Reset","description":"Trigger a password reset email for a user. Super admin only.\n\nCreates a reset token and sends the password reset email to the user.","operationId":"trigger_password_reset_api_v1_dashboard_users__user_id__trigger_reset_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerResetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/jobs/spiderMaps/submit":{"post":{"tags":["Dashboard Client"],"summary":"Submit Spider Maps Job","description":"Submit a SpiderMaps job via dashboard session auth.\n\nThis wraps the existing job submission logic but uses session cookies\ninstead of Bearer token authentication.","operationId":"submit_spider_maps_job_api_v1_dashboard_client_jobs_spiderMaps_submit_post","requestBody":{"content":{"application/json":{"schema":{"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/jobs":{"get":{"tags":["Dashboard Client"],"summary":"List Client Jobs","description":"List jobs for the authenticated client user.\n\nMulti-tenant scoping per ``resolve_client_scope``:\n  - super_admin without scope header  → all jobs (unfiltered)\n  - super_admin + X-Selected-Client-Id → that client's jobs\n  - super_admin + X-Brand-ID           → all jobs in that brand\n  - non-super_admin with client_id    → that client's jobs\n  - non-super_admin with brand_id     → all jobs in their brand","operationId":"list_client_jobs_api_v1_dashboard_client_jobs_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":10,"title":"Page Size"}},{"name":"type_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type Filter"}},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Filter"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/jobs/{job_id}/status":{"get":{"tags":["Dashboard Client"],"summary":"Get Job Status","description":"Get status for a specific job.","operationId":"get_job_status_api_v1_dashboard_client_jobs__job_id__status_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/jobs/{job_id}/results":{"get":{"tags":["Dashboard Client"],"summary":"Get Job Results","description":"Get results for a completed job.\n\nMulti-tenant scoping per ``resolve_client_scope``:\n  - super_admin (any scope) → any job\n  - non-super_admin with client_id or brand_id → only their client/brand","operationId":"get_job_results_api_v1_dashboard_client_jobs__job_id__results_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/jobs/{job_id}/cancel":{"post":{"tags":["Dashboard Client"],"summary":"Cancel Job","description":"Cancel a queued or processing job.\n\nMulti-tenant scoping per ``resolve_client_scope``:\n  - super_admin (any scope) → any job\n  - non-super_admin with client_id or brand_id → only their client/brand","operationId":"cancel_job_api_v1_dashboard_client_jobs__job_id__cancel_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research/submit":{"post":{"tags":["Dashboard Client"],"summary":"Submit Company Research","description":"Submit a company research job via dashboard session auth.\n\nSupports both super_admin (with X-Selected-Client-Id header) and client users.","operationId":"submit_company_research_api_v1_dashboard_client_research_submit_post","requestBody":{"content":{"application/json":{"schema":{"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research":{"get":{"tags":["Dashboard Client"],"summary":"List Company Research","description":"List company research jobs for the authenticated user.\n\nMulti-tenant scoping per ``resolve_client_scope``:\n  - super_admin without scope          → all research jobs\n  - super_admin + X-Selected-Client-Id → that client's research\n  - super_admin + X-Brand-ID           → all research in brand\n  - non-super_admin with client_id     → that client's research\n  - non-super_admin with brand_id      → all research in their brand","operationId":"list_company_research_api_v1_dashboard_client_research_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research/{research_id}/status":{"get":{"tags":["Dashboard Client"],"summary":"Get Research Status","description":"Get status for a specific research job.\n\nMulti-tenant scoping per ``resolve_client_scope``:\n  - super_admin (any scope) → any research\n  - non-super_admin → only their client's or brand's research","operationId":"get_research_status_api_v1_dashboard_client_research__research_id__status_get","parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research/{research_id}/results":{"get":{"tags":["Dashboard Client"],"summary":"Get Research Results","description":"Get results for a completed research job.\n\nMulti-tenant scoping per ``resolve_client_scope``:\n  - super_admin (any scope) → any research\n  - non-super_admin → only their client's or brand's research","operationId":"get_research_results_api_v1_dashboard_client_research__research_id__results_get","parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research/{research_id}/pause":{"post":{"tags":["Dashboard Client"],"summary":"Pause Research","description":"Pause a running research job.","operationId":"pause_research_api_v1_dashboard_client_research__research_id__pause_post","parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research/{research_id}/resume":{"post":{"tags":["Dashboard Client"],"summary":"Resume Research","description":"Resume a paused research job.","operationId":"resume_research_api_v1_dashboard_client_research__research_id__resume_post","parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/research/{research_id}/cancel":{"post":{"tags":["Dashboard Client"],"summary":"Cancel Research","description":"Cancel a research job.","operationId":"cancel_research_api_v1_dashboard_client_research__research_id__cancel_post","parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/locations/countries":{"get":{"tags":["Dashboard Client"],"summary":"List Countries Dashboard","description":"List all available countries with location counts.\n\nUsed for campaign creation country dropdown.","operationId":"list_countries_dashboard_api_v1_dashboard_client_locations_countries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/client/locations/selectable-units":{"get":{"tags":["Dashboard Client"],"summary":"List Selectable Units Dashboard","description":"Flat, alphabetically merged list of selectable geo units for searchable\npickers (the \"smart box\" typeahead).\n\nEach unit is either a whole country (``kind=\"country\"``) or a US state\n(``kind=\"state\"``). When ``states_as_units`` is true the US country row is\nomitted and its states are injected as top-level units, side by side with\ncountries — type \"flor\" → Florida, \"germ\" → Germany. This makes selecting\n\"the whole USA\" impossible by construction (the volume guard is the\nbackstop). When false, every country (US included) is returned as-is.","operationId":"list_selectable_units_dashboard_api_v1_dashboard_client_locations_selectable_units_get","parameters":[{"name":"states_as_units","in":"query","required":false,"schema":{"type":"boolean","description":"When true, countries in the states-as-units set (US) are dropped and replaced by their admin regions (states) as top-level selectable units — so 'all of USA' cannot be picked for a ZIP campaign. When false, returns plain countries (US included as a whole) — for address/attribute pickers where the country is the correct unit.","default":true,"title":"States As Units"},"description":"When true, countries in the states-as-units set (US) are dropped and replaced by their admin regions (states) as top-level selectable units — so 'all of USA' cannot be picked for a ZIP campaign. When false, returns plain countries (US included as a whole) — for address/attribute pickers where the country is the correct unit."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/locations/regions":{"get":{"tags":["Dashboard Client"],"summary":"List Regions Dashboard","description":"List all admin regions (states/provinces) for a country.\n\nUsed for campaign creation region filter.","operationId":"list_regions_dashboard_api_v1_dashboard_client_locations_regions_get","parameters":[{"name":"country_code","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":2,"title":"Country Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/locations/stats":{"get":{"tags":["Dashboard Client"],"summary":"Get Country Stats Dashboard","description":"Get location statistics for a country.\n\nUsed to show how many locations match different filter modes.","operationId":"get_country_stats_dashboard_api_v1_dashboard_client_locations_stats_get","parameters":[{"name":"country_code","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":2,"title":"Country Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/locations":{"get":{"tags":["Dashboard Client"],"summary":"List Locations Dashboard","description":"List locations with filtering and pagination.\n\nUsed for campaign creation location selection (custom mode).","operationId":"list_locations_dashboard_api_v1_dashboard_client_locations_get","parameters":[{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"title":"Country Code"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"location_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location Type"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/quota":{"get":{"tags":["Dashboard Campaigns"],"summary":"Get Quota Status","description":"Get the current campaign quota status for the authenticated client.\n\nReturns remaining quotas for daily, monthly, and concurrent campaigns.\nFor super_admin without a selected client, returns unlimited quotas.","operationId":"get_quota_status_api_v1_dashboard_client_campaigns_quota_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaStatusResponse"}}}}}}},"/api/v1/dashboard/client/campaigns/active-locations":{"get":{"tags":["Dashboard Campaigns"],"summary":"List Active Locations","description":"Return lat/lng dots for the Live Theater Tactical Map.\n\nV1.0 (default flags off, back-compat): just queued/in-progress location\ndots (gray). Powers the queued layer when a campaign first starts.\n\nV1.1 (flags on): full geographic state of the user's active campaigns.\nAdds dim-gold completed-location dots (persist while campaign is active)\nand bright-gold per-business dots (one per business found at each location).\nSolves the \"map empty after dots age out of SSE buffer\" UX bug.\n\nMulti-tenant scoped:\n  - super_admin + X-Selected-Client-Id     → that client\n  - super_admin + X-Brand-ID only           → all clients in that brand\n  - super_admin without scope               → empty list\n  - client_user / brand_admin w/ client_id → that client\n  - brand-scoped user (client_id NULL,\n    brand_id set)                          → all clients in that brand\n  - non-super_admin without scope           → empty list\n\nTotal response capped at 5000 rows when include_businesses=true,\n500 otherwise.","operationId":"list_active_locations_api_v1_dashboard_client_campaigns_active_locations_get","parameters":[{"name":"include_completed","in":"query","required":false,"schema":{"type":"boolean","description":"If true, also return dim-gold dots for completed locations of active campaigns.","default":false,"title":"Include Completed"},"description":"If true, also return dim-gold dots for completed locations of active campaigns."},{"name":"include_businesses","in":"query","required":false,"schema":{"type":"boolean","description":"If true, also return bright-gold per-business dots from public.results.data->'businesses'.","default":false,"title":"Include Businesses"},"description":"If true, also return bright-gold per-business dots from public.results.data->'businesses'."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveLocationsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns":{"get":{"tags":["Dashboard Campaigns"],"summary":"List Campaigns","description":"List campaigns for the authenticated client.\n\nFor super_admin: Can filter by X-Selected-Client-Id or X-Brand-ID header.\nFor client_user/brand_admin: Filter by X-Brand-ID header or their client_id.","operationId":"list_campaigns_api_v1_dashboard_client_campaigns_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Dashboard Campaigns"],"summary":"Create Campaign","description":"Create a new SpiderMaps campaign.\n\nEnforces quota limits before creation.","operationId":"create_campaign_api_v1_dashboard_client_campaigns_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}":{"get":{"tags":["Dashboard Campaigns"],"summary":"Get Campaign Status","description":"Get detailed status for a specific campaign.","operationId":"get_campaign_status_api_v1_dashboard_client_campaigns__campaign_id__get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Dashboard Campaigns"],"summary":"Update Campaign","description":"Update an existing campaign's configuration.\n\n**What can be updated:**\n- `search_query` - Changes search term for future jobs\n- `name` - Campaign display name\n- SpiderMaps options (max_results, extract_reviews, etc.)\n- `workflow` - Workflow configuration (merged with existing)\n\n**Restrictions:**\n- Only active or stopped/paused campaigns can be updated\n- Completed campaigns cannot be modified\n- Changes only affect PENDING locations","operationId":"update_campaign_api_v1_dashboard_client_campaigns__campaign_id__patch","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Dashboard Campaigns"],"summary":"Delete Campaign","description":"Delete a campaign and all related data.\n\nRequirements:\n- Campaign must be stopped (no active jobs)\n- User must own the campaign\n\nCascade deletes:\n- campaign_locations (auto via FK)\n- campaign_workflow_jobs (auto via FK)\n- orchestrated_campaign_runs (manual)\n- location_worker_runs (manual)","operationId":"delete_campaign_api_v1_dashboard_client_campaigns__campaign_id__delete","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCampaignResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/stage-progress":{"get":{"tags":["Dashboard Campaigns"],"summary":"Get Campaign Stage Progress","description":"Return per-stage campaign progress (Maps / Site / Verify / Vayapin).\n\nReplaces the misleading single \"0/N 0%\" bar with location-level\ncompleted/active/queued/failed counts per pipeline stage, plus a\nvelocity-based ETA. Stage list is derived from the campaign's\n`workflow_config` — Site/Verify/Vayapin only appear when their\nconfig block has enabled=True.\n\nCached in Redis for 30s (chrome polls every 15s while active).","operationId":"get_campaign_stage_progress_api_v1_dashboard_client_campaigns__campaign_id__stage_progress_get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/jobs":{"get":{"tags":["Dashboard Campaigns"],"summary":"List Campaign Jobs","description":"List jobs for a specific campaign with pagination.","operationId":"list_campaign_jobs_api_v1_dashboard_client_campaigns__campaign_id__jobs_get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignJobsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/next":{"post":{"tags":["Dashboard Campaigns"],"summary":"Submit Next Job","description":"Submit the next pending job in the campaign.\n\nTwo safety properties this endpoint enforces:\n\n1. **Cross-tenant authorization (Bug #2-bis fix).** Without this check, any\n   authenticated ``client_user`` could call /next on any campaign by\n   guessing its ID — the job would be billed to the caller's own\n   ``client_id`` while writing into another tenant's\n   ``scraping_campaigns.workflow_*`` counters. ``can_access_campaign``\n   resolves the rightful owner.\n\n2. **Tenant Bearer forging (Bug #2 fix).** The campaign WindMill flow's\n   sub-modules (SpiderMaps, SpiderSite, SpiderVerify, VayaPin) are\n   strict-required since the §D1 audit-2 redeploy: an empty ``api_token``\n   raises ``ValueError('api_token is required')`` rather than falling back\n   to ``f/spideriq/api_token`` (which holds a stale workspace Bearer). We\n   forge the Bearer from the **campaign owner's** stored credentials, not\n   the caller's — super_admin impersonating VAYAPIN must dispatch as\n   VAYAPIN, not as themselves.\n\nShared helper at ``app/services/tenant_bearer.py``. Mirror of\n``_resolve_tenant_bearer`` in ``app/api/v1/jobs/pipeline_flows.py``.","operationId":"submit_next_job_api_v1_dashboard_client_campaigns__campaign_id__next_post","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignNextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/stop":{"post":{"tags":["Dashboard Campaigns"],"summary":"Stop Campaign","description":"Stop/pause a campaign.","operationId":"stop_campaign_api_v1_dashboard_client_campaigns__campaign_id__stop_post","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/continue":{"post":{"tags":["Dashboard Campaigns"],"summary":"Continue Campaign","description":"Resume a stopped campaign.","operationId":"continue_campaign_api_v1_dashboard_client_campaigns__campaign_id__continue_post","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/jobs/{job_id}/results":{"get":{"tags":["Dashboard Campaigns"],"summary":"Get Job Results","description":"Get results for a specific job in the campaign.\n\nIf wait=True, blocks until the job completes (up to 10 minutes).","operationId":"get_job_results_api_v1_dashboard_client_campaigns__campaign_id__jobs__job_id__results_get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"wait","in":"query","required":false,"schema":{"type":"boolean","description":"Wait for job completion","default":false,"title":"Wait"},"description":"Wait for job completion"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/export":{"get":{"tags":["Dashboard Campaigns"],"summary":"Export Campaign Results","description":"Export all campaign results.\n\nExports all completed job results for a campaign in JSON or CSV format.","operationId":"export_campaign_results_api_v1_dashboard_client_campaigns__campaign_id__export_get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","description":"Export format: json or csv","default":"json","title":"Format"},"description":"Export format: json or csv"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/runs":{"get":{"tags":["Dashboard Campaigns"],"summary":"List Campaign Runs","description":"Get Inngest workflow runs for a campaign.\n\nReturns tracked runs from the orchestrated_campaign_runs table,\nshowing run status, timing, and step counts.","operationId":"list_campaign_runs_api_v1_dashboard_client_campaigns__campaign_id__runs_get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/runs/{run_id}":{"get":{"tags":["Dashboard Campaigns"],"summary":"Get Campaign Run Detail","description":"Get detailed information about a specific campaign run.\n\nReturns run details including step-level information for waterfall visualization.","operationId":"get_campaign_run_detail_api_v1_dashboard_client_campaigns__campaign_id__runs__run_id__get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"integer","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/locations/{location_id}/runs":{"get":{"tags":["Dashboard Campaigns"],"summary":"Get Location Worker Runs","description":"Get worker runs for a specific campaign location.\n\nReturns SpiderMaps, SpiderSite, and SpiderVerify runs with timing and results.\nEnables per-job visibility in campaign detail UI.","operationId":"get_location_worker_runs_api_v1_dashboard_client_campaigns__campaign_id__locations__location_id__runs_get","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"location_id","in":"path","required":true,"schema":{"type":"integer","title":"Location Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/locations/{location_id}/retry":{"post":{"tags":["Dashboard Campaigns"],"summary":"Retry Location","description":"Retry a failed or completed location. Always auto-submits.\n\n- retry_mode=\"full\": Re-run entire workflow from SpiderMaps\n- retry_mode=\"site\": Keep Maps results, re-run SpiderSite + SpiderVerify\n- retry_mode=\"verify\": Keep Site results, re-run SpiderVerify only\n\nMax 3 retries per location. Returns 400 if limit exceeded.","operationId":"retry_location_api_v1_dashboard_client_campaigns__campaign_id__locations__location_id__retry_post","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"location_id","in":"path","required":true,"schema":{"type":"integer","title":"Location Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryLocationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryLocationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/locations/{location_id}/reset":{"post":{"tags":["Dashboard Campaigns"],"summary":"Reset Stuck Location","description":"Reset a location stuck in 'submitted' (processing) state.\n\n- mark_as=\"failed\": Mark as failed (default)\n- mark_as=\"pending\": Reset to pending for re-submission","operationId":"reset_stuck_location_api_v1_dashboard_client_campaigns__campaign_id__locations__location_id__reset_post","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"location_id","in":"path","required":true,"schema":{"type":"integer","title":"Location Id"}},{"name":"mark_as","in":"query","required":false,"schema":{"enum":["failed","pending"],"type":"string","default":"failed","title":"Mark As"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/campaigns/{campaign_id}/retry-failed":{"post":{"tags":["Dashboard Campaigns"],"summary":"Bulk Retry Failed","description":"Retry all failed locations in the campaign (up to max_locations).\n\nOnly retries locations that haven't exceeded the max retry limit (3).\nReturns list of location IDs that were reset and re-submitted.","operationId":"bulk_retry_failed_api_v1_dashboard_client_campaigns__campaign_id__retry_failed_post","parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","title":"Campaign Id"}},{"name":"max_locations","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Max Locations"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkRetryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/events/recent":{"get":{"tags":["Dashboard Live Theater"],"summary":"List Recent Events","description":"Synthesize the last N campaign-lifecycle events from existing tables\nand return them in SSE-envelope shape. Skips events fired in the\nlast 60 seconds — the SSE stream handles those, prevents double-render\nwhen prefill + live race.\n\nV1.2: enriched payloads + card variety:\n  - ``campaign.location.completed`` now JOINs ``campaign_workflow_jobs``\n    for real per-location ``sitesCrawled``/``emailsVerified`` counts\n    (previously hardcoded 0 → all KPIs except BUSINESSES showed 0).\n  - Synthesized ``campaign.vayapin.exported`` events from\n    ``public.results`` worker_type='vayapin' so PINS KPI ticks\n    and the Intel Feed has VayapinExportedCard variety.\n  - Synthesized ``campaign.business.found`` events from\n    ``campaign_workflow_jobs`` (capped per campaign so business\n    cards don't dominate the rolling buffer).\n  - Per-event-type response quota — instead of chronological top-N\n    biased toward whichever type has highest cadence, return up to\n    ``per_type`` of each type. Keeps the Intel Feed visually varied.\n\nDefault window 7 days, max 30 days. Default 50 events, max 200.","operationId":"list_recent_events_api_v1_dashboard_client_events_recent_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"default":168,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecentEventsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/overview/stats":{"get":{"tags":["Dashboard Workspace"],"summary":"Overview Stats","description":"Real Overview KPIs (running jobs, leads, verified emails, gate spend,\nsparkline, campaign health) for the resolved tenant scope.","operationId":"overview_stats_api_v1_dashboard_overview_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverviewStatsResponse"}}}}}}},"/api/v1/dashboard/activity":{"get":{"tags":["Dashboard Workspace"],"summary":"Activity","description":"Campaign-lifecycle activity feed, newest first, cursor-paginated.","operationId":"activity_api_v1_dashboard_activity_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/usage":{"get":{"tags":["Dashboard Workspace"],"summary":"Usage","description":"Daily jobs/leads/cost series + totals + billing-cycle lead quota.","operationId":"usage_api_v1_dashboard_usage_get","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^(30d|90d)$","default":"30d","title":"Period"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/pat/requests":{"get":{"tags":["Dashboard PAT"],"summary":"List PAT requests (admin only)","description":"List pending and historical PAT requests visible to the current admin.\n\nVisibility:\n- ``super_admin``: all requests across all clients.\n- ``brand_admin``: requests targeting clients in their active brand\n  memberships.\n- ``client_user``: 403 — admin role required to review requests.\n\nDefault sort: newest first. Default page size: 50.","operationId":"list_pat_requests_api_v1_dashboard_pat_requests_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/PATRequestStatus"},{"type":"null"}],"description":"Filter by status (default: all)","title":"Status"},"description":"Filter by status (default: all)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATRequestListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/pat/requests/{request_id}":{"get":{"tags":["Dashboard PAT"],"summary":"PAT request detail (admin only)","description":"Detail view for a single PAT request. Returns 404 either when the\nrequest doesn't exist OR when the caller's role doesn't permit viewing\nit (don't leak existence to brand admins outside their scope).","operationId":"get_pat_request_api_v1_dashboard_pat_requests__request_id__get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATRequestListItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/pat/requests/{request_id}/approve":{"post":{"tags":["Dashboard PAT"],"summary":"Approve PAT request via dashboard session auth","description":"Approve a pending PAT request. Replaces the email-link approve URL\nfor admins who already have a dashboard session. The PAT token issued\nby approval is cached for the polling agent in Redis (same channel as\nthe email flow) — the dashboard caller does not see it.","operationId":"approve_pat_request_api_v1_dashboard_pat_requests__request_id__approve_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/pat/requests/{request_id}/deny":{"post":{"tags":["Dashboard PAT"],"summary":"Deny PAT request via dashboard session auth","description":"Deny a pending PAT request via dashboard session auth.","operationId":"deny_pat_request_api_v1_dashboard_pat_requests__request_id__deny_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/token-plans":{"get":{"tags":["Dashboard - Token Plans"],"summary":"List my per-token plans","description":"The (token × service_type) grid scoped to the authenticated client. Same\nshape as the admin list, but only ever this client's own tokens.","operationId":"list_my_token_plans_api_v1_dashboard_token_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPlanListResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/dashboard/token-plans/{token_id}/{service_type}":{"put":{"tags":["Dashboard - Token Plans"],"summary":"Upsert one of my per-token plans (clamped to my client plan)","description":"Partial upsert of a token's plan. The token must belong to the caller\n(403 otherwise). Each supplied budget cap is clamped to the client's CSP cap;\nany field exceeding it returns 422 listing every violation. 404 when the\nclient has no CSP for this service_type. Fires NOTIFY on write.","operationId":"upsert_my_token_plan_api_v1_dashboard_token_plans__token_id___service_type__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}},{"name":"service_type","in":"path","required":true,"schema":{"type":"string","title":"Service Type"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenServicePlanClientUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenServicePlanRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Dashboard - Token Plans"],"summary":"Delete one of my per-token plans (revert to my client default)","description":"Revert the token to the client default for this service_type. 403 if the\ntoken isn't the caller's; 404 if no override exists. Fires NOTIFY.","operationId":"delete_my_token_plan_api_v1_dashboard_token_plans__token_id___service_type__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}},{"name":"service_type","in":"path","required":true,"schema":{"type":"string","title":"Service Type"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/token-plans":{"get":{"tags":["Dashboard - Token Plans (UI)"],"summary":"List my per-token plans (dashboard session)","description":"The (token × service_type) grid scoped to the session's effective client.\nSame shape as the admin + Bearer lists, but only ever this client's own\ntokens. super_admin sees the impersonated client (X-Selected-Client-Id /\nX-Brand-ID).","operationId":"list_my_token_plans_api_v1_dashboard_client_token_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPlanListResponse"}}}}}}},"/api/v1/dashboard/client/token-plans/{token_id}/{service_type}":{"put":{"tags":["Dashboard - Token Plans (UI)"],"summary":"Upsert one of my per-token plans (clamped to my client plan)","description":"Partial upsert of a token's plan. The token must belong to the session's\neffective client (403 otherwise). Each supplied budget cap is clamped to the\nclient's CSP cap; any field exceeding it returns 422 listing every violation.\n404 when the client has no CSP for this service_type. Fires NOTIFY on write.","operationId":"upsert_my_token_plan_api_v1_dashboard_client_token_plans__token_id___service_type__put","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}},{"name":"service_type","in":"path","required":true,"schema":{"type":"string","title":"Service Type"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenServicePlanClientUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenServicePlanRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Dashboard - Token Plans (UI)"],"summary":"Delete one of my per-token plans (revert to my client default)","description":"Revert the token to the client default for this service_type. 403 if the\ntoken isn't the session client's; 404 if no override exists. Fires NOTIFY.","operationId":"delete_my_token_plan_api_v1_dashboard_client_token_plans__token_id___service_type__delete","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}},{"name":"service_type","in":"path","required":true,"schema":{"type":"string","title":"Service Type"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/plans/me":{"get":{"tags":["Dashboard Plans"],"summary":"Get My Subscriptions","description":"The caller's active/trialing/past_due subscriptions + per-service\nmonthly usage bars. Empty list when the caller has no client scope.","operationId":"get_my_subscriptions_api_v1_dashboard_plans_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MySubscription"},"type":"array","title":"Response Get My Subscriptions Api V1 Dashboard Plans Me Get"}}}}}}},"/api/v1/dashboard/plans/browse":{"get":{"tags":["Dashboard Plans"],"summary":"Browse Tariffs","description":"All active tariffs (latest version each) the caller can subscribe to.\nIncludes the Stripe Price ids + cents so the UI computes the annual gate +\nsavings client-side. ``is_current`` marks the caller's existing tariffs.","operationId":"browse_tariffs_api_v1_dashboard_plans_browse_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BrowseTariff"},"type":"array","title":"Response Browse Tariffs Api V1 Dashboard Plans Browse Get"}}}}}}},"/api/v1/dashboard/plans/browse/bundles":{"get":{"tags":["Dashboard Plans"],"summary":"Browse Bundles","description":"Active bundle offerings with pre/post-discount pricing. Empty when no\nbundles are defined.","operationId":"browse_bundles_api_v1_dashboard_plans_browse_bundles_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BrowseBundle"},"type":"array","title":"Response Browse Bundles Api V1 Dashboard Plans Browse Bundles Get"}}}}}}},"/api/v1/dashboard/plans/invoices":{"get":{"tags":["Dashboard Plans"],"summary":"List Invoices","description":"The caller's recent Stripe invoices (last 20). Empty when the client\nhas no Stripe Customer yet (never subscribed).","operationId":"list_invoices_api_v1_dashboard_plans_invoices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ClientInvoice"},"type":"array","title":"Response List Invoices Api V1 Dashboard Plans Invoices Get"}}}}}}},"/api/v1/dashboard/plans/billing-portal":{"post":{"tags":["Dashboard Plans"],"summary":"Create Billing Portal","description":"Create a Stripe Billing Portal session and return its URL. The client\nmanages payment methods, invoices, and cancellation there.","operationId":"create_billing_portal_api_v1_dashboard_plans_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPortalResponse"}}}}}}},"/api/v1/dashboard/plans/upgrade-preview":{"post":{"tags":["Dashboard Plans"],"summary":"Upgrade Preview","description":"Proration preview for switching the caller's current subscription to a\ndifferent tariff version. Resolves the target Stripe Price (raises 409 if\nannual is requested but unminted, 404 if the version is unknown), then\nasks Stripe for the upcoming prorated invoice. Falls back to a no-proration\nestimate when the caller has no active subscription to migrate.","operationId":"upgrade_preview_api_v1_dashboard_plans_upgrade_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradePreviewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradePreview"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/plans/recent-denies":{"get":{"tags":["Dashboard Plans"],"summary":"Recent Denies","description":"The caller's recently DENIED dispatch decisions (last 30 days, 20 max)\n— what the customer can act on (rate / cost / quota). Partition-pruned by\nthe decided_at predicate.","operationId":"recent_denies_api_v1_dashboard_plans_recent_denies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RecentDeny"},"type":"array","title":"Response Recent Denies Api V1 Dashboard Plans Recent Denies Get"}}}}}}},"/api/v1/dashboard/plans/subscribe":{"post":{"tags":["Dashboard Plans"],"summary":"Subscribe","description":"Create a Stripe-hosted Checkout Session for the caller to subscribe to\na tariff version. Returns the hosted checkout URL the frontend redirects\nto. 404 unknown tariff · 409 annual-not-available (both via the typed\nexceptions' handlers in app/main.py).","operationId":"subscribe_api_v1_dashboard_plans_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/plans/change":{"post":{"tags":["Dashboard Plans"],"summary":"Change Plan","description":"Switch the caller's existing subscription item to a new tariff_version\nand/or billing interval — IN PLACE on the same Stripe subscription (no new\nsub, no double-billing). Prorations are invoiced immediately\n(``always_invoice``) so the customer is charged the difference today, per\nthe upgrade-preview copy.\n\n404 if the caller holds no active item for ``from_tariff_version_id``;\n409 if the target requires annual billing but has no annual Price (both via\nthe typed exceptions' handlers in app/main.py).","operationId":"change_plan_api_v1_dashboard_plans_change_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePlanRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/plans/cancel":{"post":{"tags":["Dashboard Plans"],"summary":"Cancel Subscription","description":"Schedule the caller's active subscription to cancel at the end of the\ncurrent billing period. The customer keeps access until\n``current_period_end``; no item is deleted, so ``/resume`` can un-cancel.\nNo body — a client has exactly one active parent subscription.\n\n404 if the caller has no active subscription (via the typed exception's\nhandler in app/main.py).","operationId":"cancel_subscription_api_v1_dashboard_plans_cancel_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStateResponse"}}}}}}},"/api/v1/dashboard/plans/resume":{"post":{"tags":["Dashboard Plans"],"summary":"Resume Subscription","description":"Un-cancel the caller's active subscription (clear\n``cancel_at_period_end``) — the correct \"changed my mind before the period\nrolls\" path, instead of minting a new subscription. No body.\n\n404 if no active subscription; 409 if the subscription is not currently\nscheduled to cancel (both via the typed exceptions' handlers in\napp/main.py).","operationId":"resume_subscription_api_v1_dashboard_plans_resume_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStateResponse"}}}}}}},"/api/v1/dashboard/client/leads":{"get":{"tags":["Dashboard Leads"],"summary":"List Leads","description":"List leads with pagination, search, and filters.","operationId":"list_leads_api_v1_dashboard_client_leads_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Search"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(campaign|company_intel)$"},{"type":"null"}],"title":"Source"}},{"name":"has_email","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Email"}},{"name":"has_verified_email","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Verified Email"}},{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Country Code"}},{"name":"workflow_stage","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workflow Stage"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","pattern":"^(asc|desc)$","default":"desc","title":"Sort Dir"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/leads/stats":{"get":{"tags":["Dashboard Leads"],"summary":"Lead Stats","description":"KPI counts for the leads dashboard header.","operationId":"lead_stats_api_v1_dashboard_client_leads_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/client/leads/export":{"get":{"tags":["Dashboard Leads"],"summary":"Export Leads","description":"Export all leads as CSV or JSON.","operationId":"export_leads_api_v1_dashboard_client_leads_export_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(csv|json)$","default":"csv","title":"Format"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(campaign|company_intel)$"},{"type":"null"}],"title":"Source"}},{"name":"country_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Country Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/client/leads/{lead_id}":{"get":{"tags":["Dashboard Leads"],"summary":"Get Lead","description":"Full lead detail with all enrichment data.","operationId":"get_lead_api_v1_dashboard_client_leads__lead_id__get","parameters":[{"name":"lead_id","in":"path","required":true,"schema":{"type":"integer","title":"Lead Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/vayapin/stats":{"get":{"tags":["Dashboard VayaPin"],"summary":"Get Vayapin Stats","description":"Get VayaPin dashboard KPI statistics.\n\nHonors only the SCOPE filters (search / country / campaign / date range) —\n`status` + `has_seo` are deliberately ignored here because these KPIs ARE the\nstatus/SEO breakdown (Total / With-SEO / Success-rate).","operationId":"get_vayapin_stats_api_v1_dashboard_vayapin_stats_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VayapinDashboardStats"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/vayapin/pins":{"get":{"tags":["Dashboard VayaPin"],"summary":"Get Vayapin Pins","description":"Get paginated list of VayaPin PINs with search and filters.","operationId":"get_vayapin_pins_api_v1_dashboard_vayapin_pins_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"has_seo","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Seo"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter PINs exported on/after this date (YYYY-MM-DD)","title":"Date From"},"description":"Filter PINs exported on/after this date (YYYY-MM-DD)"},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter PINs exported on/before this date (YYYY-MM-DD)","title":"Date To"},"description":"Filter PINs exported on/before this date (YYYY-MM-DD)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VayapinPinList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/vayapin/pins/{cwj_id}":{"get":{"tags":["Dashboard VayaPin"],"summary":"Get Vayapin Pin Detail","description":"Get full PIN detail including SEO content and API audit log.","operationId":"get_vayapin_pin_detail_api_v1_dashboard_vayapin_pins__cwj_id__get","parameters":[{"name":"cwj_id","in":"path","required":true,"schema":{"type":"integer","title":"Cwj Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VayapinPinDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/vayapin/countries":{"get":{"tags":["Dashboard VayaPin"],"summary":"Get Vayapin Countries","description":"Get country distribution for chart (honors the full filter bar).","operationId":"get_vayapin_countries_api_v1_dashboard_vayapin_countries_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"has_seo","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Seo"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VayapinCountryItem"},"title":"Response Get Vayapin Countries Api V1 Dashboard Vayapin Countries Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/vayapin/campaigns":{"get":{"tags":["Dashboard VayaPin"],"summary":"Get Vayapin Campaigns","description":"List campaigns (flows) that produced VayaPin PINs, for the filter dropdown.\n\nIntentionally NOT filtered — it populates the campaign dropdown itself, so it\nmust always show the full set of options regardless of the active filters.","operationId":"get_vayapin_campaigns_api_v1_dashboard_vayapin_campaigns_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VayapinCampaignItem"},"type":"array","title":"Response Get Vayapin Campaigns Api V1 Dashboard Vayapin Campaigns Get"}}}}}}},"/api/v1/dashboard/vayapin/timeline":{"get":{"tags":["Dashboard VayaPin"],"summary":"Get Vayapin Timeline","description":"Get daily export counts for timeline chart (honors the full filter bar).\n\nWhen an explicit date range is set, it bounds the x-axis; otherwise the\n`days` lookback window applies.","operationId":"get_vayapin_timeline_api_v1_dashboard_vayapin_timeline_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"has_seo","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Seo"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VayapinTimelineItem"},"title":"Response Get Vayapin Timeline Api V1 Dashboard Vayapin Timeline Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/vayapin/export-csv":{"get":{"tags":["Dashboard VayaPin"],"summary":"Export Vayapin Csv","description":"Export VayaPin PINs as CSV (honors the active filter bar).","operationId":"export_vayapin_csv_api_v1_dashboard_vayapin_export_csv_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"has_seo","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Seo"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/submit":{"post":{"tags":["Company Research"],"summary":"Submit company research job","description":"Submit a new company research job with Celery workflow orchestration.\n\n    ## Input Types\n    - **locations**: List of locations to search (triggers SpiderMaps discovery)\n    - **domains**: List of domains to research directly (skips SpiderMaps)\n\n    ## Pipeline Stages\n    1. **SpiderMaps** - Business discovery from Google Maps\n    2. **SpiderSite** - Website crawling for contacts\n    3. **SpiderCompanyData** - Company registry enrichment (US/UK/EU)\n    4. **Social Enrichment** - Instagram, Facebook, LinkedIn\n    5. **SpiderVerify** - Email verification\n\n    ## SpiderCompanyData Configuration\n    ```json\n    {\n      \"config\": {\n        \"spidercompanydata\": {\n          \"enabled\": true,\n          \"countries_filter\": [\"US\", \"GB\"],\n          \"include_officers\": true,\n          \"include_financials\": false\n        }\n      }\n    }\n    ```","operationId":"submit_research_api_v1_company_research_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchSubmitRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchSubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/company-research/{research_id}/status":{"get":{"tags":["Company Research"],"summary":"Get research job status","description":"Get the current status and progress of a research job.","operationId":"get_research_status_api_v1_company_research__research_id__status_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/{research_id}/results":{"get":{"tags":["Company Research"],"summary":"Get research job results","description":"Get the results of a completed research job.\n\n    Includes all discovered companies with:\n    - Contact information (emails, phones)\n    - Registry data (registration number, officers, financials)\n    - Social media profiles (Instagram, Facebook, LinkedIn)\n    - Company info extracted from website","operationId":"get_research_results_api_v1_company_research__research_id__results_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}},{"name":"include_registry_data","in":"query","required":false,"schema":{"type":"boolean","description":"Include SpiderCompanyData registry results","default":true,"title":"Include Registry Data"},"description":"Include SpiderCompanyData registry results"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchResultsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/{research_id}/cancel":{"post":{"tags":["Company Research"],"summary":"Cancel research job","description":"Cancel a running or queued research job.","operationId":"cancel_research_api_v1_company_research__research_id__cancel_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research":{"get":{"tags":["Company Research"],"summary":"List research jobs","description":"List all research jobs for the current client.","operationId":"list_research_jobs_api_v1_company_research_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status: queued, processing, completed, failed, cancelled","title":"Status"},"description":"Filter by status: queued, processing, completed, failed, cancelled"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/{research_id}/pause":{"post":{"tags":["Company Research"],"summary":"Pause research job","description":"Pause a running research job.\n\n    The job will stop processing new companies but will complete\n    any currently running tasks. Use `/resume` to continue.","operationId":"pause_research_api_v1_company_research__research_id__pause_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/{research_id}/resume":{"post":{"tags":["Company Research"],"summary":"Resume paused research job","description":"Resume a paused research job and continue processing.","operationId":"resume_research_api_v1_company_research__research_id__resume_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/{research_id}":{"patch":{"tags":["Company Research"],"summary":"Update research job configuration","description":"Update configuration for a research job.\n\n    Allowed updates:\n    - name: Job display name\n    - config: Stage configurations (spidercompanydata, social_enrichment, etc.)\n    - priority: Job priority (0-10)\n    - auto_submit: Auto-submit settings","operationId":"update_research_api_v1_company_research__research_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-research/{research_id}/wait":{"get":{"tags":["Company Research"],"summary":"Wait for research job completion","description":"Wait for research job to complete and return results.\n\n    This is a blocking endpoint that polls until:\n    - Job completes (returns results)\n    - Job fails (returns error)\n    - Timeout reached (returns current status)","operationId":"wait_for_results_api_v1_company_research__research_id__wait_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"research_id","in":"path","required":true,"schema":{"type":"string","title":"Research Id"}},{"name":"timeout","in":"query","required":false,"schema":{"type":"integer","maximum":600,"minimum":10,"description":"Timeout in seconds","default":300,"title":"Timeout"},"description":"Timeout in seconds"},{"name":"poll_interval","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"description":"Poll interval in seconds","default":5,"title":"Poll Interval"},"description":"Poll interval in seconds"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks":{"post":{"tags":["Dashboard Playbooks"],"summary":"Create Playbook","description":"Create a new playbook.\n\nSupports both 'basic' (Maps → Site → Verify) and 'plus' (full pipeline) types.","operationId":"create_playbook_api_v1_playbooks_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Dashboard Playbooks"],"summary":"List Playbooks","description":"List playbooks for the authenticated user.\n\nSupports filtering by:\n- status: queued, processing, completed, failed, paused\n- playbook_type: basic, plus\n- active_only: Only show playbooks with locations or companies (default: true)\n\nSupports sorting by:\n- created_at, name, status, current_stage, companies_found, companies_processed, emails_verified","operationId":"list_playbooks_api_v1_playbooks_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"playbook_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Playbook Type"}},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Field to sort by","default":"created_at","title":"Sort By"},"description":"Field to sort by"},{"name":"sort_order","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort order: asc or desc","default":"desc","title":"Sort Order"},"description":"Sort order: asc or desc"},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only show playbooks with actual workflow data","default":true,"title":"Active Only"},"description":"Only show playbooks with actual workflow data"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/status":{"get":{"tags":["Dashboard Playbooks"],"summary":"Get Playbook Status","description":"Get detailed status for a specific playbook.","operationId":"get_playbook_status_api_v1_playbooks__playbook_id__status_get","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}":{"patch":{"tags":["Dashboard Playbooks"],"summary":"Update Playbook","description":"Update a playbook's configuration.\n\nAllows updating:\n- name: Playbook display name\n- config: Worker configuration (spidermaps, spidersite, etc.)\n- priority: Job priority\n- webhook_url: Webhook for notifications\n- webhook_secret: Webhook signing secret","operationId":"update_playbook_api_v1_playbooks__playbook_id__patch","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/results":{"get":{"tags":["Dashboard Playbooks"],"summary":"Get Playbook Results","description":"Get results (companies) for a specific playbook.","operationId":"get_playbook_results_api_v1_playbooks__playbook_id__results_get","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/pause":{"post":{"tags":["Dashboard Playbooks"],"summary":"Pause Playbook","description":"Pause a running playbook.","operationId":"pause_playbook_api_v1_playbooks__playbook_id__pause_post","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/resume":{"post":{"tags":["Dashboard Playbooks"],"summary":"Resume Playbook","description":"Resume a paused playbook.","operationId":"resume_playbook_api_v1_playbooks__playbook_id__resume_post","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/cancel":{"post":{"tags":["Dashboard Playbooks"],"summary":"Cancel Playbook","description":"Cancel a playbook.","operationId":"cancel_playbook_api_v1_playbooks__playbook_id__cancel_post","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/retry":{"post":{"tags":["Dashboard Playbooks"],"summary":"Retry Playbook","description":"Retry a playbook by re-submitting it with the same configuration.\n\nCreates a new playbook with the same settings as the original.\nWorks for completed, failed, or cancelled playbooks.","operationId":"retry_playbook_api_v1_playbooks__playbook_id__retry_post","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/locations/{location_id}/stop":{"post":{"tags":["Dashboard Playbooks"],"summary":"Stop Stuck Location","description":"Stop a stuck location job.\n\nMarks the location as failed and cancels any pending/processing jobs.\nUse this when a location is stuck for too long.","operationId":"stop_stuck_location_api_v1_playbooks__playbook_id__locations__location_id__stop_post","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}},{"name":"location_id","in":"path","required":true,"schema":{"type":"integer","title":"Location Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/runs":{"get":{"tags":["Dashboard Playbooks"],"summary":"Get Playbook Runs","description":"Get Inngest workflow runs for a playbook.\n\nReturns the list of workflow executions with their status and timing.","operationId":"get_playbook_runs_api_v1_playbooks__playbook_id__runs_get","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/playbooks/{playbook_id}/runs/{run_id}":{"get":{"tags":["Dashboard Playbooks"],"summary":"Get Playbook Run Detail","description":"Get detailed information about a specific workflow run.\n\nIncludes waterfall trace data if available.","operationId":"get_playbook_run_detail_api_v1_playbooks__playbook_id__runs__run_id__get","parameters":[{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","title":"Playbook Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"integer","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands":{"get":{"tags":["Brands"],"summary":"List My Brands","description":"List all brands the current user is a member of.\n\nReturns brands with the user's role in each brand.\nFor super_admin, returns ALL brands with admin role.","operationId":"list_my_brands_api_v1_brands_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandListResponse"}}}}}},"post":{"tags":["Brands"],"summary":"Create Brand","description":"Create a new brand.\n\nThe current user becomes the owner of the brand.","operationId":"create_brand_api_v1_brands_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}":{"get":{"tags":["Brands"],"summary":"Get Brand","description":"Get brand details.\n\nUser must be a member of the brand.","operationId":"get_brand_api_v1_brands__brand_id__get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Brands"],"summary":"Update Brand","description":"Update brand details.\n\nRequires admin or owner role in the brand.","operationId":"update_brand_api_v1_brands__brand_id__patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/settings":{"get":{"tags":["Brands"],"summary":"Get Brand Settings","description":"Get brand settings. Requires admin access.","operationId":"get_brand_settings_api_v1_brands__brand_id__settings_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandSettingsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Brands"],"summary":"Update Brand Settings","description":"Update brand settings. Requires admin access.","operationId":"update_brand_settings_api_v1_brands__brand_id__settings_patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandSettingsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandSettingsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/logo":{"post":{"tags":["Brands"],"summary":"Upload Brand Logo","description":"Upload brand logo. Requires admin access.\n\nThe image will be:\n- Center-cropped to square\n- Resized to 256x256 max\n- Converted to WebP format","operationId":"upload_brand_logo_api_v1_brands__brand_id__logo_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_brand_logo_api_v1_brands__brand_id__logo_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Upload Brand Logo Api V1 Brands  Brand Id  Logo Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Brands"],"summary":"Delete Brand Logo","description":"Delete brand logo. Requires admin access.","operationId":"delete_brand_logo_api_v1_brands__brand_id__logo_delete","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Delete Brand Logo Api V1 Brands  Brand Id  Logo Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/information":{"get":{"tags":["Brands"],"summary":"Get Brand Information","description":"Get brand business information. Requires admin access.","operationId":"get_brand_information_api_v1_brands__brand_id__information_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandInformationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Brands"],"summary":"Update Brand Information","description":"Update brand business information. Requires admin access.","operationId":"update_brand_information_api_v1_brands__brand_id__information_patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandInformationUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandInformationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/members":{"get":{"tags":["Brands"],"summary":"List Brand Members","description":"List all members of a brand.\n\nUser must be a member of the brand to view.","operationId":"list_brand_members_api_v1_brands__brand_id__members_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandMemberListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/members/{member_user_id}":{"patch":{"tags":["Brands"],"summary":"Update Member","description":"Update a member's role or position.\n\nRequires admin access. Cannot modify owners.","operationId":"update_member_api_v1_brands__brand_id__members__member_user_id__patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"member_user_id","in":"path","required":true,"schema":{"type":"string","title":"Member User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandMemberUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Member Api V1 Brands  Brand Id  Members  Member User Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Brands"],"summary":"Remove Member","description":"Remove a member from the brand.\n\nRequires admin access. Cannot remove owners.","operationId":"remove_member_api_v1_brands__brand_id__members__member_user_id__delete","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"member_user_id","in":"path","required":true,"schema":{"type":"string","title":"Member User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Remove Member Api V1 Brands  Brand Id  Members  Member User Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/invitations":{"get":{"tags":["Brands"],"summary":"List Brand Invitations","description":"List invitations for a brand.\n\nRequires admin access to the brand.","operationId":"list_brand_invitations_api_v1_brands__brand_id__invitations_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status: pending, accepted, expired, canceled","title":"Status"},"description":"Filter by status: pending, accepted, expired, canceled"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Brands"],"summary":"Create Invitation","description":"Invite a user to join the brand.\n\nRequires admin access. Sends an email invitation.","operationId":"create_invitation_api_v1_brands__brand_id__invitations_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/invitations/{invitation_id}":{"delete":{"tags":["Brands"],"summary":"Revoke Invitation","description":"Revoke a pending invitation.\n\nRequires admin access.","operationId":"revoke_invitation_api_v1_brands__brand_id__invitations__invitation_id__delete","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"integer","title":"Invitation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Revoke Invitation Api V1 Brands  Brand Id  Invitations  Invitation Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/invitations/{invitation_id}/resend":{"post":{"tags":["Brands"],"summary":"Resend Invitation","description":"Resend an invitation (generates new token, resets expiry).\n\nRequires admin access.","operationId":"resend_invitation_api_v1_brands__brand_id__invitations__invitation_id__resend_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"integer","title":"Invitation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/invitations/view/{token}":{"get":{"tags":["Brands"],"summary":"View Invitation By Token","description":"View invitation details by token.\n\nThis is a public endpoint (no auth required) so users can see\nthe invitation details before signing up/logging in.","operationId":"view_invitation_by_token_api_v1_brands_invitations_view__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/invitations/accept":{"post":{"tags":["Brands"],"summary":"Accept Invitation","description":"Accept an invitation to join a brand.\n\nThe invitation token is obtained from the invitation email link.\nUser must be logged in to accept. This endpoint uses session-only auth\nbecause the user may not be provisioned in dashboard_users yet.","operationId":"accept_invitation_api_v1_brands_invitations_accept_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationAcceptRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationAcceptResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/invitations/pending":{"get":{"tags":["Brands"],"summary":"Get My Pending Invitations","description":"Get pending invitations for the current user's email.\n\nUseful to show invitations on the dashboard.","operationId":"get_my_pending_invitations_api_v1_brands_invitations_pending_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationListResponse"}}}}}}},"/api/v1/brands/admin/all":{"get":{"tags":["Brands"],"summary":"List All Brands","description":"List all brands in the system.\n\nSuper admin only.","operationId":"list_all_brands_api_v1_brands_admin_all_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandListResponse"}}}}}}},"/api/v1/brands/{brand_id}/agent-users":{"get":{"tags":["Agent Users"],"summary":"List Agent Users","operationId":"list_agent_users_api_v1_brands__brand_id__agent_users_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Inactive"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUserListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/agent-users/{agent_user_id}":{"delete":{"tags":["Agent Users"],"summary":"Revoke Agent User","description":"Revoke the agent's token *for this brand only*.\n\nCross-brand isolation: the agent keeps working in every other brand where\nit still has a live token. The underlying ``agent_users`` row persists —\nif the agent re-auths later, we'll refresh its tokens again with the same\nOPVS identity.","operationId":"revoke_agent_user_api_v1_brands__brand_id__agent_users__agent_user_id__delete","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/agent-users/{agent_user_id}/regenerate":{"post":{"tags":["Agent Users"],"summary":"Regenerate Agent Token","description":"Rotate the PAT for this agent in this brand.\n\nThe plaintext is returned in the response — this is a dashboard-initiated\nrotation and the caller is an authenticated admin, so there's no Redis\nhand-off dance (that exists for the email-approval flow where the agent\nand the approver are different entities). Agent must update its stored\ntoken manually, same as any API-key rotation.","operationId":"regenerate_agent_token_api_v1_brands__brand_id__agent_users__agent_user_id__regenerate_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/me":{"get":{"tags":["Auth Profile"],"summary":"Get User Me","description":"Get current user's full profile including brand memberships.\n\nThis endpoint is used by the settings page to display user info\nand the list of brands the user belongs to.","operationId":"get_user_me_api_v1_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeResponse"}}}}}}},"/api/v1/auth/profile":{"patch":{"tags":["Auth Profile"],"summary":"Update Profile","description":"Update the current user's profile fields.\n\nUpdatable fields: firstname, lastname, mobile","operationId":"update_profile_api_v1_auth_profile_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/profile/photo":{"post":{"tags":["Auth Profile"],"summary":"Upload Profile Photo","description":"Upload a profile photo.\n\nAccepts: image/png, image/jpeg, image/gif, image/svg+xml\nMax size: 2MB\nStored as base64 data URL in database.","operationId":"upload_profile_photo_api_v1_auth_profile_photo_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_profile_photo_api_v1_auth_profile_photo_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Auth Profile"],"summary":"Remove Profile Photo","description":"Remove the current user's profile photo.","operationId":"remove_profile_photo_api_v1_auth_profile_photo_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileUpdateResponse"}}}}}}},"/api/v1/auth/connectors":{"get":{"tags":["Auth Profile"],"summary":"Get Connectors","description":"Get list of OAuth providers connected to this account.\n\nReturns providers like 'google', 'github' with connection status.","operationId":"get_connectors_api_v1_auth_connectors_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ConnectorInfo"},"type":"array","title":"Response Get Connectors Api V1 Auth Connectors Get"}}}}}}},"/api/v1/auth/workspaces":{"get":{"tags":["Auth Profile"],"summary":"List projects the caller can operate on","description":"Returns every workspace (project) the authenticated caller has access to. Used by `spideriq use <project>` to resolve a project name and write ./spideriq.json for per-session binding (Phase 11+12 Lock 3).\n\n- **api_client / PAT**: returns the single client the token is scoped to.\n- **client_user / brand_admin**: returns clients they are provisioned for.\n- **super_admin**: returns all active clients.","operationId":"list_workspaces_api_v1_auth_workspaces_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspacesResponse"}}}}}}},"/api/v1/auth/whoami":{"get":{"tags":["Auth Profile"],"summary":"Who am I + which project is my token bound to?","description":"Unified identity endpoint for agents and dashboard users. Returns the current client_id plus human-readable project_name so an agent can verify its binding before a destructive deploy. Expired PATs receive a distinguishable `{error: 'token_expired'}` 401 instead of a generic 'invalid token' message.","operationId":"whoami_api_v1_auth_whoami_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/me/notifications-dnd":{"patch":{"tags":["Auth Profile"],"summary":"Patch Notifications Dnd","description":"Update the caller's DND/timezone fields on `dashboard_users`.\n\nUsed by the Notifications settings tab Card 1. Quiet hours are\ninterpreted in the user's timezone by the sidecar (slice 3.2),\nso all three (tz, start, end) move together — clients re-send the\ntimezone whenever they change it, but absent fields preserve the\nexisting value. Setting `clear_window=true` clears both dnd_start\nand dnd_end together (the only way to disable quiet hours).","operationId":"patch_notifications_dnd_api_v1_auth_me_notifications_dnd_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDndRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDndResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/provision":{"post":{"tags":["crm-provision"],"summary":"Provision the CRM workspace (idempotent)","description":"Create (or return the existing) CRM workspace for the caller's client.\n\nThin wrapper around :func:`services.crm.provision_workspace`, which\nhandles the two-session dance (RLS-scoped group/board creation, then\nplain-role IDAP backfill). Safe to call repeatedly.","operationId":"provision_crm_api_v1_crm_provision_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ProvisionRequest"},{"type":"null"}],"title":"Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/health":{"get":{"tags":["crm-provision"],"summary":"CRM schema health check","description":"Return 200 if the CRM DDL is in place, the RLS policies are\nenabled, and both CRM roles exist. No tenant scope — safe to expose.","operationId":"crm_health_api_v1_crm_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrmHealthResponse"}}}}}}},"/api/v1/crm/boards":{"get":{"tags":["crm-boards"],"summary":"List CRM boards","operationId":"list_boards_api_v1_crm_boards_get","parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Archived"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BoardResponse"},"title":"Response List Boards Api V1 Crm Boards Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crm-boards"],"summary":"Create a CRM board","operationId":"create_board_api_v1_crm_boards_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/boards/{board_id}":{"get":{"tags":["crm-boards"],"summary":"Get a CRM board","operationId":"get_board_api_v1_crm_boards__board_id__get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm-boards"],"summary":"Update a CRM board","operationId":"update_board_api_v1_crm_boards__board_id__patch","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["crm-boards"],"summary":"Delete a CRM board","operationId":"delete_board_api_v1_crm_boards__board_id__delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/board-groups":{"get":{"tags":["crm-board-groups"],"summary":"List board groups","operationId":"list_board_groups_api_v1_crm_board_groups_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardGroupResponse"},"type":"array","title":"Response List Board Groups Api V1 Crm Board Groups Get"}}}}}}},"/api/v1/crm/board-groups/{group_id}":{"get":{"tags":["crm-board-groups"],"summary":"Get a board group","operationId":"get_board_group_api_v1_crm_board_groups__group_id__get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGroupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm-board-groups"],"summary":"Update a board group","operationId":"update_board_group_api_v1_crm_board_groups__group_id__patch","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGroupUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGroupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["crm-board-groups"],"summary":"Delete a board group","operationId":"delete_board_group_api_v1_crm_board_groups__group_id__delete","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/boards/{board_id}/columns":{"get":{"tags":["crm-columns"],"summary":"List columns for a board","operationId":"list_columns_api_v1_crm_boards__board_id__columns_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ColumnResponse"},"title":"Response List Columns Api V1 Crm Boards  Board Id  Columns Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crm-columns"],"summary":"Create a column","operationId":"create_column_api_v1_crm_boards__board_id__columns_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/columns/{column_id}":{"get":{"tags":["crm-columns"],"summary":"Get a column","operationId":"get_column_api_v1_crm_columns__column_id__get","parameters":[{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm-columns"],"summary":"Update a column","operationId":"update_column_api_v1_crm_columns__column_id__patch","parameters":[{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["crm-columns"],"summary":"Delete a column","operationId":"delete_column_api_v1_crm_columns__column_id__delete","parameters":[{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/boards/{board_id}/columns/reorder":{"post":{"tags":["crm-columns"],"summary":"Reorder columns","operationId":"reorder_columns_api_v1_crm_boards__board_id__columns_reorder_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnReorder"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ColumnResponse"},"title":"Response Reorder Columns Api V1 Crm Boards  Board Id  Columns Reorder Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/boards/{board_id}/tasks":{"get":{"tags":["crm-tasks"],"summary":"List tasks on a board","operationId":"list_tasks_api_v1_crm_boards__board_id__tasks_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":2000,"minimum":1,"default":500,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskResponse"},"title":"Response List Tasks Api V1 Crm Boards  Board Id  Tasks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crm-tasks"],"summary":"Create a task","operationId":"create_task_api_v1_crm_boards__board_id__tasks_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/tasks/{task_id}":{"get":{"tags":["crm-tasks"],"summary":"Get a task","operationId":"get_task_api_v1_crm_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm-tasks"],"summary":"Update a task","operationId":"update_task_api_v1_crm_tasks__task_id__patch","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["crm-tasks"],"summary":"Soft-delete a task","operationId":"delete_task_api_v1_crm_tasks__task_id__delete","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/tasks/{task_id}/move":{"post":{"tags":["crm-tasks"],"summary":"Move a task to a column/position","operationId":"move_task_api_v1_crm_tasks__task_id__move_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskMove"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/tasks/{task_id}/relationships":{"get":{"tags":["crm-relationships"],"summary":"List card relationships","operationId":"list_card_relationships_api_v1_crm_tasks__task_id__relationships_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by type","title":"Type"},"description":"Filter by type"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CardRelationshipResponse"},"title":"Response List Card Relationships Api V1 Crm Tasks  Task Id  Relationships Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crm-relationships"],"summary":"Create a card relationship","operationId":"create_card_relationship_api_v1_crm_tasks__task_id__relationships_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardRelationshipCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardRelationshipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/tasks/{task_id}/relationships/{rel_type}/cards":{"get":{"tags":["crm-relationships"],"summary":"Get related cards","operationId":"get_related_cards_api_v1_crm_tasks__task_id__relationships__rel_type__cards_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"rel_type","in":"path","required":true,"schema":{"type":"string","title":"Rel Type"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","pattern":"^(children|parents)$","default":"children","title":"Direction"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"},"title":"Response Get Related Cards Api V1 Crm Tasks  Task Id  Relationships  Rel Type  Cards Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/card-relationships/{rel_id}":{"patch":{"tags":["crm-relationships"],"summary":"Update a card relationship","operationId":"update_card_relationship_api_v1_crm_card_relationships__rel_id__patch","parameters":[{"name":"rel_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rel Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardRelationshipUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardRelationshipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["crm-relationships"],"summary":"Delete a card relationship","operationId":"delete_card_relationship_api_v1_crm_card_relationships__rel_id__delete","parameters":[{"name":"rel_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rel Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/cards/search":{"get":{"tags":["crm-relationships"],"summary":"Cross-board card search","operationId":"search_cards_api_v1_crm_cards_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":200,"title":"Q"}},{"name":"board_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(companies|contacts|deals)$"},{"type":"null"}],"title":"Board Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CardSearchResult"},"title":"Response Search Cards Api V1 Crm Cards Search Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/master/tasks":{"get":{"tags":["crm-master-views"],"summary":"Cross-board CRM task aggregation","operationId":"get_master_tasks_api_v1_crm_master_tasks_get","parameters":[{"name":"board_type","in":"query","required":true,"schema":{"type":"string","pattern":"^(companies|contacts|deals|crm)$","title":"Board Type"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated status filter","title":"Status"},"description":"Comma-separated status filter"},{"name":"priority","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Priority"}},{"name":"board_ids","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Board Ids"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Search"}},{"name":"sort","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/MasterViewSort"}],"default":"priority_desc","title":"Sort"}},{"name":"group_by","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/MasterViewGroupBy"}],"default":"status","title":"Group By"}},{"name":"include_done","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Done"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MasterViewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/master/session":{"get":{"tags":["crm-master-views"],"summary":"Executive master session with stuck/overdue alerts","operationId":"get_master_session_api_v1_crm_master_session_get","parameters":[{"name":"board_type","in":"query","required":true,"schema":{"type":"string","pattern":"^(companies|contacts|deals|crm)$","title":"Board Type"}},{"name":"stuck_threshold_hours","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"default":24,"title":"Stuck Threshold Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MasterSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/master/groups/{group_id}/tasks":{"get":{"tags":["crm-master-views"],"summary":"Task aggregation scoped to one CRM workspace group","operationId":"get_group_tasks_api_v1_crm_master_groups__group_id__tasks_get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}},{"name":"group_by","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/MasterViewGroupBy"}],"default":"status","title":"Group By"}},{"name":"sort","in":"query","required":false,"schema":{"allOf":[{"$ref":"#/components/schemas/MasterViewSort"}],"default":"priority_desc","title":"Sort"}},{"name":"include_done","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Done"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":2000,"minimum":1,"default":500,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MasterViewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crm/schema":{"get":{"tags":["crm-schema"],"summary":"Introspect CRM schema for form-field mapping","description":"Return the CRM resource list, or one resource type's introspected schema.","operationId":"get_crm_schema_api_v1_crm_schema_get","parameters":[{"name":"resource_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"description":"CRM resource type to introspect (TABLE_MAP key or board type). Omit to list all available resource types.","title":"Resource Type"},"description":"CRM resource type to introspect (TABLE_MAP key or board type). Omit to list all available resource types."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations":{"get":{"tags":["Integrations"],"summary":"List Integration Overview","description":"Overview of all configured providers with aggregated stats for a brand.\n\nProvider list, key counts, healthy counts, and `daily_limit` capacity\ncome from `api_integrations` (the config).\n\nDaily usage, last-used timestamp, today's spend, and this month's\nspend come from `gate_request_logs` (the v3.2.0 litellm.Router flow's\nsource of truth). The legacy `daily_count` + `cached_usage_*` columns\nstayed zero because the v1 mark_usage callback that wrote them is no\nlonger in the request path. See PR #664.\n\nGroups strictly by `provider_name` and resolves the display label from\n`provider_registry.PROVIDER_REGISTRY` — `provider_label` rows are\ncosmetic-only and ignored (PR #657 fix for the Mistral duplicate).","operationId":"list_integration_overview_api_v1_brands__brand_id__integrations_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationOverviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Integrations"],"summary":"Create Integration","description":"Create a new API integration (vault key) for this brand.\n\nVault self-service: a human brand_admin/super_admin (session) OR the brand's\nown agent (PAT with the opt-in ``gate:vault:write`` scope) may add a key. The\nnew row is stamped ``brands_id = brand_id``; for a PAT, ``require_vault_writer``\nhas already verified the token owns ``brand_id``, so it can only ever add a\nkey to its own brand.","operationId":"create_integration_api_v1_brands__brand_id__integrations_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/providers":{"get":{"tags":["Integrations"],"summary":"List Provider Templates","description":"List available provider templates with field schemas.","operationId":"list_provider_templates_api_v1_brands__brand_id__integrations_providers_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/all":{"get":{"tags":["Integrations"],"summary":"List Integrations","description":"List all integration keys for a brand, optionally filtered by provider.\n\nEach row carries log-derived stats (requests_24h, spend_today,\nspend_month, last_used_at) aggregated from `gate_request_logs` joined\non `integration_id`. Legacy `daily_count` / `cached_usage_*` /\n`last_used_at` columns stay populated for backward compatibility but\nare stale — the v3.2.0 litellm.Router flow doesn't update them.","operationId":"list_integrations_api_v1_brands__brand_id__integrations_all_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"provider_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by provider name","title":"Provider Name"},"description":"Filter by provider name"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/{integration_id}":{"get":{"tags":["Integrations"],"summary":"Get Integration","description":"Get a single integration key detail.","operationId":"get_integration_api_v1_brands__brand_id__integrations__integration_id__get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Integrations"],"summary":"Update Integration","description":"Update an integration key. Brand admin only.","operationId":"update_integration_api_v1_brands__brand_id__integrations__integration_id__patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Integration Api V1 Brands  Brand Id  Integrations  Integration Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Integrations"],"summary":"Delete Integration","description":"Delete an integration key. Brand admin only.","operationId":"delete_integration_api_v1_brands__brand_id__integrations__integration_id__delete","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Delete Integration Api V1 Brands  Brand Id  Integrations  Integration Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/{integration_id}/reset-usage":{"post":{"tags":["Integrations"],"summary":"Reset Integration Usage","description":"Reset daily and minute usage counters. Brand admin only.","operationId":"reset_integration_usage_api_v1_brands__brand_id__integrations__integration_id__reset_usage_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Reset Integration Usage Api V1 Brands  Brand Id  Integrations  Integration Id  Reset Usage Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/{integration_id}/reauth":{"post":{"tags":["Integrations"],"summary":"Request Integration Reauth","description":"Issue a single-use re-auth link for one of this brand's vault credentials\nand email it to the key's contributor. The link lets them re-login / paste a\nfresh key and UPDATE the same row in place (see app/api/v1/gate/reauth.py).\n\nGuards:\n- non-super_admin callers must be a member of ``brand_id`` (403 otherwise),\n- the credential must belong to ``brand_id`` (404 otherwise) — a brand can\n  never re-auth a pool / other-tenant key it doesn't own.","operationId":"request_integration_reauth_api_v1_brands__brand_id__integrations__integration_id__reauth_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BrandReauthRequest"}],"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandReauthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/spend/provider/{provider_name}":{"get":{"tags":["Integrations"],"summary":"Get Provider Spend Overview","description":"Get aggregated spend overview for a provider.","operationId":"get_provider_spend_overview_api_v1_brands__brand_id__integrations_spend_provider__provider_name__get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"provider_name","in":"path","required":true,"schema":{"type":"string","title":"Provider Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderSpendOverview"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/{integration_id}/spend":{"get":{"tags":["Integrations"],"summary":"Get Integration Spend","description":"Get spend data for a specific integration with history.","operationId":"get_integration_spend_api_v1_brands__brand_id__integrations__integration_id__spend_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationSpendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/{integration_id}/test":{"post":{"tags":["Integrations"],"summary":"Test Integration Connection","description":"Test if an API key is working by making a lightweight API call.","operationId":"test_integration_connection_api_v1_brands__brand_id__integrations__integration_id__test_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/integrations/{integration_id}/sync-billing":{"post":{"tags":["Integrations"],"summary":"Sync Integration Billing","description":"Trigger billing sync for an integration using its billing adapter.","operationId":"sync_integration_billing_api_v1_brands__brand_id__integrations__integration_id__sync_billing_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Sync Integration Billing Api V1 Brands  Brand Id  Integrations  Integration Id  Sync Billing Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/integrations/pool/stats":{"get":{"tags":["Integrations"],"summary":"Get Pool Stats","description":"Get statistics for the shared key pool (share_with_pool=true).","operationId":"get_pool_stats_api_v1_admin_integrations_pool_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolStatsResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/v1/admin/integrations/pool":{"get":{"tags":["Integrations"],"summary":"List Pool Integrations","description":"List all pool integrations (share_with_pool=true). Admin only.","operationId":"list_pool_integrations_api_v1_admin_integrations_pool_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"provider_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by provider name","title":"Provider Name"},"description":"Filter by provider name"},{"name":"only_available","in":"query","required":false,"schema":{"type":"boolean","description":"Only show available keys","default":false,"title":"Only Available"},"description":"Only show available keys"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/integrations/{integration_id}/reset-failures":{"post":{"tags":["Integrations"],"summary":"Reset Integration Failures","description":"Reset consecutive failures and set health to healthy. Admin only.","operationId":"reset_integration_failures_api_v1_admin_integrations__integration_id__reset_failures_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"integration_id","in":"path","required":true,"schema":{"type":"integer","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Reset Integration Failures Api V1 Admin Integrations  Integration Id  Reset Failures Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/integrations/reset-daily":{"post":{"tags":["Integrations"],"summary":"Reset Daily Counters","description":"Reset daily counters for all integrations. Admin only.","operationId":"reset_daily_counters_api_v1_admin_integrations_reset_daily_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Reset Daily Counters Api V1 Admin Integrations Reset Daily Post"}}}}},"security":[{"APIKeyHeader":[]}]}},"/api/v1/brands/{brand_id}/mail/outreach/connections":{"get":{"tags":["Mail Outreach"],"summary":"List Outreach Connections","description":"List every outreach sidecar for this brand, newest first.","operationId":"list_outreach_connections_api_v1_brands__brand_id__mail_outreach_connections_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ConnectionResponse"},"title":"Response List Outreach Connections Api V1 Brands  Brand Id  Mail Outreach Connections Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/connections/{connection_id}":{"get":{"tags":["Mail Outreach"],"summary":"Get Outreach Connection","operationId":"get_outreach_connection_api_v1_brands__brand_id__mail_outreach_connections__connection_id__get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Mail Outreach"],"summary":"Update Outreach Connection","description":"Edit workspace_id / warmup_tag / lemwarm_domains / is_active.","operationId":"update_outreach_connection_api_v1_brands__brand_id__mail_outreach_connections__connection_id__patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Mail Outreach"],"summary":"Delete Outreach Connection","description":"Remove the outreach sidecar. Leaves api_integrations intact (user must\ndelete the integration itself via IntegrationsTab to remove credentials).\nFK cascades senders + campaigns + health_snapshots on this sidecar.","operationId":"delete_outreach_connection_api_v1_brands__brand_id__mail_outreach_connections__connection_id__delete","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/connections/{connection_id}/sync":{"post":{"tags":["Mail Outreach"],"summary":"Sync Outreach Connection","description":"Call adapter.list_senders(), upsert into mail_outreach_senders, and try to\nresolve each sender's email against mail_mailboxes for this brand's clients\n(so the classifier can key off mailbox_id).","operationId":"sync_outreach_connection_api_v1_brands__brand_id__mail_outreach_connections__connection_id__sync_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/senders":{"get":{"tags":["Mail Outreach"],"summary":"List Outreach Senders","description":"All senders across every outreach connection on this brand.","operationId":"list_outreach_senders_api_v1_brands__brand_id__mail_outreach_senders_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SenderResponse"},"title":"Response List Outreach Senders Api V1 Brands  Brand Id  Mail Outreach Senders Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/senders/{sender_id}/health":{"get":{"tags":["Mail Outreach"],"summary":"Get Sender Health","description":"Time-series for a single sender's health card.\n\n`days` bounds the history returned (default 30 — matches the Slice F detail\nchart). The most-recent snapshot is duplicated into `latest` so the client\ndoesn't have to index into history[0].","operationId":"get_sender_health_api_v1_brands__brand_id__mail_outreach_senders__sender_id__health_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"sender_id","in":"path","required":true,"schema":{"type":"integer","title":"Sender Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SenderHealthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/health/overview":{"get":{"tags":["Mail Outreach"],"summary":"Get Health Overview","description":"Latest snapshot per sender for this brand. One row per sender — powers the\ndashboard grid of HealthCards. Hits idx_health_snapshots_sender_polled via\nthe DISTINCT ON.","operationId":"get_health_overview_api_v1_brands__brand_id__mail_outreach_health_overview_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HealthOverviewRow"},"title":"Response Get Health Overview Api V1 Brands  Brand Id  Mail Outreach Health Overview Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/connections/{connection_id}/campaigns":{"get":{"tags":["Mail Outreach"],"summary":"List Remote Campaigns","description":"Campaigns visible to this connection — powers the 'pick a campaign' picker.","operationId":"list_remote_campaigns_api_v1_brands__brand_id__mail_outreach_connections__connection_id__campaigns_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RemoteCampaignResponse"},"title":"Response List Remote Campaigns Api V1 Brands  Brand Id  Mail Outreach Connections  Connection Id  Campaigns Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/connections/{connection_id}/push-status":{"get":{"tags":["Mail Outreach"],"summary":"Get Push Status","description":"Quota usage + per-campaign push counts for this connection.","operationId":"get_push_status_api_v1_brands__brand_id__mail_outreach_connections__connection_id__push_status_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/connections/{connection_id}/push":{"post":{"tags":["Mail Outreach"],"summary":"Push Campaign Leads","description":"Push a SpiderIQ campaign's verified leads into the chosen remote campaign.","operationId":"push_campaign_leads_api_v1_brands__brand_id__mail_outreach_connections__connection_id__push_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushLeadsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushLeadsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/mail/outreach/connections/{connection_id}/remove":{"post":{"tags":["Mail Outreach"],"summary":"Remove Campaign Leads","description":"Remove tracked leads from a remote campaign (frees account lead credits).","operationId":"remove_campaign_leads_api_v1_brands__brand_id__mail_outreach_connections__connection_id__remove_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"connection_id","in":"path","required":true,"schema":{"type":"integer","title":"Connection Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveLeadsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveLeadsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/stats":{"get":{"tags":["Dashboard Gate"],"summary":"Get Brand Gate Stats","description":"Brand's Gate stats: agent count, requests this month, spend.","operationId":"get_brand_gate_stats_api_v1_brands__brand_id__gate_stats_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/agents":{"get":{"tags":["Dashboard Gate"],"summary":"List Brand Agents","description":"List agent tokens belonging to this brand.\n\nStatus comes from `agent_token_status` (the canonical view —\nrevoked > expired > !is_active > active) so this dashboard agrees with\nSettings → Members → Agents on what's alive. Reading raw\n`agent_tokens.is_active` was the root of the \"10 Active / 0 active\"\ndivergence in gate-keys-coherence-plan.md §1.\n\n`created_by` is JOINed against Better Auth `\"user\"` (`user.id` is TEXT;\nSpiderGate LEARNINGS #18). NULL `created_by_user_id` surfaces as\n`{\"name\": \"System\"}` — legacy or agent-self-registered tokens have no\ndashboard operator on the hook.","operationId":"list_brand_agents_api_v1_brands__brand_id__gate_agents_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Inactive"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/agents/{token_id}":{"patch":{"tags":["Dashboard Gate"],"summary":"Update Brand Agent","description":"Update agent's budget/models/rate limits. Brand admin required.","operationId":"update_brand_agent_api_v1_brands__brand_id__gate_agents__token_id__patch","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/agents/{token_id}/detail":{"get":{"tags":["Dashboard Gate"],"summary":"Get Brand Agent Detail","description":"Detailed view of a single agent token: config + usage + recent activity.\n\nPowers the per-key detail page modeled after OpenRouter's API key page —\nconfig card, usage cards (today/week/month/total), per-model spend\nbreakdown, and recent activity rows.","operationId":"get_brand_agent_detail_api_v1_brands__brand_id__gate_agents__token_id__detail_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/usage":{"get":{"tags":["Dashboard Gate"],"summary":"Get Brand Usage","description":"Brand's usage: requests, tokens, cost, by model/provider.\n\nQueries gate_usage_hourly first (pre-aggregated, efficient). Falls back\nto gate_request_logs directly if the aggregated table has no data for the\nrequested period (e.g. recent requests not yet aggregated).","operationId":"get_brand_usage_api_v1_brands__brand_id__gate_usage_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/providers":{"get":{"tags":["Dashboard Gate"],"summary":"Get Gate Providers","description":"List available LLM providers with logos and descriptions.\nRead-only for clients — no edit capabilities.","operationId":"get_gate_providers_api_v1_brands__brand_id__gate_providers_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/playground/completions":{"post":{"tags":["Dashboard Gate"],"summary":"Playground Completions","description":"Proxy chat completions for the Playground UI.\nUses dashboard session auth and forwards to the SpiderGate engine.","operationId":"playground_completions_api_v1_brands__brand_id__gate_playground_completions_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/traces":{"get":{"tags":["Dashboard Gate Traces"],"summary":"List Traces","description":"List traces for a brand. Proxies LangFuse with DB fallback.","operationId":"list_traces_api_v1_brands__brand_id__gate_traces_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"agent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Id"}},{"name":"model","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(success|error|all)$"},{"type":"null"}],"title":"Status"}},{"name":"from_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Date"}},{"name":"to_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To Date"}},{"name":"min_latency_ms","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Min Latency Ms"}},{"name":"min_cost_usd","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Min Cost Usd"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/traces/{trace_id}":{"get":{"tags":["Dashboard Gate Traces"],"summary":"Get Trace Detail","description":"Get full trace detail with spans and content.","operationId":"get_trace_detail_api_v1_brands__brand_id__gate_traces__trace_id__get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"trace_id","in":"path","required":true,"schema":{"type":"string","title":"Trace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/traces/stats/summary":{"get":{"tags":["Dashboard Gate Traces"],"summary":"Get Trace Stats","description":"Aggregated trace statistics from gate_request_logs.","operationId":"get_trace_stats_api_v1_brands__brand_id__gate_traces_stats_summary_get","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":7,"title":"Days"}},{"name":"agent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Id"}},{"name":"model","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/agent-keys":{"post":{"tags":["Dashboard Gate"],"summary":"Create an agent key directly (dashboard-driven mint)","description":"Mint a new agent token under the given brand.\n\nThe flow mirrors `pat_service._apply_approval` — find-or-create an\n`agent_users` identity, generate a `spideriq_pat_*` secret, INSERT into\n`agent_tokens` — but bypasses the request → approve email loop because the\ncaller is a logged-in brand admin. Wrapped in a single transaction so the\npartial UNIQUE index `agent_tokens_user_client_active_uniq` never sees a\nhalf-built row.\n\nThe full secret is returned exactly once in the response body. Subsequent\nreads of the token (list / detail endpoints) only ever see `token_prefix`.","operationId":"create_agent_key_api_v1_brands__brand_id__gate_agent_keys_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentKeyRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/brands/{brand_id}/gate/agent-keys/avatar":{"post":{"tags":["Dashboard Gate"],"summary":"Upload an avatar image for an agent key (≤2 MB)","description":"Upload an avatar image to R2 under `agent-avatars/`.\n\nMED-01: enforce ~2 MB body cap before reading the full payload. We also\nvalidate Content-Type + extension to make sure the operator hasn't pointed\na profile picture URL at /etc/shadow or similar.","operationId":"upload_agent_avatar_api_v1_brands__brand_id__gate_agent_keys_avatar_post","parameters":[{"name":"brand_id","in":"path","required":true,"schema":{"type":"integer","title":"Brand Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_agent_avatar_api_v1_brands__brand_id__gate_agent_keys_avatar_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadAvatarResponse"}}}},"413":{"description":"Upload exceeds 2 MB limit"},"415":{"description":"Unsupported image type"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/idap/health":{"get":{"tags":["IDAP"],"summary":"IDAP health check","description":"Liveness probe for the IDAP router. Unauthenticated. Returns the list of supported `resource_types` (businesses, people, leads, ...) backed by `norm_cli_*` per-client tables.","operationId":"idap_health_api_v1_idap_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/idap/batch":{"post":{"tags":["IDAP"],"summary":"Batch-fetch resources by IDAP ref","description":"Resolve many `IdapRef` entries (type + id) in a single call. Missing or unauthorized refs are returned in the `errors` map; successful lookups populate `results`. Scoped to the authenticated client's `norm_cli_*` tables.","operationId":"batch_fetch_api_v1_idap_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapBatchRequest"}}},"required":true},"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapBatchResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/idap/media/{media_id}":{"get":{"tags":["IDAP"],"summary":"Proxy media binary from SeaweedFS","description":"Stream a media file (image/video/document) from SeaweedFS through the API, enforcing tenant scoping. Supports conditional requests (`If-None-Match`, `If-Modified-Since` → 304), `?thumb=1` for a 400px thumbnail, and `?download=1` to force `Content-Disposition: attachment`.","operationId":"proxy_media_api_v1_idap_media__media_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"title":"Media Id"}},{"name":"thumb","in":"query","required":false,"schema":{"type":"boolean","description":"Return 400px thumbnail","default":false,"title":"Thumb"},"description":"Return 400px thumbnail"},{"name":"download","in":"query","required":false,"schema":{"type":"boolean","description":"Set Content-Disposition: attachment","default":false,"title":"Download"},"description":"Set Content-Disposition: attachment"}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}},"304":{"description":"Not modified (conditional-request hit)"}}}},"/api/v1/idap/{resource_type}/search":{"get":{"tags":["IDAP"],"summary":"Full-text search within a resource type","description":"Full-text search across the authenticated client's `norm_cli_*` table for the given `resource_type`. Use `q` for the query, `fields` for column projection, `flags` to filter by flag keys, and `?format=yaml|md` for AI-agent-friendly output.","operationId":"search_resources_api_v1_idap__resource_type__search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"description":"Search query","title":"Q"},"description":"Search query"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"fields","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Fields"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|yaml|md)$","default":"json","title":"Format"}},{"name":"flags","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Flags"}}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapListResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}/stats":{"get":{"tags":["IDAP"],"summary":"Aggregate stats for a resource type","description":"Dashboard-oriented aggregates for the authenticated client's `norm_cli_{resource_type}` table: total count, per-flag-key counts, recency buckets. Cheap — pre-aggregated on read. Supports `?format=yaml|md` for AI-agent-friendly output.","operationId":"resource_stats_api_v1_idap__resource_type__stats_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|yaml|md)$","default":"json","title":"Format"}}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapStats"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}/resolve":{"get":{"tags":["IDAP"],"summary":"Resolve a resource by external identifier","description":"Look up a single resource by an external id (Google `place_id`, email `domain`, etc.) within the authenticated client's tenant scope. Returns the canonical IDAP record without requiring the internal UUID. Exactly one external identifier query param must be supplied.","operationId":"resolve_by_external_id_api_v1_idap__resource_type__resolve_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"place_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Google Place ID (e.g. 0x47e66fdad6f1cc73:0x341211b3fccd79e1)","title":"Place Id"},"description":"Google Place ID (e.g. 0x47e66fdad6f1cc73:0x341211b3fccd79e1)"},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Domain name (e.g. mariospizzeria.com)","title":"Domain"},"description":"Domain name (e.g. mariospizzeria.com)"},{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Email address (e.g. info@example.com)","title":"Email"},"description":"Email address (e.g. info@example.com)"},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"description":"LinkedIn URL or other URL identifier (linkedin_profiles only)","title":"Url"},"description":"LinkedIn URL or other URL identifier (linkedin_profiles only)"},{"name":"linkedin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"LinkedIn profile URL on a contact (e.g. https://linkedin.com/in/jane)","title":"Linkedin"},"description":"LinkedIn profile URL on a contact (e.g. https://linkedin.com/in/jane)"},{"name":"twitter","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Twitter profile URL on a contact","title":"Twitter"},"description":"Twitter profile URL on a contact"},{"name":"vat","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"description":"VAT number (e.g. DE123456789, GB123456789)","title":"Vat"},"description":"VAT number (e.g. DE123456789, GB123456789)"},{"name":"registration_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"description":"National registration number (e.g. UK Companies House 00445790)","title":"Registration Number"},"description":"National registration number (e.g. UK Companies House 00445790)"},{"name":"lei","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"description":"Legal Entity Identifier (20-char ISO 17442 code)","title":"Lei"},"description":"Legal Entity Identifier (20-char ISO 17442 code)"},{"name":"tax_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"description":"National tax id (e.g. US EIN, DE Steuernummer)","title":"Tax Id"},"description":"National tax id (e.g. US EIN, DE Steuernummer)"},{"name":"source_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"Registry-provided source id (admin/dev path — composite UNIQUE with `source`, seq-scans)","title":"Source Id"},"description":"Registry-provided source id (admin/dev path — composite UNIQUE with `source`, seq-scans)"},{"name":"pin_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"VayaPin PIN name (e.g. 'BB:TAPAS') — pins only","title":"Pin Name"},"description":"VayaPin PIN name (e.g. 'BB:TAPAS') — pins only"},{"name":"pin_data_set_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"VayaPin data set UUID — pins only (natural key)","title":"Pin Data Set Id"},"description":"VayaPin data set UUID — pins only (natural key)"},{"name":"account_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"VayaPin account UUID — pins only","title":"Account Id"},"description":"VayaPin account UUID — pins only"},{"name":"pin_subscription_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"VayaPin subscription UUID — pins only","title":"Pin Subscription Id"},"description":"VayaPin subscription UUID — pins only"},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include (e.g. 'emails,phones')","title":"Include"},"description":"Comma-separated related types to include (e.g. 'emails,phones')"},{"name":"fields","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Comma-separated field projection","title":"Fields"},"description":"Comma-separated field projection"},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|yaml|md)$","default":"json","title":"Format"}}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapResource"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}/duplicates":{"get":{"tags":["IDAP"],"summary":"Find duplicate resources sharing a common external key","description":"Return clusters of resources in the authenticated client's tenant that share the same value for a whitelisted external key (e.g. two or more `businesses` rows with the same `google_place_id`). Each cluster has count >= 2 — single-occurrence values are filtered out by HAVING COUNT(*) > 1. Used in dedupe workflows: surface the candidates here, then call `DELETE /idap/<resource_type>/{id}` (Wave D.2) to remove the duplicate. Whitelisted `key` values for `businesses` (Wave D.1): `google_place_id`, `domain`, `phone_e164` (direct columns), `vat`, `registration_number`, `lei`, `tax_id` (joined via `company_registry.business_id`).","operationId":"find_duplicates_endpoint_api_v1_idap__resource_type__duplicates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"key","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":50,"description":"Whitelisted clustering key. Allowed values depend on `resource_type` — see endpoint description. Free strings outside the whitelist return 400.","title":"Key"},"description":"Whitelisted clustering key. Allowed values depend on `resource_type` — see endpoint description. Free strings outside the whitelist return 400."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max clusters to return (1..500). Default 100.","default":100,"title":"Limit"},"description":"Max clusters to return (1..500). Default 100."}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapDuplicatesResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}/flags/bulk":{"post":{"tags":["IDAP"],"summary":"Bulk add/remove flags on many resources","description":"Apply flag writes (add/remove) to many resources of the same type in one call. Per-row failures are returned in `errors`; successful writes go in `results`. Scoped to the authenticated client's tenant tables.","operationId":"write_flags_bulk_api_v1_idap__resource_type__flags_bulk_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapBulkFlagWrite"}}}},"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapBulkFlagResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}/{resource_id}/flags":{"post":{"tags":["IDAP"],"summary":"Add or remove flags on a single resource","description":"Write one or more flag changes (add/remove) against a single resource in the authenticated client's tenant scope. The response returns the resulting full flag state. Use `/flags/bulk` when batching across many resources of the same type.","operationId":"write_flags_api_v1_idap__resource_type___resource_id__flags_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"title":"Resource Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapFlagWrite"}}}},"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapFlagResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}/{resource_id}":{"get":{"tags":["IDAP"],"summary":"Fetch a single resource by ID","description":"Return the canonical IDAP record for a single resource in the authenticated client's tenant. Supports `fields` projection, `include` (comma-separated related-resource expansions), and `?format=yaml|md` for AI-agent-friendly output. An invalid UUID is treated as a 404 rather than a 500.","operationId":"get_resource_api_v1_idap__resource_type___resource_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"title":"Resource Id"}},{"name":"fields","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Comma-separated field projection","title":"Fields"},"description":"Comma-separated field projection"},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include (e.g. 'emails,phones')","title":"Include"},"description":"Comma-separated related types to include (e.g. 'emails,phones')"},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|yaml|md)$","default":"json","title":"Format"}}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapResource"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/{resource_type}":{"get":{"tags":["IDAP"],"summary":"List resources of a given type (paged, sync-friendly)","description":"List resources within the authenticated client's tenant, with `since`/`until` timestamp filters for incremental sync, cursor-based pagination, `fields` projection, `include` expansion, `flags` filtering, and `?format=yaml|md` output. Designed so external systems (OPVS board-sync, CRMs) can incrementally pull new/changed records.","operationId":"list_resources_api_v1_idap__resource_type__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Return resources modified after this timestamp","title":"Since"},"description":"Return resources modified after this timestamp"},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Return resources modified before this timestamp","title":"Until"},"description":"Return resources modified before this timestamp"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Cursor"}},{"name":"fields","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Fields"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include (e.g. 'emails,phones')","title":"Include"},"description":"Comma-separated related types to include (e.g. 'emails,phones')"},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|yaml|md)$","default":"json","title":"Format"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Campaign Id"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Source"}},{"name":"flags","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Flags"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","maxLength":50,"default":"created_at","title":"Sort"}},{"name":"order","in":"query","required":false,"schema":{"type":"string","pattern":"^(asc|desc)$","default":"desc","title":"Order"}}],"responses":{"200":{"description":"Request succeeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapListResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Resource not found in the authenticated client's tenant-scoped tables","content":{"application/json":{"example":{"detail":"Resource not found"}}}},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}}}}},"/api/v1/idap/businesses/{business_id}":{"delete":{"tags":["IDAP"],"summary":"Delete a business record + cascade across per-tenant tables","description":"🔴 **Destructive.** Hard-deletes the business row in `norm_cli_<client>.businesses` AND cascades to linked tables (pins, business_contacts, business_registry, company_registry, contacts, phones, domains, linkedin_profiles). Transactional — all or nothing. Audit row written to `public.idap_deletions_audit` in the same transaction.\n\n**Does NOT touch cs.vayapin.com.** VayaPin pin pages are permanent per VayaPin §10. Response includes `vayapin_pins_remain_external` + the orphaned `pin_data_set_id`s so the caller knows what external state remains.\n\n**Does NOT cascade `emails`.** That table is a tenant-wide canonical store keyed on `email` UNIQUE — multiple businesses share verification rows. The `business_contacts` join row is what gets removed; the email's verification metadata stays.\n\n**`booking_flows` + `services` auto-cascade** via Postgres ON DELETE CASCADE FK; counts surface in `auto_cascaded`.\n\n**`bookings` is a blocker.** If any `bookings` row references this business or its contacts, the call returns 409 with the blocking booking IDs — caller resolves those first.\n\nTenant-owner auth only: caller's `client_id` IS the tenant scope. Idempotent: second call on the same UUID returns 404.","operationId":"delete_business_api_v1_idap_businesses__business_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"business_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"UUID of the business to delete. Resolve alternate keys via GET /idap/businesses/resolve first.","title":"Business Id"},"description":"UUID of the business to delete. Resolve alternate keys via GET /idap/businesses/resolve first."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BusinessDeleteRequest"},{"type":"null"}],"description":"Optional `{reason: '...'}` recorded verbatim in the audit row.","title":"Body"}}}},"responses":{"200":{"description":"Business + child rows deleted; audit row written. Response body reports per-table cascade counts + the VayaPin pin IDs that remain external (cs.vayapin.com pages are permanent per VayaPin §10 — we delete OUR record only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessDeleteResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"example":{"detail":"Invalid authentication token format. Expected: client_id:api_key:api_secret"}}}},"404":{"description":"Business UUID not found in the caller's tenant schema. Also returned on idempotent re-DELETE of an already-deleted ID."},"422":{"description":"Validation error (invalid resource_type, cursor, filter, or flag payload)","content":{"application/json":{"example":{"detail":"Validation error in request"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"example":{"detail":"Rate limit exceeded. Maximum 100 requests per minute."}}},"headers":{"X-RateLimit-Limit":{"description":"Maximum requests allowed per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in current window","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when rate limit resets","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}}},"409":{"description":"One or more `bookings` rows reference this business or its contacts via ON DELETE NO ACTION FK. Body lists the blocking booking IDs (up to 50). Caller must re-point or delete the bookings before re-trying."}}}},"/api/v1/booking/feature-status":{"get":{"tags":["SpiderBook"],"summary":"Get Feature Status","operationId":"get_feature_status_api_v1_booking_feature_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpiderBookFeatureStatus"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/content/help":{"get":{"tags":["Content"],"summary":"Content Help","description":"AI agent reference: returns all available content types, block types,\nLiquid filters, tags, theme structure, and data sources.\n\nDefaults to YAML (token-efficient). Use ?format=json for programmatic use.\nNo authentication required.","operationId":"content_help_api_v1_content_help_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"default":"yaml","title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/help/block-fields":{"get":{"tags":["Content"],"summary":"Content Block Fields","description":"E.2 (2026-05-22 Claude Code usability F-16) — per-block-type field map.\n\nReturns:\n  - All block types when ``block_type`` is unset\n  - The single block-type entry when ``block_type=hero`` (or similar)\n\nThe shape mirrors the ``block_types`` section of /content/help, including:\n  - ``fields``: canonical fields the default theme's snippet reads\n  - ``_aliases``: agent-natural mistakes → canonical replacement\n  - ``_anti_patterns``: shapes that 422 with a hint\n  - ``_notes``: free-form caveats\n\nThe source of truth is mirrored in:\n  - app/api/v1/_content_help.py (this endpoint's data)\n  - app/services/page_auditor.py (the audit warning + alias detection)\n\nBoth must stay in lock-step. Drift surfaces as silent-blank-section\nfailures the auditor catches at read-time — and as wrong-hint advice\nhere. Update both in the same PR.","operationId":"content_block_fields_api_v1_content_help_block_fields_get","parameters":[{"name":"block_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"If set, return only the entry for this block_type. Omit to list all.","title":"Block Type"},"description":"If set, return only the entry for this block_type. Omit to list all."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"default":"json","title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/variables":{"get":{"tags":["Content"],"summary":"Content Variables","description":"AI agent merge-tag reference: every flat variable (`{{ firstname }}`,\n`{{ company_name }}`, `{{ city }}`, etc.) available in dynamic-landing\ntemplates, with descriptions, source paths, selection rules, and realistic\nexample values from the Mario's Pizzeria demo fixture.\n\nAuto-generated from `apps/liquid-renderer/merge-tags.spec.json` — the exact\nsame JSON the TypeScript renderer imports at build time. Impossible to drift.\n\nDefaults to YAML (token-efficient for agents). Use `?format=json` for\nprogrammatic consumption. No authentication required.","operationId":"content_variables_api_v1_content_variables_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"default":"yaml","title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/themes":{"get":{"tags":["Content"],"summary":"List Themes","description":"List available built-in themes (public, no auth).","operationId":"list_themes_api_v1_content_themes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/themes/{name}":{"get":{"tags":["Content"],"summary":"Get Theme Detail","description":"Get a theme's details and all template files (public, no auth).","operationId":"get_theme_detail_api_v1_content_themes__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/site-templates":{"get":{"tags":["Content"],"summary":"Browse public site templates","description":"List public site templates. No auth — anyone can browse the gallery. Phase D adds 5 universal-axis filters (mood, palette, brand_fit, scene_type, agent_meta.<key>). The /marketplace/site-templates path is an alias for the same endpoint.","operationId":"list_site_templates_api_v1_content_site_templates_get","parameters":[{"name":"industry","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Industry"}},{"name":"use_case","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Use Case"}},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filter to templates that have this tag","title":"Tag"},"description":"Filter to templates that have this tag"},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"mood","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value mood filter (set-overlap).","title":"Mood"},"description":"Multi-value mood filter (set-overlap)."},{"name":"palette","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value palette filter (set-overlap).","title":"Palette"},"description":"Multi-value palette filter (set-overlap)."},{"name":"brand_fit","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value industry-fit filter (set-overlap).","title":"Brand Fit"},"description":"Multi-value industry-fit filter (set-overlap)."},{"name":"scene_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Single-value scene/intent filter.","title":"Scene Type"},"description":"Single-value scene/intent filter."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/site-templates/{slug}":{"get":{"tags":["Content"],"summary":"Get a site template by slug","description":"Fetch a single public site template by slug. No auth. 404 includes ``did_you_mean`` close-match suggestions.","operationId":"get_site_template_api_v1_content_site_templates__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/marketplace/site-templates":{"get":{"tags":["Content"],"summary":"Browse marketplace site templates (alias)","description":"Alias of GET /content/site-templates. Same query surface.","operationId":"list_marketplace_site_templates_api_v1_content_marketplace_site_templates_get","parameters":[{"name":"industry","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Industry"}},{"name":"use_case","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Use Case"}},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Tag"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"mood","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"title":"Mood"}},{"name":"palette","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"title":"Palette"}},{"name":"brand_fit","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"title":"Brand Fit"}},{"name":"scene_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Scene Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/marketplace/category-counts":{"get":{"tags":["Content"],"summary":"Discover marketplace categories from live DB","description":"Returns every DISTINCT `marketplace_category` value present in the global published catalog, with row counts. Powers the frontend's auto-discovery layer so a new value added via PATCH is immediately visible in the UI without a registry source change.","operationId":"list_marketplace_category_counts_api_v1_content_marketplace_category_counts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/marketplace/search":{"get":{"tags":["Content"],"summary":"Cross-table marketplace search","description":"UNION search across content_bg_videos + content_components + content_site_templates with universal-axis filters. Public read. Returns a flat list of MarketplaceSearchItem rows projected to a common shape so agents can ``group_by(.asset_type)``. Backed by the GIN bitmap-AND indexes installed in migration 177; passing any of the controlled-vocab axes (mood / brand_fit / scene_type) is recommended to keep latency under 100ms even on the full catalog.","operationId":"search_marketplace_api_v1_content_marketplace_search_get","parameters":[{"name":"asset_types","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":3,"description":"Filter to specific asset tables. Allowed values: bg_video, component, site_template. Empty = all 3.","title":"Asset Types"},"description":"Filter to specific asset tables. Allowed values: bg_video, component, site_template. Empty = all 3."},{"name":"mood","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value mood filter (set-overlap).","title":"Mood"},"description":"Multi-value mood filter (set-overlap)."},{"name":"palette","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"title":"Palette"}},{"name":"brand_fit","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"title":"Brand Fit"}},{"name":"scene_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Scene Type"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":20,"title":"Limit"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketplaceSearchResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/marketplace/components/{slug}":{"get":{"tags":["Content"],"summary":"Get a marketplace component by slug","description":"Fetch a single is_global marketplace component by slug. Public read. 404 includes did_you_mean close-match suggestions.","operationId":"get_marketplace_component_api_v1_content_marketplace_components__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/data-sources":{"get":{"tags":["Content"],"summary":"List public data sources","description":"Read-side projection of the content_data_sources registry. Used by the page editor's Source Picker (Phase E) and the agent help endpoint. Each row describes a dynamic-block source: posts, authors, categories, tags, idap.countries, idap.cities, idap.streets, idap.businesses, idap.lead. Hierarchical sources expose a parent_id.","operationId":"list_data_sources_api_v1_content_data_sources_get","parameters":[{"name":"parent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"If provided, return only sources whose parent_id matches (hierarchy walk).","title":"Parent Id"},"description":"If provided, return only sources whose parent_id matches (hierarchy walk)."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataSourceListResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/data-sources/{source_id}/aggregate":{"get":{"tags":["Content"],"summary":"Aggregate a data source into chart buckets","description":"Marketplace V2 W5.3 — backs the dynamic `chart` block.  Returns an array of `[{label, value}, ...]` produced by aggregating the registered data source per the query params.  Tenant isolation: client_id resolved from the X-Content-Domain header (same as all other public content endpoints).\n\nDefence-in-depth: max_items is capped at 500 here AND in the SQL layer (server-side LIMIT clause).  Pydantic enforces the same ceiling on inbound block validation.  group_by_field + value_field are validated against the source's declared field types via `data_source_registry`.","operationId":"aggregate_data_source_api_v1_content_data_sources__source_id__aggregate_get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"group_by","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Source-schema field id to group buckets by.","title":"Group By"},"description":"Source-schema field id to group buckets by."},{"name":"agg","in":"query","required":false,"schema":{"type":"string","pattern":"^(count|sum|avg)$","description":"Aggregation function.","default":"count","title":"Agg"},"description":"Aggregation function."},{"name":"value_field","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Required when agg in (sum, avg); ignored otherwise.","title":"Value Field"},"description":"Required when agg in (sum, avg); ignored otherwise."},{"name":"max_items","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max buckets to return.  Hard-capped at 500 in SQL too.","default":50,"title":"Max Items"},"description":"Max buckets to return.  Hard-capped at 500 in SQL too."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"description":"Optional response format.  Defaults to JSON.","title":"Format"},"description":"Optional response format.  Defaults to JSON."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataSourceAggregateResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/data-sources/{source_id}/items":{"get":{"tags":["Content"],"summary":"List items from a registered data source","description":"Backs the dynamic `list` / `item` blocks and any `kind='dynamic'` component bound to a collection. Returns an array of records from the registered source, filtered/sorted/paginated per the query params. Tenant isolation: client_id resolved from the X-Content-Domain header (same as all other public content endpoints); published content only.\n\nFilters are passed as arbitrary query params matching the source's `schema_json.filters` (e.g. `?tag=news&category=...`). `sort` is a single field with an optional `:asc|:desc` suffix; `limit` (1-500) + `offset` paginate; `fields=slug,title` projects. v1 sources: posts, authors, categories, tags, changelog. All `idap.*` ship in Phase 2 → 501 (the idap.* guard runs before the singleton check, so `idap.lead` also → 501, not 422); it reaches /lp/ templates as `lead`. 422 is reserved for a future NON-idap singleton source.","operationId":"list_data_source_items_api_v1_content_data_sources__source_id__items_get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max records to return.","default":50,"title":"Limit"},"description":"Max records to return."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset.","default":0,"title":"Offset"},"description":"Pagination offset."},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Sort field, optional `:asc|:desc` (or `-field`) suffix.","title":"Sort"},"description":"Sort field, optional `:asc|:desc` (or `-field`) suffix."},{"name":"fields","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Comma-separated field ids to include; omit for all fields.","title":"Fields"},"description":"Comma-separated field ids to include; omit for all fields."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"description":"Optional response format. Defaults to JSON.","title":"Format"},"description":"Optional response format. Defaults to JSON."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataSourceItemsResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/data-sources/{source_id}":{"get":{"tags":["Content"],"summary":"Get one data source by id","description":"Fetch a single content_data_sources row by id. 404 with did_you_mean suggestions when the id doesn't exist.","operationId":"get_data_source_api_v1_content_data_sources__source_id__get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataSourceResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/marketplace/components":{"get":{"tags":["Content"],"summary":"Browse marketplace components","description":"Browse the SpiderIQ section library. Public read — no auth. Phase D adds 5 universal-axis filters (mood, palette, brand_fit, scene_type, agent_meta.<key>) so agents can narrow by tonal / industry / behavioural axes. The vocabulary for each axis is served by GET /content/help → marketplace.universal_axes.","operationId":"list_marketplace_components_api_v1_content_marketplace_components_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filter by marketplace_category (hero, features, pricing, social-proof, content, forms, team, footer, header, cta, faq).","title":"Category"},"description":"Filter by marketplace_category (hero, features, pricing, social-proof, content, forms, team, footer, header, cta, faq)."},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Surface featured-only sections.","title":"Is Featured"},"description":"Surface featured-only sections."},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Tag filter (single tag).","title":"Tag"},"description":"Tag filter (single tag)."},{"name":"owner_client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Filter to components owned by this client_id (UUID or short-id). When set, returns ONLY rows owned by that client (system-namespace components are excluded). Defaults to None = include both system + brand-owned components, current behavior. Used by marketplace authoring brands to audit just-their-own counts (Antigravity Status Report #3, 2026-05-11).","title":"Owner Client Id"},"description":"Filter to components owned by this client_id (UUID or short-id). When set, returns ONLY rows owned by that client (system-namespace components are excluded). Defaults to None = include both system + brand-owned components, current behavior. Used by marketplace authoring brands to audit just-their-own counts (Antigravity Status Report #3, 2026-05-11)."},{"name":"mood","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value mood filter (set-overlap). See /content/help.","title":"Mood"},"description":"Multi-value mood filter (set-overlap). See /content/help."},{"name":"palette","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value palette filter (set-overlap).","title":"Palette"},"description":"Multi-value palette filter (set-overlap)."},{"name":"brand_fit","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value industry-fit filter (set-overlap).","title":"Brand Fit"},"description":"Multi-value industry-fit filter (set-overlap)."},{"name":"scene_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Single-value scene/intent filter.","title":"Scene Type"},"description":"Single-value scene/intent filter."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"description":"Optional response format. Defaults to JSON.","title":"Format"},"description":"Optional response format. Defaults to JSON."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/marketplace/bg-videos":{"get":{"tags":["Content"],"summary":"Browse marketplace background videos","description":"List curated background videos. Public read — no auth. Phase D adds 5 universal-axis filters (mood, palette, brand_fit, scene_type, agent_meta.<key>). Vocabulary served by /content/help.","operationId":"list_marketplace_bg_videos_api_v1_content_marketplace_bg_videos_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"nature | city | abstract | food | tech | people","title":"Category"},"description":"nature | city | abstract | food | tech | people"},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Tag"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"mood","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value mood filter (set-overlap).","title":"Mood"},"description":"Multi-value mood filter (set-overlap)."},{"name":"palette","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value palette filter (set-overlap).","title":"Palette"},"description":"Multi-value palette filter (set-overlap)."},{"name":"brand_fit","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Multi-value industry-fit filter (set-overlap).","title":"Brand Fit"},"description":"Multi-value industry-fit filter (set-overlap)."},{"name":"scene_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Single-value scene/intent filter.","title":"Scene Type"},"description":"Single-value scene/intent filter."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/marketplace/bg-videos/{slug}":{"get":{"tags":["Content"],"summary":"Get a marketplace background video by slug","description":"Fetch a single bg-video by slug. Public read — no auth. 404 includes ``did_you_mean`` with closest-match slugs so an agent that hallucinates a slug gets a deterministic recovery path.","operationId":"get_marketplace_bg_video_api_v1_content_marketplace_bg_videos__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/proof/recent-sales":{"get":{"tags":["Content"],"summary":"Recent customer events (PII-filtered)","description":"Returns recent customer events for the toast component `sys-proof-recent-sales-toast`. Tenants publish events as posts in a category (default slug `recent-sales`) with structured `custom_fields = {first_name, city, kind}`. The endpoint NEVER emits last_name, email, phone, post body, title, or excerpt — only first_name + city + kind + occurred_at. Rows where first_name OR city is empty are dropped server-side. Window: events younger than `min_age_minutes` are suppressed; events older than `max_age_hours` are dropped.","operationId":"get_recent_sales_api_v1_content_proof_recent_sales_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9-]+$","description":"Post-category slug to read events from.","default":"recent-sales","title":"Category"},"description":"Post-category slug to read events from."},{"name":"min_age_minutes","in":"query","required":false,"schema":{"type":"integer","maximum":1440,"minimum":0,"description":"Suppress events younger than this many minutes (default 5).","default":5,"title":"Min Age Minutes"},"description":"Suppress events younger than this many minutes (default 5)."},{"name":"max_age_hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"Drop events older than this many hours (default 72).","default":72,"title":"Max Age Hours"},"description":"Drop events older than this many hours (default 72)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecentSalesResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/visitor-geo":{"get":{"tags":["Content"],"summary":"Reflect Cloudflare visitor-geo headers","description":"Returns the visitor's country / city / region / timezone as provided by Cloudflare's edge headers (`CF-IPCountry`, `CF-IPCity`, `CF-Region`, `CF-Timezone`). NO database lookup, NO IP storage — this is a stateless reflection of the edge enrichment plan. Country may be `null` when the request did not transit Cloudflare (local dev) or when CF returned `XX` / `T1` (Tor / unknown). City and region are nullable on free CF zones.","operationId":"get_visitor_geo_api_v1_content_visitor_geo_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisitorGeoResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/pages":{"get":{"tags":["Content"],"summary":"List Pages","description":"List all published marketing pages. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_pages_api_v1_content_pages_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/pages/{slug}":{"get":{"tags":["Content"],"summary":"Get Page","description":"Get a published page by slug. Use ?format=yaml|md for agent-friendly responses.","operationId":"get_page_api_v1_content_pages__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/posts":{"get":{"tags":["Content"],"summary":"List Posts","description":"List all published blog posts. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_posts_api_v1_content_posts_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/posts/featured":{"get":{"tags":["Content"],"summary":"List Featured Posts","description":"List featured published posts. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_featured_posts_api_v1_content_posts_featured_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/posts/search":{"get":{"tags":["Content"],"summary":"Search Posts","description":"Full-text search across published posts. Use ?format=yaml|md for agent-friendly responses.","operationId":"search_posts_api_v1_content_posts_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/posts/{slug}":{"get":{"tags":["Content"],"summary":"Get Post","description":"Get a published blog post by slug. Increments view count. Use ?format=yaml|md for agent-friendly responses.","operationId":"get_post_api_v1_content_posts__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/categories":{"get":{"tags":["Content"],"summary":"List Categories","description":"List all blog categories. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_categories_api_v1_content_categories_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/tags":{"get":{"tags":["Content"],"summary":"List Tags","description":"List all tags with post counts. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_tags_api_v1_content_tags_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/authors":{"get":{"tags":["Content"],"summary":"List Authors","description":"List all active authors. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_authors_api_v1_content_authors_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/authors/{slug}":{"get":{"tags":["Content"],"summary":"Get Author","description":"Get an author by slug (public profile). Use ?format=yaml|md for agent-friendly responses.","operationId":"get_author_api_v1_content_authors__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/components":{"get":{"tags":["Content"],"summary":"List Components","description":"List published components available for the site.\nIncludes global (system) components and client-specific published components.\n\nWhen `slugs` is provided, returns only the latest published version of each\nrequested slug — ignoring `page`/`page_size` caps since the caller is asking\nfor a known bounded set.","operationId":"list_components_api_v1_content_components_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by component category","title":"Category"},"description":"Filter by component category"},{"name":"slugs","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated list of component slugs to fetch (batch). Used by the Liquid renderer's per-page prefetch so it can grab all referenced components in one round-trip regardless of pagination / total count.","title":"Slugs"},"description":"Comma-separated list of component slugs to fetch (batch). Used by the Liquid renderer's per-page prefetch so it can grab all referenced components in one round-trip regardless of pagination / total count."},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/components/{slug}":{"get":{"tags":["Content"],"summary":"Get Component","description":"Get a published component by slug. Used by the Liquid renderer Worker\nat render time to fetch component template + CSS + props schema.","operationId":"get_component_api_v1_content_components__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Specific version (default: latest published)","title":"Version"},"description":"Specific version (default: latest published)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/cdn-allowlist":{"get":{"tags":["Content"],"summary":"List Cdn Allowlist","description":"List active CDN libraries available for component dependencies.\nAI agents and the renderer use this to discover available libraries.\nNo authentication required.","operationId":"list_cdn_allowlist_api_v1_content_cdn_allowlist_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CdnAllowlistListResponse"}}}}}}},"/api/v1/content/docs/tree":{"get":{"tags":["Content"],"summary":"Get Docs Tree","description":"Get the full documentation tree structure.\nClient resolved from request domain.\n\nDocs Platform v2 · 3.4 — ``?version=`` scopes the tree to one docs version\n(omit → the tenant's default version). The response carries the tenant's\npublished ``versions`` + the resolved ``current_version`` so the chrome can\nbuild the switcher from this single call.","operationId":"get_docs_tree_api_v1_content_docs_tree_get","parameters":[{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocTreeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/versions":{"get":{"tags":["Content"],"summary":"Get Doc Versions","description":"List a tenant's published docs versions for the chrome switcher (3.4).\n\nFixed-path route — MUST stay ABOVE /docs/{path:path} (the catch-all would\notherwise match path='versions'). Same constraint as /docs/search.","operationId":"get_doc_versions_api_v1_content_docs_versions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionsResponse"}}}}}}},"/api/v1/content/docs/event":{"post":{"tags":["Content"],"summary":"Capture Doc Event","description":"Docs analytics beacon (3.4) — fire-and-forget, returns 202 immediately.\n\nThe capture insert runs in a background task so it never blocks the response\n(the doc.liquid beacon does not await meaningful work). Capture failures are\nswallowed inside the service. doc.liquid only ever sends ``view``; search /\nask are captured server-side in their own handlers.\n\nFixed-path route — MUST stay ABOVE /docs/{path:path}.","operationId":"capture_doc_event_api_v1_content_docs_event_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocEventRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/search":{"get":{"tags":["Content"],"summary":"Search Docs","description":"Full-text search across the tenant's published docs.\n\nFree (not metered) keyword search over title + body_text, scoped to the\nresolved tenant and status='published', excluding section rows. Returns\nranked hits with ts_headline snippets: {title, full_path, section_title,\nsnippet}. Consumed by the docs chrome search box (task 1.4).","operationId":"search_docs_api_v1_content_docs_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":200,"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/semantic":{"get":{"tags":["Content"],"summary":"Semantic Docs","description":"Semantic (vector) search across the tenant's published docs (3.2).\n\nEmbeds the query through SpiderGate and returns the top-k most similar\nchunks with their source doc paths — meaning-based retrieval, the half of\nask-the-docs exposed on its own for agents/clients that want raw passages\nrather than a synthesized answer. Per-IP rate-limited (embeddings are paid).","operationId":"semantic_docs_api_v1_content_docs_semantic_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":500,"title":"Q"}},{"name":"top_k","in":"query","required":false,"schema":{"type":"integer","maximum":12,"minimum":1,"default":6,"title":"Top K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/ask":{"post":{"tags":["Content"],"summary":"Ask Docs","description":"Ask-the-docs (3.2): a grounded AI answer over the tenant's published docs.\n\nRetrieves the top-k relevant passages (semantic search), then asks a\nSpiderGate completion to answer using ONLY those passages, returning the\nanswer plus the cited source links (``sources[n]`` aligns with the ``[n]``\ncitations in the answer). METERED — all LLM calls route through SpiderGate\n(``service_type='docs_ai'``). The Docs-Pro entitlement + ``docs_ai`` quota\nare wired in task 3.5 via ``check_docs_ai_quota`` (a no-op seam here). Per-IP\nrate-limited.","operationId":"ask_docs_api_v1_content_docs_ask_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocAskRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/mcp":{"post":{"tags":["Content"],"summary":"Docs Mcp","description":"Hosted, per-tenant remote MCP server over this site's published docs\n(Docs Platform v2 · 3.3 — the headline moat).\n\nStreamable-HTTP transport: a single JSON-RPC 2.0 endpoint. Any external AI\nagent (Claude / ChatGPT / Cursor) points its MCP client at\n``https://<docs-domain>/api/v1/content/docs/mcp`` and gets four read-only,\ntenant-scoped, published-only tools: ``search_docs``,\n``semantic_search_docs``, ``ask_docs`` (metered via the 3.5-M trusted\ninternal path), ``get_doc``. Tenant resolved from ``X-Content-Domain`` like\nevery other ``/content/docs/*`` route. The server never initiates messages,\nso each POST gets a plain JSON response (no SSE needed); a request that is\nall notifications gets ``202 Accepted`` with no body.","operationId":"docs_mcp_api_v1_content_docs_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/docs/{path}":{"get":{"tags":["Content"],"summary":"Get Doc","description":"Get a documentation page by full path.\nPath can be nested (e.g., \"api/authentication/oauth\").\n\nDocs Platform v2 · 3.4 — ``?version=`` selects a docs version (omit → the\ntenant's default). A page missing in a non-default version falls back to the\ndefault version's page rather than 404ing (graceful \"only in latest\").","operationId":"get_doc_api_v1_content_docs__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/navigation/{location}":{"get":{"tags":["Content"],"summary":"Get Navigation","description":"Get navigation menu by location.\nLocations: header, footer, docs_sidebar","operationId":"get_navigation_api_v1_content_navigation__location__get","parameters":[{"name":"location","in":"path","required":true,"schema":{"type":"string","title":"Location"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/settings":{"get":{"tags":["Content"],"summary":"Get Settings","description":"Get site-wide content settings.\nIncludes branding, social links, analytics IDs.","operationId":"get_settings_api_v1_content_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSettingsResponse"}}}}}}},"/api/v1/content/members/jwks":{"get":{"tags":["Content"],"summary":"Get Members Jwks","description":"Public, read-only proxy for the members sidecar's JWKS (§7.6 bridge).\n\nThe members sidecar (apps/auth-members) is loopback-bound + firewalled\n(§15 R1), so the CF edge — which cannot reach 127.0.0.1 — cannot fetch its\nJWKS directly. The api-gateway CAN reach `auth-members:3002` over the docker\nnetwork, so it proxies the sidecar's `/api/auth/jwks` here. The renderer\nfetches THIS and caches it in Worker KV (TTL + `kid`-rotation). No tenant\ncontext — the keyset is global to the members system.","operationId":"get_members_jwks_api_v1_content_members_jwks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/members/gate-context":{"get":{"tags":["Content"],"summary":"Get Members Gate Context","description":"Per-request context the edge gate needs (project id + the two flags).\n\nResolves the request domain → its project's public id (`proj_…`) for the\n§15 R4 `resolve_project(host) == jwt.project_id` assertion, plus the\nper-tenant `edge_auth_enabled` flag and the global `edge_auth_global`\nkill-switch. The renderer fetches this only when a page's `access` is\nnon-public (the public path never calls it → R6 no-op).\n\nFail-open posture: if the domain has no resolvable project the response is\ninert (project_id=null, edge_auth_enabled=false) and the edge renders the\npage ungated. This is safe because the renderer must ALREADY have resolved\nthe same domain to fetch the page at all — a gate-context miss therefore\nonly co-occurs with a page-fetch miss (the page wouldn't render either way).\nThe global flag is always returned so an incident flip bypasses everything.","operationId":"get_members_gate_context_api_v1_content_members_gate_context_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberGateContextResponse"}}}}}}},"/api/v1/content/members/data-sources/{source_id}/items":{"get":{"tags":["Content"],"summary":"Get Member Data Items","description":"Member-scoped, Data-Restriction-filtered records for a gated page.\n\n401 — no/invalid/expired member JWT. 403 — valid token for a DIFFERENT\nproject (§15 R4). 404 — host not resolvable to a project, or a source with no\nv1 enforcement adapter. 200 — the member's permitted rows only.","operationId":"get_member_data_items_api_v1_content_members_data_sources__source_id__items_get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/members/auth/{auth_path}":{"post":{"tags":["Content"],"summary":"Proxy Members Auth","description":"Public, allowlisted reverse-proxy for the members sidecar's WRITE auth\nendpoints (§C2.5 same-host sign-in bridge). Forwards method/body/cookies to\n`auth-members:3002/api/auth/*` and passes `Set-Cookie` through unchanged.\n\nA path not on the public allowlist → 404 (admin/org/token are never exposed).","operationId":"proxy_members_auth_api_v1_content_members_auth__auth_path__post","parameters":[{"name":"auth_path","in":"path","required":true,"schema":{"type":"string","title":"Auth Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content"],"summary":"Proxy Members Auth","description":"Public, allowlisted reverse-proxy for the members sidecar's WRITE auth\nendpoints (§C2.5 same-host sign-in bridge). Forwards method/body/cookies to\n`auth-members:3002/api/auth/*` and passes `Set-Cookie` through unchanged.\n\nA path not on the public allowlist → 404 (admin/org/token are never exposed).","operationId":"proxy_members_auth_api_v1_content_members_auth__auth_path__post","parameters":[{"name":"auth_path","in":"path","required":true,"schema":{"type":"string","title":"Auth Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"options":{"tags":["Content"],"summary":"Proxy Members Auth","description":"Public, allowlisted reverse-proxy for the members sidecar's WRITE auth\nendpoints (§C2.5 same-host sign-in bridge). Forwards method/body/cookies to\n`auth-members:3002/api/auth/*` and passes `Set-Cookie` through unchanged.\n\nA path not on the public allowlist → 404 (admin/org/token are never exposed).","operationId":"proxy_members_auth_api_v1_content_members_auth__auth_path__post","parameters":[{"name":"auth_path","in":"path","required":true,"schema":{"type":"string","title":"Auth Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/redirects/check":{"get":{"tags":["Content"],"summary":"Check Redirect","description":"Check if a path has an active redirect.\nUsed by Next.js middleware for redirect handling.","operationId":"check_redirect_api_v1_content_redirects_check_get","parameters":[{"name":"path","in":"query","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/playbook":{"get":{"tags":["Content"],"summary":"Get Playbook","description":"Look up the canonical tool-sequence for a stated goal (Tier 4.5).\n\nIndex (no intent) is ~2 KB YAML — safe to call on every session start.\nFull recipe for one task is typically 500-1500 bytes.","operationId":"get_playbook_api_v1_content_playbook_get","parameters":[{"name":"intent","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Exact task key OR natural-language goal","title":"Intent"},"description":"Exact task key OR natural-language goal"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md|json)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/directory/categories":{"get":{"tags":["Content"],"summary":"List Directory Categories","description":"List every directory category for this tenant.","operationId":"list_directory_categories_api_v1_content_directory_categories_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/directory/categories/{category_slug}":{"get":{"tags":["Content"],"summary":"Get Directory Category","description":"Single category + the list of cities with listings inside it.","operationId":"get_directory_category_api_v1_content_directory_categories__category_slug__get","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/directory/categories/{category_slug}/cities/{city_slug}":{"get":{"tags":["Content"],"summary":"Get Directory City","description":"All listings in (category, city). 404 if the combo has zero published listings.","operationId":"get_directory_city_api_v1_content_directory_categories__category_slug__cities__city_slug__get","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}},{"name":"city_slug","in":"path","required":true,"schema":{"type":"string","title":"City Slug"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/directory/categories/{category_slug}/cities/{city_slug}/{listing_slug}":{"get":{"tags":["Content"],"summary":"Get Directory Listing","description":"Single listing detail. city_slug is validated against the listing's actual city_slug.","operationId":"get_directory_listing_api_v1_content_directory_categories__category_slug__cities__city_slug___listing_slug__get","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}},{"name":"city_slug","in":"path","required":true,"schema":{"type":"string","title":"City Slug"}},{"name":"listing_slug","in":"path","required":true,"schema":{"type":"string","title":"Listing Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/sitemap.xml":{"get":{"tags":["Content"],"summary":"Get Sitemap","description":"Generate XML sitemap for all published content.\nIncludes pages, blog posts, and documentation.","operationId":"get_sitemap_api_v1_content_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/content/sitemap":{"get":{"tags":["Content"],"summary":"Get Sitemap Json","description":"Get sitemap data as JSON (for programmatic access).","operationId":"get_sitemap_json_api_v1_content_sitemap_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SitemapResponse"}}}}}}},"/api/v1/content/feed.xml":{"get":{"tags":["Content"],"summary":"Get Rss Feed","description":"RSS 2.0 feed of published posts (sys-rss-feed extension).\n\nEmpty post list serves a valid empty channel — anti-hallucination per\ncatalog/LEARNINGS.md: never return 500 from a content extension surface,\neven when the tenant has no published posts.","operationId":"get_rss_feed_api_v1_content_feed_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/atom.xml":{"get":{"tags":["Content"],"summary":"Get Atom Feed","description":"Atom 1.0 feed of published posts (sys-atom-feed extension).","operationId":"get_atom_feed_api_v1_content_atom_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/feed.json":{"get":{"tags":["Content"],"summary":"Get Json Feed","description":"JSON Feed 1.1 of published posts (sys-feed-json extension).","operationId":"get_json_feed_api_v1_content_feed_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/changelog":{"get":{"tags":["Content"],"summary":"List Public Changelog","description":"Published changelog entries, newest first (drafts never exposed).","operationId":"list_public_changelog_api_v1_content_changelog_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max entries (audit §9 pagination).","default":50,"title":"Limit"},"description":"Max entries (audit §9 pagination)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/changelog/feed.xml":{"get":{"tags":["Content"],"summary":"Get Changelog Rss","description":"RSS 2.0 feed of published changelog entries (per-IP rate-limited).","operationId":"get_changelog_rss_api_v1_content_changelog_feed_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/changelog/atom.xml":{"get":{"tags":["Content"],"summary":"Get Changelog Atom","description":"Atom 1.0 feed of published changelog entries (per-IP rate-limited).","operationId":"get_changelog_atom_api_v1_content_changelog_atom_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/robots.txt":{"get":{"tags":["Content"],"summary":"Get Robots Txt","description":"Generate /robots.txt for the requesting tenant.\n\nReads ``content_settings.extensions.robots``:\n- ``enabled=false`` → falls back to legacy minimal output.\n- ``rules=[]`` → emits one ``User-agent: * Allow: /`` group.\n- ``auto_sitemap_link=true`` → appends ``Sitemap: <origin>/sitemap.xml``.\n- ``extra_lines=[…]`` → emitted verbatim before the sitemap line.","operationId":"get_robots_txt_api_v1_content_robots_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/content/opensearch.xml":{"get":{"tags":["Content"],"summary":"Get Opensearch Xml","description":"OpenSearch 1.1 description document (sys-opensearch-xml extension).\n\nReads ``content_settings.extensions.opensearch`` for short_name /\ndescription / search URL template / image overrides. Falls back to\n``site_name`` / ``site_tagline`` / ``favicon_url`` when fields are unset\nso a fresh tenant still gets a browser-registerable description.\n\n``enabled=false`` in config → 410 Gone (browsers stop offering the\nsearch-engine registration without retrying).","operationId":"get_opensearch_xml_api_v1_content_opensearch_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/content/llms.txt":{"get":{"tags":["Content"],"summary":"Get Llms Txt","description":"llms.txt for LLM crawlers (sys-llms-txt extension).\n\nStructure preset selected via ``content_settings.extensions.llms_txt.structure``:\none of ``minimal`` | ``blog-only`` | ``fastapi-style`` (default) |\n``mintlify``. ``include_authors`` appends post authors to bullets.\n``max_items_per_section`` is clamped to [1, 500].\n\nEach preset only fetches the data it needs:\n- minimal       → docs tree only\n- blog-only     → posts only\n- fastapi-style → pages + posts + docs\n- mintlify      → docs + posts (sectionable docs preferred)\n\n``enabled=false`` in config → 410 Gone.","operationId":"get_llms_txt_api_v1_content_llms_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/content/llms-full.txt":{"get":{"tags":["Content"],"summary":"Get Llms Full Txt","description":"llms-full.txt — the full Markdown body of a tenant's content (SEO 2.3).\n\nWhere ``/llms.txt`` is an INDEX (titles + links), this is the CONTENT:\nevery published, indexable page/post/doc serialized to Markdown and\nconcatenated in sitemap order (pages → posts → docs), each as\n``## {title}\\nSource: {url}\\n\\n{markdown}``.\n\nOPT-IN per tenant via ``content_settings.extensions.llms_txt.full_enabled``\n(default FALSE). When the parent ``llms_txt`` surface is disabled, or\n``full_enabled`` is off, this returns **404** — the full corpus is an\nexplicit choice, not an always-on surface.\n\nSize governance (NO silent truncation): the body is capped by\n``full_max_items`` and ``full_max_bytes`` (both clamped server-side); when\ncontent is dropped, a trailing ``> (truncated: N of M pages …)`` marker is\nappended. Hard-cached (crawler-facing, low-traffic).","operationId":"get_llms_full_txt_api_v1_content_llms_full_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/content/leads/resolve":{"get":{"tags":["Content"],"summary":"Resolve Lead","description":"Resolve a lead/business by external identifier for dynamic landing pages.\n\nUsed by the Liquid renderer to fetch lead data at render time.\nResolves the client from X-Content-Domain header (same as all content endpoints).\nReturns the full business record with optional includes.","operationId":"resolve_lead_api_v1_content_leads_resolve_get","parameters":[{"name":"place_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Google Place ID (e.g. 0x47e66fdad6f1cc73:0x341211b3fccd79e1)","title":"Place Id"},"description":"Google Place ID (e.g. 0x47e66fdad6f1cc73:0x341211b3fccd79e1)"},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Domain name","title":"Domain"},"description":"Domain name"},{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Email address","title":"Email"},"description":"Email address"},{"name":"pin_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"VayaPin pin name — resolves the business linked to this pin","title":"Pin Name"},"description":"VayaPin pin name — resolves the business linked to this pin"},{"name":"pin_data_set_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"VayaPin pin data-set ID — resolves the linked business","title":"Pin Data Set Id"},"description":"VayaPin pin data-set ID — resolves the linked business"},{"name":"account_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"VayaPin account ID — resolves the linked business","title":"Account Id"},"description":"VayaPin account ID — resolves the linked business"},{"name":"pin_subscription_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"VayaPin pin subscription ID — resolves the linked business","title":"Pin Subscription Id"},"description":"VayaPin pin subscription ID — resolves the linked business"},{"name":"vat","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"VAT number — resolves the business linked to this registry record","title":"Vat"},"description":"VAT number — resolves the business linked to this registry record"},{"name":"lei","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"Legal Entity Identifier (LEI) — resolves the linked business","title":"Lei"},"description":"Legal Entity Identifier (LEI) — resolves the linked business"},{"name":"tax_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"Tax identification number — resolves the linked business","title":"Tax Id"},"description":"Tax identification number — resolves the linked business"},{"name":"registration_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"null"}],"description":"Company registration number — resolves the linked business","title":"Registration Number"},"description":"Company registration number — resolves the linked business"},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types (emails,phones,domains,contacts)","title":"Include"},"description":"Comma-separated related types (emails,phones,domains,contacts)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/forms/{form_id}/submit":{"post":{"tags":["Content"],"summary":"Submit a dynamic form-block fill","description":"Public form submission endpoint for the Marketplace V2 dynamic `form` block. `form_id` is composed as `<page_uuid>:<block_uuid>` — the service resolves the owning tenant from the page row (never from the URL or body), so the same form_id from a different tenant cannot be replayed against this site. The request body is validated against the resolved block's `props.fields` list; unknown fields are rejected. When `props.submit_url` is set the submission is also POSTed there as JSON; when `props.fallback_idap_lead` is true (default), the submission is persisted to `content_form_submissions`.","operationId":"submit_form_api_v1_content_forms__form_id__submit_post","parameters":[{"name":"form_id","in":"path","required":true,"schema":{"type":"string","title":"Form Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmitRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmitResponse"}}}},"400":{"description":"Bad request — malformed parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation failed for one of the query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}}},"/api/v1/content/vayapin/cards":{"get":{"tags":["Content"],"summary":"Get Vayapin Cards","description":"Resolve VayaPin \"cards\" for rendering on pages / blog posts.\n\nGlobal directory — NOT tenant-scoped, so no domain resolution. The Liquid\nrenderer (and agents) call this to bake card data into a page at build time.\nOnly public + listed pins are returned.\n\n- **Pinned mode** (`?pins=BB:TAPAS,BB:CHAMPERS`): exact card per named pin,\n  in the order given, with any unresolved names reported back.\n- **Query mode** (`?q=`/`?country=`/`?city=`/`?category=`): a list of cards.","operationId":"get_vayapin_cards_api_v1_content_vayapin_cards_get","parameters":[{"name":"pins","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"Comma-separated pin ids (e.g. 'BB:TAPAS,BB:CHAMPERS'). Pinned mode — resolves each named pin to its exact card.","title":"Pins"},"description":"Comma-separated pin ids (e.g. 'BB:TAPAS,BB:CHAMPERS'). Pinned mode — resolves each named pin to its exact card."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Full-text query (query mode)","title":"Q"},"description":"Full-text query (query mode)"},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"description":"2-letter pin namespace, e.g. 'bb' (query mode)","title":"Country"},"description":"2-letter pin namespace, e.g. 'bb' (query mode)"},{"name":"city","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"City / settlement filter (query mode)","title":"City"},"description":"City / settlement filter (query mode)"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"Category slug, e.g. 'restaurant' (query mode)","title":"Category"},"description":"Category slug, e.g. 'restaurant' (query mode)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Max cards in query mode","default":12,"title":"Limit"},"description":"Max cards in query mode"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VayaPinCardsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/feedback":{"post":{"tags":["Content"],"summary":"Submit Doc Feedback","description":"Record a 'was this helpful?' vote for the resolved tenant's doc.\n\nReturns 201 with a tiny ack envelope. 429 if the per-IP/tenant window is\nexceeded; 400/422 on bad input (handled by FastAPI/Pydantic); generic 500\non an internal error (never leaks the cause).","operationId":"submit_doc_feedback_api_v1_content_docs_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocFeedbackRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/docs/playground/request":{"post":{"tags":["Content"],"summary":"Playground Request","description":"Relay one user-composed request to the imported API and return its response.\n\nThe target host is fixed server-side to the imported spec's declared base URL\n(persisted on the doc), never the client's choice. Returns\n``{status, headers, body, elapsed_ms}``. 400 if the doc has no playground\nbase URL or the relay is refused (SSRF / method / size / timeout — all with a\nsafe, instructive message); 429 if rate-limited; generic 500 on an internal\nerror.","operationId":"playground_request_api_v1_content_docs_playground_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaygroundRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/marketplace/categories":{"get":{"tags":["Content — Marketplace"],"summary":"List the marketplace category vocabulary","description":"Returns every category in the vocabulary table. Public read so MCP tools, CLI, and AI agents can discover valid `marketplace_category` values without dashboard access. Pass `include_counts=true` to join live row counts from content_components (matches the existing /marketplace/category-counts endpoint shape).","operationId":"list_marketplace_categories_api_v1_content_marketplace_categories_get","parameters":[{"name":"asset_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to a specific asset class: site-template, bg-video, or component.","title":"Asset Type"},"description":"Filter to a specific asset class: site-template, bg-video, or component."},{"name":"include_counts","in":"query","required":false,"schema":{"type":"boolean","description":"When true, populates row_count with live counts from content_components.","default":false,"title":"Include Counts"},"description":"When true, populates row_count with live counts from content_components."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketplaceCategoryList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/marketplace/categories/{slug}":{"get":{"tags":["Content — Marketplace"],"summary":"Get a single marketplace category by slug","operationId":"get_marketplace_category_api_v1_content_marketplace_categories__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketplaceCategory"}}}},"404":{"description":"Slug not in the vocabulary."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/content/presence/{path}":{"get":{"tags":["Content Presence"],"summary":"Live-viewer count SSE stream for the current page","description":"Server-Sent Events stream emitting the current viewer count for `{path}` on the resolving tenant. Used by the marketplace component `sys-proof-live-viewers-pulse`. Same-origin EventSource expected (tenant resolves via the Host / X-Content-Domain header).\n\n**Query params:**\n- `sid` (required): opaque session id (UUID v4 from sessionStorage). 8–64 chars, `[A-Za-z0-9_-]`.\n- `i` (optional): polling interval in seconds. Server-clamped to `[15, 120]` (default 30).\n\n**Events:**\n- `ready` — emitted once on connect with the resolved path + interval.\n- `presence` — emitted every `i` seconds with `{count, path, interval_s}`.","operationId":"stream_presence_api_v1_content_presence__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"sid","in":"query","required":true,"schema":{"type":"string","minLength":8,"maxLength":64,"description":"Opaque per-tab session id from sessionStorage.","title":"Sid"},"description":"Opaque per-tab session id from sessionStorage."},{"name":"i","in":"query","required":false,"schema":{"type":"integer","maximum":120,"minimum":15,"description":"Polling interval (seconds).","default":30,"title":"I"},"description":"Polling interval (seconds)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages":{"get":{"tags":["Content Studio"],"summary":"List Pages","description":"List all content pages (including drafts). Use ?format=yaml|md for agent-friendly responses.","operationId":"list_pages_api_v1_dashboard_content_pages_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Page","description":"Create a new content page. Phase 11+12 dry_run/confirm_token gated.","operationId":"create_page_api_v1_dashboard_content_pages_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}":{"get":{"tags":["Content Studio"],"summary":"Get Page","description":"Get a page by ID. Use ?format=yaml|md for agent-friendly responses.\n\nP5: pass ?audit_level=off|errors|warnings|all to control the `_page_audit`\ndecoration. Default 'warnings' returns errors + warnings in the audit block.","operationId":"get_page_api_v1_dashboard_content_pages__page_id__get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}},{"name":"audit_level","in":"query","required":false,"schema":{"type":"string","pattern":"^(off|errors|warnings|all)$","description":"P5: include a `_page_audit` block on the response. 'off' skips the auditor entirely (cheapest). 'errors' / 'warnings' / 'all' filter by severity. Default 'warnings' — agent-friendly.","default":"warnings","title":"Audit Level"},"description":"P5: include a `_page_audit` block on the response. 'off' skips the auditor entirely (cheapest). 'errors' / 'warnings' / 'all' filter by severity. Default 'warnings' — agent-friendly."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Page","description":"Update a page. Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"update_page_api_v1_dashboard_content_pages__page_id__patch","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Page","description":"Archive a page (soft delete). Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"delete_page_api_v1_dashboard_content_pages__page_id__delete","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/preview":{"post":{"tags":["Content Studio"],"summary":"Page Preview","description":"Issue a short-lived iframe preview URL for the in-flight blocks the editor\nis currently showing. The URL is loaded by the Page Editor iframe; the\nLiquid renderer reads the token via ``__spideriq_preview_token`` query\nparam and substitutes ``page.blocks`` with the supplied draft before\nrendering.\n\nTokens expire in 15 minutes — the editor will refetch on focus/edit.\nLock 1 ↔ Lock 5: tenancy is enforced by validating the page belongs to\nthe caller's resolved client_id; the snapshot stored in Redis carries\nthe resolved client_id, not anything from the URL.","operationId":"page_preview_api_v1_dashboard_content_pages__page_id__preview_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagePreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Page","description":"Publish a page (creates version snapshot). Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"publish_page_api_v1_dashboard_content_pages__page_id__publish_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the publish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the publish."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Page","description":"Duplicate a page. New row: status='draft', fresh UUIDs on every block,\nauto-generated slug (or caller-provided `new_slug`). The copy is always\nowned by the caller's client — no cross-tenant duplication via this endpoint.\n\nEnables the Apr-24 catalog-triage follow-on work (template gallery,\nsection library, import adapters all build on this primitive).","operationId":"duplicate_page_api_v1_dashboard_content_pages__page_id__duplicate_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateRequest"}],"default":{},"title":"Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/blocks/{block_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Page Block","description":"Insert a deep copy of one block into the same page. Fresh UUID on the\nnew block; same data/props/component_slug as the source.","operationId":"duplicate_page_block_api_v1_dashboard_content_pages__page_id__blocks__block_id__duplicate_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"block_id","in":"path","required":true,"schema":{"type":"string","title":"Block Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateBlockRequest"}],"default":{"position":"after"},"title":"Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/insert-section":{"post":{"tags":["Content Studio"],"summary":"Insert Section Into Page Endpoint","description":"Insert a marketplace section into an existing page (Phase C).\n\nRequest body matches `InsertSectionRequest`:\n  { component_slug, component_version?, props?, position?, anchor_block_id?,\n    data_binding?, layout_id? }\n\nPhase 11+12 gated. dry_run=true returns the preview envelope; confirm_token\nconsumes it and performs the mutation.","operationId":"insert_section_into_page_endpoint_api_v1_dashboard_content_pages__page_id__insert_section_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the insertion and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the insertion and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and apply the insertion.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and apply the insertion."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."},{"name":"audit_level","in":"query","required":false,"schema":{"type":"string","pattern":"^(off|errors|warnings|all)$","description":"P5: include an `_audit` block on the success response. Default 'all' for mutations so agents see every finding immediately. 'off' is available for tight-loop scripts that bulk-insert and audit later.","default":"all","title":"Audit Level"},"description":"P5: include an `_audit` block on the success response. Default 'all' for mutations so agents see every finding immediately. 'off' is available for tight-loop scripts that bulk-insert and audit later."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Page","description":"Revert page to draft status. Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"unpublish_page_api_v1_dashboard_content_pages__page_id__unpublish_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the unpublish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the unpublish."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/versions":{"get":{"tags":["Content Studio"],"summary":"Get Page Versions","description":"List version snapshots for a page (newest first).\n\nP4: each row reports ``block_count`` + ``blocks_size`` so the editor can\nsummarise without round-tripping the full snapshot. Use\n``GET /pages/{id}/versions/{version_number}`` to fetch a single snapshot\nin full (with blocks).","operationId":"get_page_versions_api_v1_dashboard_content_pages__page_id__versions_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageVersionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/versions/{version_number}":{"get":{"tags":["Content Studio"],"summary":"Get Page Version","description":"Fetch a single page version snapshot in full (includes blocks).","operationId":"get_page_version_api_v1_dashboard_content_pages__page_id__versions__version_number__get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"version_number","in":"path","required":true,"schema":{"type":"integer","title":"Version Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageVersionDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/lock":{"post":{"tags":["Content Studio"],"summary":"Lock Page Endpoint","description":"Lock a page against further mutations.\n\nP4 — agent-surface hardening. Idempotent: re-locking refreshes the\nreason/timestamp. Any role with content-scoped access can lock; unlock\nauthorisation is more restrictive (lock-holder OR force=true with\nsuper_admin / brand_admin).","operationId":"lock_page_endpoint_api_v1_dashboard_content_pages__page_id__lock_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PageLockRequest"}],"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageLockResponse"}}}},"423":{"description":"Page already locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/unlock":{"post":{"tags":["Content Studio"],"summary":"Unlock Page Endpoint","description":"Unlock a page.\n\nAuthorisation:\n  * Anyone whose ``user_id`` matches ``locked_by_actor_id`` can unlock.\n  * super_admin / brand_admin can unlock with ``?force=true``.\n  * Otherwise: 403 (force gate) or \"Page not found / not authorised\" (200\n    with the unchanged row would be a misleading response — we 404 so\n    the caller distinguishes \"the page doesn't exist\" from \"it's locked\n    by someone else\", since the latter is itself the answer to the\n    permission question).","operationId":"unlock_page_endpoint_api_v1_dashboard_content_pages__page_id__unlock_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"super_admin / brand_admin override — unlock regardless of who locked.","default":false,"title":"Force"},"description":"super_admin / brand_admin override — unlock regardless of who locked."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageLockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/restore":{"post":{"tags":["Content Studio"],"summary":"Restore Page Version Endpoint","description":"Restore a page to a historical version snapshot.\n\nPhase 11+12 gated. ``dry_run=true`` returns a preview envelope with the\nsnapshot summary. The page lock is enforced — call with ``force=true``\n(super_admin / brand_admin) to override.","operationId":"restore_page_version_endpoint_api_v1_dashboard_content_pages__page_id__restore_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"version_number","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Which version to restore (1-indexed).","title":"Version Number"},"description":"Which version to restore (1-indexed)."},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the restore + receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the restore + receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the restore.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the restore."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PageRestoreRequest"}],"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/export":{"get":{"tags":["Content Studio"],"summary":"Export Page","description":"Self-contained page export with audit (P2 — agent-surface hardening).\n\nReturns the page row + every component referenced by ``page.blocks`` (full\nbody inlined: html_template / js / css / props_schema / dependencies /\nagent_meta / kind / layouts) + site-level settings + domains + a\n``PageAuditor`` walk over the same data.\n\nThree formats:\n  - ``format=json`` (default) — flat JSON envelope\n  - ``format=md`` — human-readable Markdown (text/markdown)\n  - ``format=archive`` — ZIP byte stream (application/zip), VSCode-extension-compatible\n\nThe page must belong to the caller's client_id (Lock 5 — same multi-tenant\nscope as ``GET /pages/{id}``).","operationId":"export_page_api_v1_dashboard_content_pages__page_id__export_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|md|archive)$","description":"json → flat JSON envelope; md → human-readable Markdown; archive → ZIP whose layout matches the VSCode extension's local registry shape (page.json / components/<slug>@<version>.json / settings.json / domains.json / audit.md / manifest.json).","default":"json","title":"Format"},"description":"json → flat JSON envelope; md → human-readable Markdown; archive → ZIP whose layout matches the VSCode extension's local registry shape (page.json / components/<slug>@<version>.json / settings.json / domains.json / audit.md / manifest.json)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts":{"get":{"tags":["Content Studio"],"summary":"List Posts","description":"List all blog posts (including drafts). Use ?format=yaml|md for agent-friendly responses.","operationId":"list_posts_api_v1_dashboard_content_posts_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category id","title":"Category"},"description":"Filter by category id"},{"name":"author_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by author (byline) id","title":"Author Id"},"description":"Filter by author (byline) id"},{"name":"created_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by record creator / agent id","title":"Created By"},"description":"Filter by record creator / agent id"},{"name":"created_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only posts published-or-created on/after this ISO timestamp (time-window filter)","title":"Created After"},"description":"Only posts published-or-created on/after this ISO timestamp (time-window filter)"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Post","description":"Create a new blog post. Phase 11+12 dry_run/confirm_token gated.\n\nUnknown fields in the request body are silently dropped by Pydantic\n(extra='ignore' default) but surfaced in `warnings[]` on the response\nwith a \"Did you mean X?\" hint. Catches the common cover_image vs\ncover_image_url / category_id vs category_ids / featured vs is_featured\nconfusions that AI agents fall into.","operationId":"create_post_api_v1_dashboard_content_posts_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}":{"get":{"tags":["Content Studio"],"summary":"Get Post","description":"Get a post by ID. Use ?format=yaml|md for agent-friendly responses.","operationId":"get_post_api_v1_dashboard_content_posts__post_id__get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Post","description":"Update a blog post. Phase 11+12 dry_run/confirm_token gated.\n\nUnknown fields in the request body are silently dropped by Pydantic\nbut surfaced in `warnings[]` on the response with a \"Did you mean X?\"\nhint. Catches the common cover_image vs cover_image_url / category_id\nvs category_ids / featured vs is_featured confusions.","operationId":"update_post_api_v1_dashboard_content_posts__post_id__patch","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Post","description":"Delete a blog post. Phase 11+12 dry_run/confirm_token gated.","operationId":"delete_post_api_v1_dashboard_content_posts__post_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Post","description":"Publish a blog post.","operationId":"publish_post_api_v1_dashboard_content_posts__post_id__publish_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Post","description":"Unpublish a blog post (set to draft).","operationId":"unpublish_post_api_v1_dashboard_content_posts__post_id__unpublish_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/schedule":{"post":{"tags":["Content Studio"],"summary":"Schedule Post","description":"Schedule a post to auto-publish at a future time (status='scheduled').\n\nThe scheduler sidecar flips it to 'published' once the time arrives. Public\nsurfaces gate on status='published', so a scheduled post stays private until\nthen. A non-future time is rejected — use /publish for immediate publish.","operationId":"schedule_post_api_v1_dashboard_content_posts__post_id__schedule_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostScheduleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/unschedule":{"post":{"tags":["Content Studio"],"summary":"Unschedule Post","description":"Cancel a scheduled publish — revert the post to draft.","operationId":"unschedule_post_api_v1_dashboard_content_posts__post_id__unschedule_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Post","description":"Duplicate a blog post. Returns the new draft with a fresh slug + UUID.","operationId":"duplicate_post_api_v1_dashboard_content_posts__post_id__duplicate_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateRequest"}],"default":{},"title":"Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/status":{"post":{"tags":["Content Studio"],"summary":"Update Post Status","description":"Update post status (draft, pending_review, published, archived).","operationId":"update_post_status_api_v1_dashboard_content_posts__post_id__status_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostStatusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/posts/{post_id}/related":{"put":{"tags":["Content Studio"],"summary":"Update Post Related","description":"Set related posts for a post.","operationId":"update_post_related_api_v1_dashboard_content_posts__post_id__related_put","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"title":"Related Post Ids"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/authors":{"get":{"tags":["Content Studio"],"summary":"List Authors","description":"List all authors. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_authors_api_v1_dashboard_content_authors_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"agent_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Type"}},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Author","description":"Create a new author.","operationId":"create_author_api_v1_dashboard_content_authors_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/authors/{author_id}":{"get":{"tags":["Content Studio"],"summary":"Get Author","description":"Get an author by ID. Use ?format=yaml|md for agent-friendly responses.","operationId":"get_author_api_v1_dashboard_content_authors__author_id__get","parameters":[{"name":"author_id","in":"path","required":true,"schema":{"type":"string","title":"Author Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Author","description":"Update an author.","operationId":"update_author_api_v1_dashboard_content_authors__author_id__patch","parameters":[{"name":"author_id","in":"path","required":true,"schema":{"type":"string","title":"Author Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Author","description":"Deactivate an author (soft-delete).","operationId":"delete_author_api_v1_dashboard_content_authors__author_id__delete","parameters":[{"name":"author_id","in":"path","required":true,"schema":{"type":"string","title":"Author Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/tags":{"get":{"tags":["Content Studio"],"summary":"List Tags","description":"List all tags. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_tags_api_v1_dashboard_content_tags_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Tag","description":"Create a new tag.","operationId":"create_tag_api_v1_dashboard_content_tags_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/tags/{tag_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Tag","description":"Update a tag.","operationId":"update_tag_api_v1_dashboard_content_tags__tag_id__patch","parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","title":"Tag Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Tag","description":"Delete a tag.","operationId":"delete_tag_api_v1_dashboard_content_tags__tag_id__delete","parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","title":"Tag Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/categories":{"get":{"tags":["Content Studio"],"summary":"List Categories","description":"List blog categories. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_categories_api_v1_dashboard_content_categories_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Category","description":"Create a new category.","operationId":"create_category_api_v1_dashboard_content_categories_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCategoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/categories/{category_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Category","description":"Update a category.","operationId":"update_category_api_v1_dashboard_content_categories__category_id__patch","parameters":[{"name":"category_id","in":"path","required":true,"schema":{"type":"string","title":"Category Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCategoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Category","description":"Delete a category.","operationId":"delete_category_api_v1_dashboard_content_categories__category_id__delete","parameters":[{"name":"category_id","in":"path","required":true,"schema":{"type":"string","title":"Category Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/tree":{"get":{"tags":["Content Studio"],"summary":"Get Docs Tree","description":"Get full docs tree (including drafts).\n\nDocs Platform v2 · 3.4 — ``?version=`` scopes the editor tree to one docs\nversion (omit → default). Response carries all versions (every status, for\nmanagement) + the resolved current_version.","operationId":"get_docs_tree_api_v1_dashboard_content_docs_tree_get","parameters":[{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocTreeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs":{"post":{"tags":["Content Studio"],"summary":"Create Doc","description":"Create a documentation page.","operationId":"create_doc_api_v1_dashboard_content_docs_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content Studio"],"summary":"List Docs","description":"Flat list of docs (metadata only, no body) — complements GET /docs/tree.\n\nAgents use this to discover a doc's UUID by title/status before fetching or\nmutating it via GET /docs/{doc_id}.","operationId":"list_docs_api_v1_dashboard_content_docs_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (draft / published / archived).","title":"Status"},"description":"Filter by status (draft / published / archived)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/versions":{"get":{"tags":["Content Studio"],"summary":"List Doc Versions","description":"List the tenant's docs versions (all statuses, for management).","operationId":"list_doc_versions_api_v1_dashboard_content_docs_versions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionsResponse"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Doc Version","description":"Register a new docs version label. ``is_default`` demotes the prior default.","operationId":"create_doc_version_api_v1_dashboard_content_docs_versions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/versions/{version_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Doc Version","description":"Update a docs version (title / order / status / default flag).","operationId":"update_doc_version_api_v1_dashboard_content_docs_versions__version_id__patch","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Doc Version","description":"Delete a docs version. Refuses the default version or one still in use.","operationId":"delete_doc_version_api_v1_dashboard_content_docs_versions__version_id__delete","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/analytics":{"get":{"tags":["Content Studio"],"summary":"Get Docs Analytics","description":"Server-side docs analytics overview (views / searches / asks) for the\ntenant over the last ``days``. Degrades to an empty rollup, never 500s.","operationId":"get_docs_analytics_api_v1_dashboard_content_docs_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/{doc_id}":{"get":{"tags":["Content Studio"],"summary":"Get Doc","description":"Get a doc by ID (drafts included).","operationId":"get_doc_api_v1_dashboard_content_docs__doc_id__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Doc","description":"Update a doc (title, body, SEO, hierarchy). Phase 11+12 dry_run/confirm_token gated.","operationId":"update_doc_api_v1_dashboard_content_docs__doc_id__patch","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Doc","description":"Archive a doc (soft delete). Phase 11+12 dry_run/confirm_token gated.","operationId":"delete_doc_api_v1_dashboard_content_docs__doc_id__delete","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/{doc_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Doc","description":"Duplicate a doc page. Copy lives as a sibling of the original under the\nsame parent_id with a fresh slug. Status: draft.","operationId":"duplicate_doc_api_v1_dashboard_content_docs__doc_id__duplicate_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateRequest"}],"default":{},"title":"Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/{doc_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Doc","description":"Publish a draft doc. Phase 11+12 dry_run/confirm_token gated.","operationId":"publish_doc_api_v1_dashboard_content_docs__doc_id__publish_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the publish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the publish."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/{doc_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Doc","description":"Revert a doc to draft status. Phase 11+12 dry_run/confirm_token gated.","operationId":"unpublish_doc_api_v1_dashboard_content_docs__doc_id__unpublish_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the unpublish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the unpublish."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/{doc_id}/export":{"get":{"tags":["Content Studio"],"summary":"Export Doc","description":"Self-contained doc export. Returns the doc row + site settings + domains.\n\nDocs are prose-first, so there are no component bodies to inline (unlike\npage export). The doc must belong to the caller's client_id.","operationId":"export_doc_api_v1_dashboard_content_docs__doc_id__export_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|md)$","description":"json → flat JSON envelope (doc + settings + domains); md → human-readable Markdown rendered from the Tiptap body.","default":"json","title":"Format"},"description":"json → flat JSON envelope (doc + settings + domains); md → human-readable Markdown rendered from the Tiptap body."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/import":{"post":{"tags":["Content Studio"],"summary":"Import Doc Markdown","description":"Import a doc from a Markdown body. Create a new doc (omit ``doc_id``,\nprovide ``slug`` + ``title``) or replace an existing doc's body (set\n``doc_id``). `:::component{...}` directives become embedded component nodes.\n\nCreate delegates to ``create_doc`` (ungated); update delegates to the\nPhase 11+12 dry_run/confirm_token-gated ``update_doc``.","operationId":"import_doc_markdown_api_v1_dashboard_content_docs_import_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12 (update path): preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12 (update path): preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12 (update path): consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12 (update path): consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocMarkdownImport"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/import-openapi":{"post":{"tags":["Content Studio"],"summary":"Import Openapi","description":"Import an OpenAPI/Swagger spec into the docs tree. `dry_run=true` returns\nthe planned section + pages without writing (powers the wizard preview);\notherwise the reference is generated idempotently and (if `publish`) published.\n\nErrors map to 422 (bad/oversized/unfetchable spec — message is client-safe)\nor 500 (logged, generic) — never leak the spec URL or upstream detail.","operationId":"import_openapi_api_v1_dashboard_content_docs_import_openapi_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiImportRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/{doc_id}/markdown":{"get":{"tags":["Content Studio"],"summary":"Export Doc Markdown","description":"Export a doc's body as pure Markdown (round-trips with POST /docs/import).\n\n`:::component{...}` directives are preserved (render_tiptap_markdown emits them\nvia serialize_component_node). The title is prepended as an H1. Distinct from\n`/docs/{id}/export?format=md`, which wraps the same body in a JSON envelope.","operationId":"export_doc_markdown_api_v1_dashboard_content_docs__doc_id__markdown_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/import":{"post":{"tags":["Content Studio"],"summary":"Import Page Markdown","description":"Import a page from a Markdown body — the markdown→page converter. Create a\nnew page (omit ``page_id``, provide ``slug`` + ``title``) or replace an\nexisting page's blocks (set ``page_id``). Prose runs become ``rich_text``\nblocks; `:::component{...}` directives become ``component`` blocks.\n\nDelegates to the Phase 11+12 dry_run/confirm_token-gated ``create_page`` /\n``update_page`` (the converter handler only does the markdown→blocks mapping).","operationId":"import_page_markdown_api_v1_dashboard_content_pages_import_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create/update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create/update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageMarkdownImport"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/pages/{page_id}/markdown":{"get":{"tags":["Content Studio"],"summary":"Export Page Markdown","description":"Export a page's blocks as pure Markdown (round-trips with POST /pages/import).\n\nComponent blocks serialize to `:::component{...}` directives (the converter\ncontract — `render_page_markdown(serialize_components=True)`). This differs\nfrom `/pages/{id}/export?format=md`, which renders each component as a labelled\nhuman-readable section inside a JSON envelope (not round-trippable).","operationId":"export_page_markdown_api_v1_dashboard_content_pages__page_id__markdown_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/docs/reorder":{"put":{"tags":["Content Studio"],"summary":"Reorder Docs","description":"Reorder documentation tree.","operationId":"reorder_docs_api_v1_dashboard_content_docs_reorder_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocReorderRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/navigation/{location}":{"get":{"tags":["Content Studio"],"summary":"Get Navigation","description":"Get navigation menu.","operationId":"get_navigation_api_v1_dashboard_content_navigation__location__get","parameters":[{"name":"location","in":"path","required":true,"schema":{"type":"string","title":"Location"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Content Studio"],"summary":"Update Navigation","description":"Update navigation menu.","operationId":"update_navigation_api_v1_dashboard_content_navigation__location__put","parameters":[{"name":"location","in":"path","required":true,"schema":{"type":"string","title":"Location"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/categories":{"get":{"tags":["Content Studio"],"summary":"Dash List Directory Categories","description":"List all directory categories for this client (includes drafts/empty ones).","operationId":"dash_list_directory_categories_api_v1_dashboard_content_directory_categories_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Dash Create Directory Category","description":"Create a directory category.\n\nBody: {name, slug?, description?, icon?, seo_title_template?,\n       seo_description_template?, template?, data_source?, sort_order?}\n\nseo_*_template strings accept {category}, {city}, {listing} placeholders —\nrendered server-side on the public read endpoints.","operationId":"dash_create_directory_category_api_v1_dashboard_content_directory_categories_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/categories/{category_slug}":{"get":{"tags":["Content Studio"],"summary":"Dash Get Directory Category","operationId":"dash_get_directory_category_api_v1_dashboard_content_directory_categories__category_slug__get","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Dash Update Directory Category","operationId":"dash_update_directory_category_api_v1_dashboard_content_directory_categories__category_slug__patch","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Dash Delete Directory Category","description":"Delete a category (cascades to its listings).","operationId":"dash_delete_directory_category_api_v1_dashboard_content_directory_categories__category_slug__delete","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/categories/{category_slug}/listings/bulk":{"post":{"tags":["Content Studio"],"summary":"Dash Bulk Upsert Listings","description":"Bulk upsert listings. Body: {listings: [{name, slug?, city, state?, ...}, ...]}.\n\nUpsert semantics: slug unique per category. Missing slug auto-generated from name.\nRefreshes the materialized view + category counts on success.\n\nTypical use: agent dumps the results of a SpiderMaps campaign into a category\nvia a single call. Supports `source_job_id` so listings can be traced back\nto the job that produced them.","operationId":"dash_bulk_upsert_listings_api_v1_dashboard_content_directory_categories__category_slug__listings_bulk_post","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/listings":{"get":{"tags":["Content Studio"],"summary":"Dash List Directory Listings","description":"List listings across categories. Filters: category_slug, city, status.","operationId":"dash_list_directory_listings_api_v1_dashboard_content_directory_listings_get","parameters":[{"name":"category_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category Slug"}},{"name":"city","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"published","title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/categories/{category_slug}/listings":{"post":{"tags":["Content Studio"],"summary":"Dash Upsert Listing","description":"Create or update a single listing in a category. Slug-based upsert.","operationId":"dash_upsert_listing_api_v1_dashboard_content_directory_categories__category_slug__listings_post","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/categories/{category_slug}/listings/{listing_slug}":{"delete":{"tags":["Content Studio"],"summary":"Dash Delete Listing","operationId":"dash_delete_listing_api_v1_dashboard_content_directory_categories__category_slug__listings__listing_slug__delete","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}},{"name":"listing_slug","in":"path","required":true,"schema":{"type":"string","title":"Listing Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/categories/{category_slug}/import-from-idap":{"post":{"tags":["Content Studio"],"summary":"Dash Import From Idap","description":"Populate a directory category from the tenant's IDAP data (norm_cli_*.businesses).\n\nOne call replaces the \"run SpiderMaps → transform results → bulk_upsert\" loop.\nThe norm_cli_* schema is already the canonical store for every business\nSpiderIQ has seen for this tenant — this reads directly from it.\n\nBody (all optional):\n  {category_filter: \"Plumber\", country_code: \"US\", city: \"Miami\",\n   rating_min: 4.0, limit: 5000}\n\nReturns {upserted, failed, source_rows, affected_cities, source_schema, filter, ...}.","operationId":"dash_import_from_idap_api_v1_dashboard_content_directory_categories__category_slug__import_from_idap_post","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/directory/refresh-stats":{"post":{"tags":["Content Studio"],"summary":"Dash Refresh Directory Stats","description":"Manually refresh the city_stats materialized view. Normally auto-refreshed on bulk upsert.","operationId":"dash_refresh_directory_stats_api_v1_dashboard_content_directory_refresh_stats_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/content/media":{"get":{"tags":["Content Studio"],"summary":"List Media","description":"List media files for the current client, newest first.\n\nFilters: `folder` (exact match), `mime_type` (prefix match — `image/` catches\nall image MIME types).","operationId":"list_media_api_v1_dashboard_content_media_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"folder","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder"}},{"name":"mime_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/media/upload":{"post":{"tags":["Content Studio"],"summary":"Upload Media","description":"Upload an image to Cloudflare R2.","operationId":"upload_media_api_v1_dashboard_content_media_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_api_v1_dashboard_content_media_upload_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/media/upload-batch":{"post":{"tags":["Content Studio"],"summary":"Upload Media Batch","description":"Upload one or many files to SpiderMedia (SeaweedFS).\n\nWeight policy — scroll-sequence folders (`scroll-sequences/*` or\n`is_scroll_sequence=true`): 500 KB per-file hard, 20 MB batch total hard,\n200 KB / 10 MB soft warnings. Everything else: 20 MB per-file (500 MB for\nvideo/*), 500 MB batch total. Hard ceilings return 400 with the offending\nfile(s); soft warnings come back in `warnings[]` with the upload succeeding.\n\nReturns {uploaded: [...], failed: [...], warnings: [...], totals}.","operationId":"upload_media_batch_api_v1_dashboard_content_media_upload_batch_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_batch_api_v1_dashboard_content_media_upload_batch_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/media/{media_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Media","description":"Update a legacy R2 media file's metadata (folder / alt_text / caption).\n\nTargets the ``content_media`` table (the dashboard R2 uploader surface), NOT\nthe per-tenant ``media_assets`` catalog. True PATCH — only supplied fields\nchange. 200 + the updated row; 404 if no such media for this client.","operationId":"update_media_api_v1_dashboard_content_media__media_id__patch","parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Media","description":"Delete a legacy R2 media file (``content_media`` row + best-effort R2\nobject cleanup). 204 on success, 404 if no such media for this client.\n\nThe DB row delete is authoritative; the R2 object removal is best-effort and\nnon-blocking — an orphaned R2 object never fails the request (the row is\nalready gone, so the asset no longer appears in the library).","operationId":"delete_media_api_v1_dashboard_content_media__media_id__delete","parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/settings":{"get":{"tags":["Content Studio"],"summary":"Get Settings","description":"Get content settings.","operationId":"get_settings_api_v1_dashboard_content_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSettingsResponse"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Settings","description":"Update content settings. Phase 11+12 dry_run/confirm_token gated.","operationId":"update_settings_api_v1_dashboard_content_settings_patch","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSettingsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/redirects":{"get":{"tags":["Content Studio"],"summary":"List Redirects","description":"List all redirects.","operationId":"list_redirects_api_v1_dashboard_content_redirects_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectListResponse"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Redirect","description":"Create a redirect.","operationId":"create_redirect_api_v1_dashboard_content_redirects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/redirects/{redirect_id}":{"delete":{"tags":["Content Studio"],"summary":"Delete Redirect","description":"Delete a redirect.","operationId":"delete_redirect_api_v1_dashboard_content_redirects__redirect_id__delete","parameters":[{"name":"redirect_id","in":"path","required":true,"schema":{"type":"string","title":"Redirect Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains":{"get":{"tags":["Content Studio"],"summary":"List Domains","description":"List all custom domains for this client plus the auto-provisioned\nplatform preview URL when the client has ever deployed.","operationId":"list_domains_api_v1_dashboard_content_domains_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainListResponse"}}}}}},"post":{"tags":["Content Studio"],"summary":"Add Domain","description":"Register a new custom domain.\n\nDrives BOTH Cloudflare onboarding paths and surfaces the outcome of each\nso the dashboard can render an actionable banner instead of silently\nmasking failures:\n\n* **CF for SaaS custom hostname** — for domains whose zone is NOT in our\n  CF account. The client adds a CNAME to the fallback origin and CF\n  handles SSL + dispatch.\n* **Worker Route on the domain's own zone** — for domains whose zone IS\n  in our CF account (incl. subdomains of zones we own). Without this,\n  KV mappings exist but CF returns 522 because no Worker handles the\n  inbound request.\n\nEach helper detects which path it owns and no-ops on the other (see\n``register_custom_hostname`` skipping in-account zones, and\n``ensure_worker_route`` skipping zones in other accounts). Both are\ncalled unconditionally; their structured return values populate\n``worker_route_status`` and ``custom_hostname_status`` on the response.","operationId":"add_domain_api_v1_dashboard_content_domains_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains/subdomain":{"post":{"tags":["Content Studio"],"summary":"Add Subdomain","description":"Register a free ``{label}.sites.spideriq.ai`` subdomain.\n\nThe zero-cost counterpart to ``add_domain``: ``sites.spideriq.ai`` is a\nzone we own with a wildcard Worker Route already configured, so this path\nmakes NO Cloudflare custom-hostname call and needs NO DNS verification —\nit writes the ``DOMAIN_MAP`` KV entry and the dispatch Worker routes the\nhostname to the client's tenant script. Available on every tier; the free\ntier's single slot is one of these.\n\nCounts against the same ``max_domains`` quota as a custom domain.","operationId":"add_subdomain_api_v1_dashboard_content_domains_subdomain_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubdomainCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains/subdomain/availability":{"get":{"tags":["Content Studio"],"summary":"Check Subdomain Availability","description":"Check whether a ``{label}.sites.spideriq.ai`` subdomain is free.\n\nPowers the live availability check in the dashboard subdomain editor.\nReturns ``{available, host, reason}``. ``available=false`` with a ``reason``\nwhen the label is malformed; otherwise reflects global host uniqueness\n(``content_domains.domain`` is globally unique).","operationId":"check_subdomain_availability_api_v1_dashboard_content_domains_subdomain_availability_get","parameters":[{"name":"label","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":63,"title":"Label"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains/{domain}":{"patch":{"tags":["Content Studio"],"summary":"Update Domain","description":"Update a domain (SD1): set its start page and/or rename the host.\n\nA rename remaps Cloudflare so the new host serves the same tenant site —\nthe new ``DOMAIN_MAP`` KV entry (and, for an out-of-account custom domain, a\nfresh custom hostname + Worker Route) is created and the old host's mapping\nis torn down. Subdomains on ``*.sites.spideriq.ai`` route via the existing\nwildcard, so they only need the KV swap. The change goes live on the next\ndeploy.","operationId":"update_domain_api_v1_dashboard_content_domains__domain__patch","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Remove Domain","description":"Remove a custom domain.","operationId":"remove_domain_api_v1_dashboard_content_domains__domain__delete","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains/{domain}/verify":{"post":{"tags":["Content Studio"],"summary":"Verify Domain","description":"Verify a domain (checks DNS TXT record).\n\nSurfaces ``needs_deploy=True`` when the client has no live deploy yet so\nthe dashboard can prompt \"Click to deploy your site\" — without that, a\nfreshly verified domain will return CF 522 because the per-client tenant\nWorker script doesn't exist in the dispatch namespace.","operationId":"verify_domain_api_v1_dashboard_content_domains__domain__verify_post","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains/{domain}/recheck":{"post":{"tags":["Content Studio"],"summary":"Recheck Domain Ssl","description":"Re-query Cloudflare for this domain's live SSL state and persist it.\n\nThe on-demand, customer-triggered counterpart to the background\nreconciliation poll: powers the dashboard \"Re-check\" button so a customer\nwho just fixed their DNS doesn't have to wait for the next poll tick. Does\nexactly ONE Cloudflare custom-hostname read for the one domain (not a fan-out\nover the whole list — that stays cheap and rate-limit-safe), maps the CF SSL\nlifecycle onto our ``ssl_status`` enum, and returns the refreshed row with\nthe live CF status echoed in ``custom_hostname_status``.","operationId":"recheck_domain_ssl_api_v1_dashboard_content_domains__domain__recheck_post","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/domains/{domain}/primary":{"post":{"tags":["Content Studio"],"summary":"Set Primary Domain","description":"Set a domain as the primary domain for this client.","operationId":"set_primary_domain_api_v1_dashboard_content_domains__domain__primary_post","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/deploy":{"post":{"tags":["Content Studio"],"summary":"Deploy Site","description":"Deploy the client's tenant site to Workers for Platforms.\n\nPhase 11+12: pass `dry_run=true` to receive a preview URL and confirm_token\nwithout deploying; pass `confirm_token=<cft_…>` to consume the prior preview\nand deploy to production. Calls without either flag keep today's immediate\nbehaviour (back-compat until Stage 4 rollout flips the default).","operationId":"deploy_site_api_v1_dashboard_content_deploy_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: issue a preview + confirm_token without deploying.","default":false,"title":"Dry Run"},"description":"Phase 11+12: issue a preview + confirm_token without deploying."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and deploy to production.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and deploy to production."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/deploy/preview":{"post":{"tags":["Content Studio"],"summary":"Deploy Site Preview","description":"Phase 11+12 Stage 2 — issue a preview URL + confirm_token for the proposed\ndeploy. No CF production script upload, no DNS flip. Caller reviews the\npreview, then calls `POST /deploy/production` with the `confirm_token` to\nactually deploy.","operationId":"deploy_site_preview_api_v1_dashboard_content_deploy_preview_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/content/deploy/production":{"post":{"tags":["Content Studio"],"summary":"Deploy Site Production","description":"Phase 11+12 Stage 2 — consume a prior preview's `confirm_token` (Lock 4)\nand run the real deploy. 403 on tenant/action/resource mismatch, 410 on\nexpired, 409 on already-consumed replay.","operationId":"deploy_site_production_api_v1_dashboard_content_deploy_production_post","parameters":[{"name":"confirm_token","in":"query","required":true,"schema":{"type":"string","description":"Token from a prior /deploy/preview call.","title":"Confirm Token"},"description":"Token from a prior /deploy/preview call."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/deploy/status":{"get":{"tags":["Content Studio"],"summary":"Get Deploy Status","description":"Get the most recent deploy status for this client.","operationId":"get_deploy_status_api_v1_dashboard_content_deploy_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployStatusResponse"}}}}}}},"/api/v1/dashboard/content/deploy/history":{"get":{"tags":["Content Studio"],"summary":"List Deploys","description":"List recent deploys for this client.","operationId":"list_deploys_api_v1_dashboard_content_deploy_history_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/deploy/readiness":{"get":{"tags":["Content Studio"],"summary":"Get Deploy Readiness","description":"Check if this client's site is ready to deploy.\n\nReturns a checklist of prerequisites (settings, domain, templates, pages)\nwith pass/fail status and actionable fix instructions.\nBlocking items prevent deploy; warnings are advisory.","operationId":"get_deploy_readiness_api_v1_dashboard_content_deploy_readiness_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployReadinessResponse"}}}}}}},"/api/v1/dashboard/content/deploy/{deploy_id}":{"get":{"tags":["Content Studio"],"summary":"Get Deploy Detail","description":"Full detail for a single deploy — backs the per-deploy detail page.\n\nClient-scoped: returns 404 if the deploy doesn't exist OR belongs to a\ndifferent tenant (no cross-tenant leakage). Computes `duration_ms` from\ndeployed_at − created_at. Declared AFTER the static /deploy/* routes so\n`{deploy_id}` never shadows status/history/readiness.","operationId":"get_deploy_detail_api_v1_dashboard_content_deploy__deploy_id__get","parameters":[{"name":"deploy_id","in":"path","required":true,"schema":{"type":"string","title":"Deploy Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/changelog":{"get":{"tags":["Content Studio"],"summary":"List Changelog","description":"List changelog entries (newest published first, then drafts).\n\nDrafts are included for the editor surface. Paginated — the public feed +\ntimeline read the same service method with include_drafts=False.","operationId":"list_changelog_api_v1_dashboard_content_changelog_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (draft / published / archived).","title":"Status"},"description":"Filter by status (draft / published / archived)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max entries to return.","default":50,"title":"Limit"},"description":"Max entries to return."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset.","default":0,"title":"Offset"},"description":"Pagination offset."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Changelog","description":"Create a draft changelog entry (409 on a duplicate version).","operationId":"create_changelog_api_v1_dashboard_content_changelog_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/changelog/{entry_id}":{"get":{"tags":["Content Studio"],"summary":"Get Changelog","description":"Get one changelog entry by ID (drafts included).","operationId":"get_changelog_api_v1_dashboard_content_changelog__entry_id__get","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Changelog","description":"Update a changelog entry's title/body (version is immutable post-create\nonly via this route's schema; pass it through update_changelog if needed).","operationId":"update_changelog_api_v1_dashboard_content_changelog__entry_id__patch","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Changelog","description":"Soft-delete a changelog entry (status → archived). Returns 204.","operationId":"delete_changelog_api_v1_dashboard_content_changelog__entry_id__delete","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/changelog/{entry_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Changelog","description":"Publish a changelog entry (fires changelog.published on first publish).","operationId":"publish_changelog_api_v1_dashboard_content_changelog__entry_id__publish_post","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/changelog/{entry_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Changelog","description":"Revert a changelog entry to draft.","operationId":"unpublish_changelog_api_v1_dashboard_content_changelog__entry_id__unpublish_post","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/audit/links":{"get":{"tags":["Content Studio"],"summary":"Audit Internal Links","description":"Walk every published page's blocks + navigation menus and validate\ninternal links against the published-page roster + active redirects.\n\nReturns `{valid_count, broken: [{path, source, reason}], proposed_redirects, known_redirects}`.\n\nFixes the Unavis-report pain: cleaning up `/en/*` legacy URLs across nav +\nblocks used to require manual crawling. One call surfaces every broken link\nwith its exact source so agents can propose redirects in a single pass.","operationId":"audit_internal_links_api_v1_dashboard_content_audit_links_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages":{"get":{"tags":["Content Studio"],"summary":"List Pages","description":"List all content pages (including drafts). Use ?format=yaml|md for agent-friendly responses.","operationId":"list_pages_api_v1_dashboard_projects__project_id__content_pages_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Page","description":"Create a new content page. Phase 11+12 dry_run/confirm_token gated.","operationId":"create_page_api_v1_dashboard_projects__project_id__content_pages_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}":{"get":{"tags":["Content Studio"],"summary":"Get Page","description":"Get a page by ID. Use ?format=yaml|md for agent-friendly responses.\n\nP5: pass ?audit_level=off|errors|warnings|all to control the `_page_audit`\ndecoration. Default 'warnings' returns errors + warnings in the audit block.","operationId":"get_page_api_v1_dashboard_projects__project_id__content_pages__page_id__get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}},{"name":"audit_level","in":"query","required":false,"schema":{"type":"string","pattern":"^(off|errors|warnings|all)$","description":"P5: include a `_page_audit` block on the response. 'off' skips the auditor entirely (cheapest). 'errors' / 'warnings' / 'all' filter by severity. Default 'warnings' — agent-friendly.","default":"warnings","title":"Audit Level"},"description":"P5: include a `_page_audit` block on the response. 'off' skips the auditor entirely (cheapest). 'errors' / 'warnings' / 'all' filter by severity. Default 'warnings' — agent-friendly."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Page","description":"Update a page. Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"update_page_api_v1_dashboard_projects__project_id__content_pages__page_id__patch","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Page","description":"Archive a page (soft delete). Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"delete_page_api_v1_dashboard_projects__project_id__content_pages__page_id__delete","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/preview":{"post":{"tags":["Content Studio"],"summary":"Page Preview","description":"Issue a short-lived iframe preview URL for the in-flight blocks the editor\nis currently showing. The URL is loaded by the Page Editor iframe; the\nLiquid renderer reads the token via ``__spideriq_preview_token`` query\nparam and substitutes ``page.blocks`` with the supplied draft before\nrendering.\n\nTokens expire in 15 minutes — the editor will refetch on focus/edit.\nLock 1 ↔ Lock 5: tenancy is enforced by validating the page belongs to\nthe caller's resolved client_id; the snapshot stored in Redis carries\nthe resolved client_id, not anything from the URL.","operationId":"page_preview_api_v1_dashboard_projects__project_id__content_pages__page_id__preview_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagePreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Page","description":"Publish a page (creates version snapshot). Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"publish_page_api_v1_dashboard_projects__project_id__content_pages__page_id__publish_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the publish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the publish."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Page","description":"Duplicate a page. New row: status='draft', fresh UUIDs on every block,\nauto-generated slug (or caller-provided `new_slug`). The copy is always\nowned by the caller's client — no cross-tenant duplication via this endpoint.\n\nEnables the Apr-24 catalog-triage follow-on work (template gallery,\nsection library, import adapters all build on this primitive).","operationId":"duplicate_page_api_v1_dashboard_projects__project_id__content_pages__page_id__duplicate_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateRequest"}],"default":{},"title":"Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/blocks/{block_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Page Block","description":"Insert a deep copy of one block into the same page. Fresh UUID on the\nnew block; same data/props/component_slug as the source.","operationId":"duplicate_page_block_api_v1_dashboard_projects__project_id__content_pages__page_id__blocks__block_id__duplicate_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"block_id","in":"path","required":true,"schema":{"type":"string","title":"Block Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateBlockRequest"}],"default":{"position":"after"},"title":"Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/insert-section":{"post":{"tags":["Content Studio"],"summary":"Insert Section Into Page Endpoint","description":"Insert a marketplace section into an existing page (Phase C).\n\nRequest body matches `InsertSectionRequest`:\n  { component_slug, component_version?, props?, position?, anchor_block_id?,\n    data_binding?, layout_id? }\n\nPhase 11+12 gated. dry_run=true returns the preview envelope; confirm_token\nconsumes it and performs the mutation.","operationId":"insert_section_into_page_endpoint_api_v1_dashboard_projects__project_id__content_pages__page_id__insert_section_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the insertion and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the insertion and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and apply the insertion.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and apply the insertion."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."},{"name":"audit_level","in":"query","required":false,"schema":{"type":"string","pattern":"^(off|errors|warnings|all)$","description":"P5: include an `_audit` block on the success response. Default 'all' for mutations so agents see every finding immediately. 'off' is available for tight-loop scripts that bulk-insert and audit later.","default":"all","title":"Audit Level"},"description":"P5: include an `_audit` block on the success response. Default 'all' for mutations so agents see every finding immediately. 'off' is available for tight-loop scripts that bulk-insert and audit later."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Page","description":"Revert page to draft status. Phase 11+12 dry_run/confirm_token gated. P4 lock-aware.","operationId":"unpublish_page_api_v1_dashboard_projects__project_id__content_pages__page_id__unpublish_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the unpublish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the unpublish."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/versions":{"get":{"tags":["Content Studio"],"summary":"Get Page Versions","description":"List version snapshots for a page (newest first).\n\nP4: each row reports ``block_count`` + ``blocks_size`` so the editor can\nsummarise without round-tripping the full snapshot. Use\n``GET /pages/{id}/versions/{version_number}`` to fetch a single snapshot\nin full (with blocks).","operationId":"get_page_versions_api_v1_dashboard_projects__project_id__content_pages__page_id__versions_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageVersionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/versions/{version_number}":{"get":{"tags":["Content Studio"],"summary":"Get Page Version","description":"Fetch a single page version snapshot in full (includes blocks).","operationId":"get_page_version_api_v1_dashboard_projects__project_id__content_pages__page_id__versions__version_number__get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"version_number","in":"path","required":true,"schema":{"type":"integer","title":"Version Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageVersionDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/lock":{"post":{"tags":["Content Studio"],"summary":"Lock Page Endpoint","description":"Lock a page against further mutations.\n\nP4 — agent-surface hardening. Idempotent: re-locking refreshes the\nreason/timestamp. Any role with content-scoped access can lock; unlock\nauthorisation is more restrictive (lock-holder OR force=true with\nsuper_admin / brand_admin).","operationId":"lock_page_endpoint_api_v1_dashboard_projects__project_id__content_pages__page_id__lock_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PageLockRequest"}],"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageLockResponse"}}}},"423":{"description":"Page already locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/unlock":{"post":{"tags":["Content Studio"],"summary":"Unlock Page Endpoint","description":"Unlock a page.\n\nAuthorisation:\n  * Anyone whose ``user_id`` matches ``locked_by_actor_id`` can unlock.\n  * super_admin / brand_admin can unlock with ``?force=true``.\n  * Otherwise: 403 (force gate) or \"Page not found / not authorised\" (200\n    with the unchanged row would be a misleading response — we 404 so\n    the caller distinguishes \"the page doesn't exist\" from \"it's locked\n    by someone else\", since the latter is itself the answer to the\n    permission question).","operationId":"unlock_page_endpoint_api_v1_dashboard_projects__project_id__content_pages__page_id__unlock_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"super_admin / brand_admin override — unlock regardless of who locked.","default":false,"title":"Force"},"description":"super_admin / brand_admin override — unlock regardless of who locked."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageLockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/restore":{"post":{"tags":["Content Studio"],"summary":"Restore Page Version Endpoint","description":"Restore a page to a historical version snapshot.\n\nPhase 11+12 gated. ``dry_run=true`` returns a preview envelope with the\nsnapshot summary. The page lock is enforced — call with ``force=true``\n(super_admin / brand_admin) to override.","operationId":"restore_page_version_endpoint_api_v1_dashboard_projects__project_id__content_pages__page_id__restore_post","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"version_number","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Which version to restore (1-indexed).","title":"Version Number"},"description":"Which version to restore (1-indexed)."},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the restore + receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the restore + receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the restore.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the restore."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P4: bypass page lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P4: bypass page lock (super_admin / brand_admin only)."}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PageRestoreRequest"}],"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/export":{"get":{"tags":["Content Studio"],"summary":"Export Page","description":"Self-contained page export with audit (P2 — agent-surface hardening).\n\nReturns the page row + every component referenced by ``page.blocks`` (full\nbody inlined: html_template / js / css / props_schema / dependencies /\nagent_meta / kind / layouts) + site-level settings + domains + a\n``PageAuditor`` walk over the same data.\n\nThree formats:\n  - ``format=json`` (default) — flat JSON envelope\n  - ``format=md`` — human-readable Markdown (text/markdown)\n  - ``format=archive`` — ZIP byte stream (application/zip), VSCode-extension-compatible\n\nThe page must belong to the caller's client_id (Lock 5 — same multi-tenant\nscope as ``GET /pages/{id}``).","operationId":"export_page_api_v1_dashboard_projects__project_id__content_pages__page_id__export_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|md|archive)$","description":"json → flat JSON envelope; md → human-readable Markdown; archive → ZIP whose layout matches the VSCode extension's local registry shape (page.json / components/<slug>@<version>.json / settings.json / domains.json / audit.md / manifest.json).","default":"json","title":"Format"},"description":"json → flat JSON envelope; md → human-readable Markdown; archive → ZIP whose layout matches the VSCode extension's local registry shape (page.json / components/<slug>@<version>.json / settings.json / domains.json / audit.md / manifest.json)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts":{"get":{"tags":["Content Studio"],"summary":"List Posts","description":"List all blog posts (including drafts). Use ?format=yaml|md for agent-friendly responses.","operationId":"list_posts_api_v1_dashboard_projects__project_id__content_posts_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category id","title":"Category"},"description":"Filter by category id"},{"name":"author_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by author (byline) id","title":"Author Id"},"description":"Filter by author (byline) id"},{"name":"created_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by record creator / agent id","title":"Created By"},"description":"Filter by record creator / agent id"},{"name":"created_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only posts published-or-created on/after this ISO timestamp (time-window filter)","title":"Created After"},"description":"Only posts published-or-created on/after this ISO timestamp (time-window filter)"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Post","description":"Create a new blog post. Phase 11+12 dry_run/confirm_token gated.\n\nUnknown fields in the request body are silently dropped by Pydantic\n(extra='ignore' default) but surfaced in `warnings[]` on the response\nwith a \"Did you mean X?\" hint. Catches the common cover_image vs\ncover_image_url / category_id vs category_ids / featured vs is_featured\nconfusions that AI agents fall into.","operationId":"create_post_api_v1_dashboard_projects__project_id__content_posts_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}":{"get":{"tags":["Content Studio"],"summary":"Get Post","description":"Get a post by ID. Use ?format=yaml|md for agent-friendly responses.","operationId":"get_post_api_v1_dashboard_projects__project_id__content_posts__post_id__get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Post","description":"Update a blog post. Phase 11+12 dry_run/confirm_token gated.\n\nUnknown fields in the request body are silently dropped by Pydantic\nbut surfaced in `warnings[]` on the response with a \"Did you mean X?\"\nhint. Catches the common cover_image vs cover_image_url / category_id\nvs category_ids / featured vs is_featured confusions.","operationId":"update_post_api_v1_dashboard_projects__project_id__content_posts__post_id__patch","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Post","description":"Delete a blog post. Phase 11+12 dry_run/confirm_token gated.","operationId":"delete_post_api_v1_dashboard_projects__project_id__content_posts__post_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Post","description":"Publish a blog post.","operationId":"publish_post_api_v1_dashboard_projects__project_id__content_posts__post_id__publish_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Post","description":"Unpublish a blog post (set to draft).","operationId":"unpublish_post_api_v1_dashboard_projects__project_id__content_posts__post_id__unpublish_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/schedule":{"post":{"tags":["Content Studio"],"summary":"Schedule Post","description":"Schedule a post to auto-publish at a future time (status='scheduled').\n\nThe scheduler sidecar flips it to 'published' once the time arrives. Public\nsurfaces gate on status='published', so a scheduled post stays private until\nthen. A non-future time is rejected — use /publish for immediate publish.","operationId":"schedule_post_api_v1_dashboard_projects__project_id__content_posts__post_id__schedule_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostScheduleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/unschedule":{"post":{"tags":["Content Studio"],"summary":"Unschedule Post","description":"Cancel a scheduled publish — revert the post to draft.","operationId":"unschedule_post_api_v1_dashboard_projects__project_id__content_posts__post_id__unschedule_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Post","description":"Duplicate a blog post. Returns the new draft with a fresh slug + UUID.","operationId":"duplicate_post_api_v1_dashboard_projects__project_id__content_posts__post_id__duplicate_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateRequest"}],"default":{},"title":"Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/status":{"post":{"tags":["Content Studio"],"summary":"Update Post Status","description":"Update post status (draft, pending_review, published, archived).","operationId":"update_post_status_api_v1_dashboard_projects__project_id__content_posts__post_id__status_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostStatusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/posts/{post_id}/related":{"put":{"tags":["Content Studio"],"summary":"Update Post Related","description":"Set related posts for a post.","operationId":"update_post_related_api_v1_dashboard_projects__project_id__content_posts__post_id__related_put","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"title":"Related Post Ids"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/authors":{"get":{"tags":["Content Studio"],"summary":"List Authors","description":"List all authors. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_authors_api_v1_dashboard_projects__project_id__content_authors_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"agent_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Type"}},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Author","description":"Create a new author.","operationId":"create_author_api_v1_dashboard_projects__project_id__content_authors_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/authors/{author_id}":{"get":{"tags":["Content Studio"],"summary":"Get Author","description":"Get an author by ID. Use ?format=yaml|md for agent-friendly responses.","operationId":"get_author_api_v1_dashboard_projects__project_id__content_authors__author_id__get","parameters":[{"name":"author_id","in":"path","required":true,"schema":{"type":"string","title":"Author Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Author","description":"Update an author.","operationId":"update_author_api_v1_dashboard_projects__project_id__content_authors__author_id__patch","parameters":[{"name":"author_id","in":"path","required":true,"schema":{"type":"string","title":"Author Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Author","description":"Deactivate an author (soft-delete).","operationId":"delete_author_api_v1_dashboard_projects__project_id__content_authors__author_id__delete","parameters":[{"name":"author_id","in":"path","required":true,"schema":{"type":"string","title":"Author Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/tags":{"get":{"tags":["Content Studio"],"summary":"List Tags","description":"List all tags. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_tags_api_v1_dashboard_projects__project_id__content_tags_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Tag","description":"Create a new tag.","operationId":"create_tag_api_v1_dashboard_projects__project_id__content_tags_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/tags/{tag_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Tag","description":"Update a tag.","operationId":"update_tag_api_v1_dashboard_projects__project_id__content_tags__tag_id__patch","parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","title":"Tag Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Tag","description":"Delete a tag.","operationId":"delete_tag_api_v1_dashboard_projects__project_id__content_tags__tag_id__delete","parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","title":"Tag Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/categories":{"get":{"tags":["Content Studio"],"summary":"List Categories","description":"List blog categories. Use ?format=yaml|md for agent-friendly responses.","operationId":"list_categories_api_v1_dashboard_projects__project_id__content_categories_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Category","description":"Create a new category.","operationId":"create_category_api_v1_dashboard_projects__project_id__content_categories_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCategoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/categories/{category_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Category","description":"Update a category.","operationId":"update_category_api_v1_dashboard_projects__project_id__content_categories__category_id__patch","parameters":[{"name":"category_id","in":"path","required":true,"schema":{"type":"string","title":"Category Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCategoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Category","description":"Delete a category.","operationId":"delete_category_api_v1_dashboard_projects__project_id__content_categories__category_id__delete","parameters":[{"name":"category_id","in":"path","required":true,"schema":{"type":"string","title":"Category Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/tree":{"get":{"tags":["Content Studio"],"summary":"Get Docs Tree","description":"Get full docs tree (including drafts).\n\nDocs Platform v2 · 3.4 — ``?version=`` scopes the editor tree to one docs\nversion (omit → default). Response carries all versions (every status, for\nmanagement) + the resolved current_version.","operationId":"get_docs_tree_api_v1_dashboard_projects__project_id__content_docs_tree_get","parameters":[{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocTreeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs":{"post":{"tags":["Content Studio"],"summary":"Create Doc","description":"Create a documentation page.","operationId":"create_doc_api_v1_dashboard_projects__project_id__content_docs_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content Studio"],"summary":"List Docs","description":"Flat list of docs (metadata only, no body) — complements GET /docs/tree.\n\nAgents use this to discover a doc's UUID by title/status before fetching or\nmutating it via GET /docs/{doc_id}.","operationId":"list_docs_api_v1_dashboard_projects__project_id__content_docs_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (draft / published / archived).","title":"Status"},"description":"Filter by status (draft / published / archived)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/versions":{"get":{"tags":["Content Studio"],"summary":"List Doc Versions","description":"List the tenant's docs versions (all statuses, for management).","operationId":"list_doc_versions_api_v1_dashboard_projects__project_id__content_docs_versions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionsResponse"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Doc Version","description":"Register a new docs version label. ``is_default`` demotes the prior default.","operationId":"create_doc_version_api_v1_dashboard_projects__project_id__content_docs_versions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/versions/{version_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Doc Version","description":"Update a docs version (title / order / status / default flag).","operationId":"update_doc_version_api_v1_dashboard_projects__project_id__content_docs_versions__version_id__patch","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocVersionItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Doc Version","description":"Delete a docs version. Refuses the default version or one still in use.","operationId":"delete_doc_version_api_v1_dashboard_projects__project_id__content_docs_versions__version_id__delete","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/analytics":{"get":{"tags":["Content Studio"],"summary":"Get Docs Analytics","description":"Server-side docs analytics overview (views / searches / asks) for the\ntenant over the last ``days``. Degrades to an empty rollup, never 500s.","operationId":"get_docs_analytics_api_v1_dashboard_projects__project_id__content_docs_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/{doc_id}":{"get":{"tags":["Content Studio"],"summary":"Get Doc","description":"Get a doc by ID (drafts included).","operationId":"get_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Doc","description":"Update a doc (title, body, SEO, hierarchy). Phase 11+12 dry_run/confirm_token gated.","operationId":"update_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__patch","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Doc","description":"Archive a doc (soft delete). Phase 11+12 dry_run/confirm_token gated.","operationId":"delete_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__delete","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/{doc_id}/duplicate":{"post":{"tags":["Content Studio"],"summary":"Duplicate Doc","description":"Duplicate a doc page. Copy lives as a sibling of the original under the\nsame parent_id with a fresh slug. Status: draft.","operationId":"duplicate_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__duplicate_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/DuplicateRequest"}],"default":{},"title":"Data"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/{doc_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Doc","description":"Publish a draft doc. Phase 11+12 dry_run/confirm_token gated.","operationId":"publish_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__publish_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the publish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the publish."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/{doc_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Doc","description":"Revert a doc to draft status. Phase 11+12 dry_run/confirm_token gated.","operationId":"unpublish_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__unpublish_post","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the unpublish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the unpublish."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/{doc_id}/export":{"get":{"tags":["Content Studio"],"summary":"Export Doc","description":"Self-contained doc export. Returns the doc row + site settings + domains.\n\nDocs are prose-first, so there are no component bodies to inline (unlike\npage export). The doc must belong to the caller's client_id.","operationId":"export_doc_api_v1_dashboard_projects__project_id__content_docs__doc_id__export_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(json|md)$","description":"json → flat JSON envelope (doc + settings + domains); md → human-readable Markdown rendered from the Tiptap body.","default":"json","title":"Format"},"description":"json → flat JSON envelope (doc + settings + domains); md → human-readable Markdown rendered from the Tiptap body."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/import":{"post":{"tags":["Content Studio"],"summary":"Import Doc Markdown","description":"Import a doc from a Markdown body. Create a new doc (omit ``doc_id``,\nprovide ``slug`` + ``title``) or replace an existing doc's body (set\n``doc_id``). `:::component{...}` directives become embedded component nodes.\n\nCreate delegates to ``create_doc`` (ungated); update delegates to the\nPhase 11+12 dry_run/confirm_token-gated ``update_doc``.","operationId":"import_doc_markdown_api_v1_dashboard_projects__project_id__content_docs_import_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12 (update path): preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12 (update path): preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12 (update path): consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12 (update path): consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocMarkdownImport"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/import-openapi":{"post":{"tags":["Content Studio"],"summary":"Import Openapi","description":"Import an OpenAPI/Swagger spec into the docs tree. `dry_run=true` returns\nthe planned section + pages without writing (powers the wizard preview);\notherwise the reference is generated idempotently and (if `publish`) published.\n\nErrors map to 422 (bad/oversized/unfetchable spec — message is client-safe)\nor 500 (logged, generic) — never leak the spec URL or upstream detail.","operationId":"import_openapi_api_v1_dashboard_projects__project_id__content_docs_import_openapi_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiImportRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/{doc_id}/markdown":{"get":{"tags":["Content Studio"],"summary":"Export Doc Markdown","description":"Export a doc's body as pure Markdown (round-trips with POST /docs/import).\n\n`:::component{...}` directives are preserved (render_tiptap_markdown emits them\nvia serialize_component_node). The title is prepended as an H1. Distinct from\n`/docs/{id}/export?format=md`, which wraps the same body in a JSON envelope.","operationId":"export_doc_markdown_api_v1_dashboard_projects__project_id__content_docs__doc_id__markdown_get","parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/import":{"post":{"tags":["Content Studio"],"summary":"Import Page Markdown","description":"Import a page from a Markdown body — the markdown→page converter. Create a\nnew page (omit ``page_id``, provide ``slug`` + ``title``) or replace an\nexisting page's blocks (set ``page_id``). Prose runs become ``rich_text``\nblocks; `:::component{...}` directives become ``component`` blocks.\n\nDelegates to the Phase 11+12 dry_run/confirm_token-gated ``create_page`` /\n``update_page`` (the converter handler only does the markdown→blocks mapping).","operationId":"import_page_markdown_api_v1_dashboard_projects__project_id__content_pages_import_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create/update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create/update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageMarkdownImport"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/pages/{page_id}/markdown":{"get":{"tags":["Content Studio"],"summary":"Export Page Markdown","description":"Export a page's blocks as pure Markdown (round-trips with POST /pages/import).\n\nComponent blocks serialize to `:::component{...}` directives (the converter\ncontract — `render_page_markdown(serialize_components=True)`). This differs\nfrom `/pages/{id}/export?format=md`, which renders each component as a labelled\nhuman-readable section inside a JSON envelope (not round-trippable).","operationId":"export_page_markdown_api_v1_dashboard_projects__project_id__content_pages__page_id__markdown_get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","title":"Page Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/docs/reorder":{"put":{"tags":["Content Studio"],"summary":"Reorder Docs","description":"Reorder documentation tree.","operationId":"reorder_docs_api_v1_dashboard_projects__project_id__content_docs_reorder_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocReorderRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/navigation/{location}":{"get":{"tags":["Content Studio"],"summary":"Get Navigation","description":"Get navigation menu.","operationId":"get_navigation_api_v1_dashboard_projects__project_id__content_navigation__location__get","parameters":[{"name":"location","in":"path","required":true,"schema":{"type":"string","title":"Location"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Content Studio"],"summary":"Update Navigation","description":"Update navigation menu.","operationId":"update_navigation_api_v1_dashboard_projects__project_id__content_navigation__location__put","parameters":[{"name":"location","in":"path","required":true,"schema":{"type":"string","title":"Location"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/categories":{"get":{"tags":["Content Studio"],"summary":"Dash List Directory Categories","description":"List all directory categories for this client (includes drafts/empty ones).","operationId":"dash_list_directory_categories_api_v1_dashboard_projects__project_id__content_directory_categories_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Dash Create Directory Category","description":"Create a directory category.\n\nBody: {name, slug?, description?, icon?, seo_title_template?,\n       seo_description_template?, template?, data_source?, sort_order?}\n\nseo_*_template strings accept {category}, {city}, {listing} placeholders —\nrendered server-side on the public read endpoints.","operationId":"dash_create_directory_category_api_v1_dashboard_projects__project_id__content_directory_categories_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/categories/{category_slug}":{"get":{"tags":["Content Studio"],"summary":"Dash Get Directory Category","operationId":"dash_get_directory_category_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__get","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Dash Update Directory Category","operationId":"dash_update_directory_category_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__patch","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Dash Delete Directory Category","description":"Delete a category (cascades to its listings).","operationId":"dash_delete_directory_category_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__delete","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/categories/{category_slug}/listings/bulk":{"post":{"tags":["Content Studio"],"summary":"Dash Bulk Upsert Listings","description":"Bulk upsert listings. Body: {listings: [{name, slug?, city, state?, ...}, ...]}.\n\nUpsert semantics: slug unique per category. Missing slug auto-generated from name.\nRefreshes the materialized view + category counts on success.\n\nTypical use: agent dumps the results of a SpiderMaps campaign into a category\nvia a single call. Supports `source_job_id` so listings can be traced back\nto the job that produced them.","operationId":"dash_bulk_upsert_listings_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__listings_bulk_post","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/listings":{"get":{"tags":["Content Studio"],"summary":"Dash List Directory Listings","description":"List listings across categories. Filters: category_slug, city, status.","operationId":"dash_list_directory_listings_api_v1_dashboard_projects__project_id__content_directory_listings_get","parameters":[{"name":"category_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category Slug"}},{"name":"city","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"published","title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/categories/{category_slug}/listings":{"post":{"tags":["Content Studio"],"summary":"Dash Upsert Listing","description":"Create or update a single listing in a category. Slug-based upsert.","operationId":"dash_upsert_listing_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__listings_post","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/categories/{category_slug}/listings/{listing_slug}":{"delete":{"tags":["Content Studio"],"summary":"Dash Delete Listing","operationId":"dash_delete_listing_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__listings__listing_slug__delete","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}},{"name":"listing_slug","in":"path","required":true,"schema":{"type":"string","title":"Listing Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/categories/{category_slug}/import-from-idap":{"post":{"tags":["Content Studio"],"summary":"Dash Import From Idap","description":"Populate a directory category from the tenant's IDAP data (norm_cli_*.businesses).\n\nOne call replaces the \"run SpiderMaps → transform results → bulk_upsert\" loop.\nThe norm_cli_* schema is already the canonical store for every business\nSpiderIQ has seen for this tenant — this reads directly from it.\n\nBody (all optional):\n  {category_filter: \"Plumber\", country_code: \"US\", city: \"Miami\",\n   rating_min: 4.0, limit: 5000}\n\nReturns {upserted, failed, source_rows, affected_cities, source_schema, filter, ...}.","operationId":"dash_import_from_idap_api_v1_dashboard_projects__project_id__content_directory_categories__category_slug__import_from_idap_post","parameters":[{"name":"category_slug","in":"path","required":true,"schema":{"type":"string","title":"Category Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/directory/refresh-stats":{"post":{"tags":["Content Studio"],"summary":"Dash Refresh Directory Stats","description":"Manually refresh the city_stats materialized view. Normally auto-refreshed on bulk upsert.","operationId":"dash_refresh_directory_stats_api_v1_dashboard_projects__project_id__content_directory_refresh_stats_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/media":{"get":{"tags":["Content Studio"],"summary":"List Media","description":"List media files for the current client, newest first.\n\nFilters: `folder` (exact match), `mime_type` (prefix match — `image/` catches\nall image MIME types).","operationId":"list_media_api_v1_dashboard_projects__project_id__content_media_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"folder","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder"}},{"name":"mime_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/media/upload":{"post":{"tags":["Content Studio"],"summary":"Upload Media","description":"Upload an image to Cloudflare R2.","operationId":"upload_media_api_v1_dashboard_projects__project_id__content_media_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_api_v1_dashboard_projects__project_id__content_media_upload_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/media/upload-batch":{"post":{"tags":["Content Studio"],"summary":"Upload Media Batch","description":"Upload one or many files to SpiderMedia (SeaweedFS).\n\nWeight policy — scroll-sequence folders (`scroll-sequences/*` or\n`is_scroll_sequence=true`): 500 KB per-file hard, 20 MB batch total hard,\n200 KB / 10 MB soft warnings. Everything else: 20 MB per-file (500 MB for\nvideo/*), 500 MB batch total. Hard ceilings return 400 with the offending\nfile(s); soft warnings come back in `warnings[]` with the upload succeeding.\n\nReturns {uploaded: [...], failed: [...], warnings: [...], totals}.","operationId":"upload_media_batch_api_v1_dashboard_projects__project_id__content_media_upload_batch_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_batch_api_v1_dashboard_projects__project_id__content_media_upload_batch_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/media/{media_id}":{"patch":{"tags":["Content Studio"],"summary":"Update Media","description":"Update a legacy R2 media file's metadata (folder / alt_text / caption).\n\nTargets the ``content_media`` table (the dashboard R2 uploader surface), NOT\nthe per-tenant ``media_assets`` catalog. True PATCH — only supplied fields\nchange. 200 + the updated row; 404 if no such media for this client.","operationId":"update_media_api_v1_dashboard_projects__project_id__content_media__media_id__patch","parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Media","description":"Delete a legacy R2 media file (``content_media`` row + best-effort R2\nobject cleanup). 204 on success, 404 if no such media for this client.\n\nThe DB row delete is authoritative; the R2 object removal is best-effort and\nnon-blocking — an orphaned R2 object never fails the request (the row is\nalready gone, so the asset no longer appears in the library).","operationId":"delete_media_api_v1_dashboard_projects__project_id__content_media__media_id__delete","parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/settings":{"get":{"tags":["Content Studio"],"summary":"Get Settings","description":"Get content settings.","operationId":"get_settings_api_v1_dashboard_projects__project_id__content_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSettingsResponse"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Settings","description":"Update content settings. Phase 11+12 dry_run/confirm_token gated.","operationId":"update_settings_api_v1_dashboard_projects__project_id__content_settings_patch","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the change and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the change and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSettingsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/redirects":{"get":{"tags":["Content Studio"],"summary":"List Redirects","description":"List all redirects.","operationId":"list_redirects_api_v1_dashboard_projects__project_id__content_redirects_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectListResponse"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Redirect","description":"Create a redirect.","operationId":"create_redirect_api_v1_dashboard_projects__project_id__content_redirects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/redirects/{redirect_id}":{"delete":{"tags":["Content Studio"],"summary":"Delete Redirect","description":"Delete a redirect.","operationId":"delete_redirect_api_v1_dashboard_projects__project_id__content_redirects__redirect_id__delete","parameters":[{"name":"redirect_id","in":"path","required":true,"schema":{"type":"string","title":"Redirect Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains":{"get":{"tags":["Content Studio"],"summary":"List Domains","description":"List all custom domains for this client plus the auto-provisioned\nplatform preview URL when the client has ever deployed.","operationId":"list_domains_api_v1_dashboard_projects__project_id__content_domains_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainListResponse"}}}}}},"post":{"tags":["Content Studio"],"summary":"Add Domain","description":"Register a new custom domain.\n\nDrives BOTH Cloudflare onboarding paths and surfaces the outcome of each\nso the dashboard can render an actionable banner instead of silently\nmasking failures:\n\n* **CF for SaaS custom hostname** — for domains whose zone is NOT in our\n  CF account. The client adds a CNAME to the fallback origin and CF\n  handles SSL + dispatch.\n* **Worker Route on the domain's own zone** — for domains whose zone IS\n  in our CF account (incl. subdomains of zones we own). Without this,\n  KV mappings exist but CF returns 522 because no Worker handles the\n  inbound request.\n\nEach helper detects which path it owns and no-ops on the other (see\n``register_custom_hostname`` skipping in-account zones, and\n``ensure_worker_route`` skipping zones in other accounts). Both are\ncalled unconditionally; their structured return values populate\n``worker_route_status`` and ``custom_hostname_status`` on the response.","operationId":"add_domain_api_v1_dashboard_projects__project_id__content_domains_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains/subdomain":{"post":{"tags":["Content Studio"],"summary":"Add Subdomain","description":"Register a free ``{label}.sites.spideriq.ai`` subdomain.\n\nThe zero-cost counterpart to ``add_domain``: ``sites.spideriq.ai`` is a\nzone we own with a wildcard Worker Route already configured, so this path\nmakes NO Cloudflare custom-hostname call and needs NO DNS verification —\nit writes the ``DOMAIN_MAP`` KV entry and the dispatch Worker routes the\nhostname to the client's tenant script. Available on every tier; the free\ntier's single slot is one of these.\n\nCounts against the same ``max_domains`` quota as a custom domain.","operationId":"add_subdomain_api_v1_dashboard_projects__project_id__content_domains_subdomain_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubdomainCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains/subdomain/availability":{"get":{"tags":["Content Studio"],"summary":"Check Subdomain Availability","description":"Check whether a ``{label}.sites.spideriq.ai`` subdomain is free.\n\nPowers the live availability check in the dashboard subdomain editor.\nReturns ``{available, host, reason}``. ``available=false`` with a ``reason``\nwhen the label is malformed; otherwise reflects global host uniqueness\n(``content_domains.domain`` is globally unique).","operationId":"check_subdomain_availability_api_v1_dashboard_projects__project_id__content_domains_subdomain_availability_get","parameters":[{"name":"label","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":63,"title":"Label"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains/{domain}":{"patch":{"tags":["Content Studio"],"summary":"Update Domain","description":"Update a domain (SD1): set its start page and/or rename the host.\n\nA rename remaps Cloudflare so the new host serves the same tenant site —\nthe new ``DOMAIN_MAP`` KV entry (and, for an out-of-account custom domain, a\nfresh custom hostname + Worker Route) is created and the old host's mapping\nis torn down. Subdomains on ``*.sites.spideriq.ai`` route via the existing\nwildcard, so they only need the KV swap. The change goes live on the next\ndeploy.","operationId":"update_domain_api_v1_dashboard_projects__project_id__content_domains__domain__patch","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Remove Domain","description":"Remove a custom domain.","operationId":"remove_domain_api_v1_dashboard_projects__project_id__content_domains__domain__delete","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains/{domain}/verify":{"post":{"tags":["Content Studio"],"summary":"Verify Domain","description":"Verify a domain (checks DNS TXT record).\n\nSurfaces ``needs_deploy=True`` when the client has no live deploy yet so\nthe dashboard can prompt \"Click to deploy your site\" — without that, a\nfreshly verified domain will return CF 522 because the per-client tenant\nWorker script doesn't exist in the dispatch namespace.","operationId":"verify_domain_api_v1_dashboard_projects__project_id__content_domains__domain__verify_post","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains/{domain}/recheck":{"post":{"tags":["Content Studio"],"summary":"Recheck Domain Ssl","description":"Re-query Cloudflare for this domain's live SSL state and persist it.\n\nThe on-demand, customer-triggered counterpart to the background\nreconciliation poll: powers the dashboard \"Re-check\" button so a customer\nwho just fixed their DNS doesn't have to wait for the next poll tick. Does\nexactly ONE Cloudflare custom-hostname read for the one domain (not a fan-out\nover the whole list — that stays cheap and rate-limit-safe), maps the CF SSL\nlifecycle onto our ``ssl_status`` enum, and returns the refreshed row with\nthe live CF status echoed in ``custom_hostname_status``.","operationId":"recheck_domain_ssl_api_v1_dashboard_projects__project_id__content_domains__domain__recheck_post","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/domains/{domain}/primary":{"post":{"tags":["Content Studio"],"summary":"Set Primary Domain","description":"Set a domain as the primary domain for this client.","operationId":"set_primary_domain_api_v1_dashboard_projects__project_id__content_domains__domain__primary_post","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy":{"post":{"tags":["Content Studio"],"summary":"Deploy Site","description":"Deploy the client's tenant site to Workers for Platforms.\n\nPhase 11+12: pass `dry_run=true` to receive a preview URL and confirm_token\nwithout deploying; pass `confirm_token=<cft_…>` to consume the prior preview\nand deploy to production. Calls without either flag keep today's immediate\nbehaviour (back-compat until Stage 4 rollout flips the default).","operationId":"deploy_site_api_v1_dashboard_projects__project_id__content_deploy_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: issue a preview + confirm_token without deploying.","default":false,"title":"Dry Run"},"description":"Phase 11+12: issue a preview + confirm_token without deploying."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and deploy to production.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and deploy to production."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy/preview":{"post":{"tags":["Content Studio"],"summary":"Deploy Site Preview","description":"Phase 11+12 Stage 2 — issue a preview URL + confirm_token for the proposed\ndeploy. No CF production script upload, no DNS flip. Caller reviews the\npreview, then calls `POST /deploy/production` with the `confirm_token` to\nactually deploy.","operationId":"deploy_site_preview_api_v1_dashboard_projects__project_id__content_deploy_preview_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy/production":{"post":{"tags":["Content Studio"],"summary":"Deploy Site Production","description":"Phase 11+12 Stage 2 — consume a prior preview's `confirm_token` (Lock 4)\nand run the real deploy. 403 on tenant/action/resource mismatch, 410 on\nexpired, 409 on already-consumed replay.","operationId":"deploy_site_production_api_v1_dashboard_projects__project_id__content_deploy_production_post","parameters":[{"name":"confirm_token","in":"query","required":true,"schema":{"type":"string","description":"Token from a prior /deploy/preview call.","title":"Confirm Token"},"description":"Token from a prior /deploy/preview call."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy/status":{"get":{"tags":["Content Studio"],"summary":"Get Deploy Status","description":"Get the most recent deploy status for this client.","operationId":"get_deploy_status_api_v1_dashboard_projects__project_id__content_deploy_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployStatusResponse"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy/history":{"get":{"tags":["Content Studio"],"summary":"List Deploys","description":"List recent deploys for this client.","operationId":"list_deploys_api_v1_dashboard_projects__project_id__content_deploy_history_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy/readiness":{"get":{"tags":["Content Studio"],"summary":"Get Deploy Readiness","description":"Check if this client's site is ready to deploy.\n\nReturns a checklist of prerequisites (settings, domain, templates, pages)\nwith pass/fail status and actionable fix instructions.\nBlocking items prevent deploy; warnings are advisory.","operationId":"get_deploy_readiness_api_v1_dashboard_projects__project_id__content_deploy_readiness_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployReadinessResponse"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/deploy/{deploy_id}":{"get":{"tags":["Content Studio"],"summary":"Get Deploy Detail","description":"Full detail for a single deploy — backs the per-deploy detail page.\n\nClient-scoped: returns 404 if the deploy doesn't exist OR belongs to a\ndifferent tenant (no cross-tenant leakage). Computes `duration_ms` from\ndeployed_at − created_at. Declared AFTER the static /deploy/* routes so\n`{deploy_id}` never shadows status/history/readiness.","operationId":"get_deploy_detail_api_v1_dashboard_projects__project_id__content_deploy__deploy_id__get","parameters":[{"name":"deploy_id","in":"path","required":true,"schema":{"type":"string","title":"Deploy Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/changelog":{"get":{"tags":["Content Studio"],"summary":"List Changelog","description":"List changelog entries (newest published first, then drafts).\n\nDrafts are included for the editor surface. Paginated — the public feed +\ntimeline read the same service method with include_drafts=False.","operationId":"list_changelog_api_v1_dashboard_projects__project_id__content_changelog_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (draft / published / archived).","title":"Status"},"description":"Filter by status (draft / published / archived)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max entries to return.","default":50,"title":"Limit"},"description":"Max entries to return."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset.","default":0,"title":"Offset"},"description":"Pagination offset."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio"],"summary":"Create Changelog","description":"Create a draft changelog entry (409 on a duplicate version).","operationId":"create_changelog_api_v1_dashboard_projects__project_id__content_changelog_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/changelog/{entry_id}":{"get":{"tags":["Content Studio"],"summary":"Get Changelog","description":"Get one changelog entry by ID (drafts included).","operationId":"get_changelog_api_v1_dashboard_projects__project_id__content_changelog__entry_id__get","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio"],"summary":"Update Changelog","description":"Update a changelog entry's title/body (version is immutable post-create\nonly via this route's schema; pass it through update_changelog if needed).","operationId":"update_changelog_api_v1_dashboard_projects__project_id__content_changelog__entry_id__patch","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio"],"summary":"Delete Changelog","description":"Soft-delete a changelog entry (status → archived). Returns 204.","operationId":"delete_changelog_api_v1_dashboard_projects__project_id__content_changelog__entry_id__delete","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/changelog/{entry_id}/publish":{"post":{"tags":["Content Studio"],"summary":"Publish Changelog","description":"Publish a changelog entry (fires changelog.published on first publish).","operationId":"publish_changelog_api_v1_dashboard_projects__project_id__content_changelog__entry_id__publish_post","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/changelog/{entry_id}/unpublish":{"post":{"tags":["Content Studio"],"summary":"Unpublish Changelog","description":"Revert a changelog entry to draft.","operationId":"unpublish_changelog_api_v1_dashboard_projects__project_id__content_changelog__entry_id__unpublish_post","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangelogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/audit/links":{"get":{"tags":["Content Studio"],"summary":"Audit Internal Links","description":"Walk every published page's blocks + navigation menus and validate\ninternal links against the published-page roster + active redirects.\n\nReturns `{valid_count, broken: [{path, source, reason}], proposed_redirects, known_redirects}`.\n\nFixes the Unavis-report pain: cleaning up `/en/*` legacy URLs across nav +\nblocks used to require manual crawling. One call surfaces every broken link\nwith its exact source so agents can propose redirects in a single pass.","operationId":"audit_internal_links_api_v1_dashboard_projects__project_id__content_audit_links_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/content/search-console/connect/start":{"post":{"tags":["Search Console"],"summary":"Gsc Connect Start","description":"Build the Google consent URL for read-only Search Console access.\n\nStores a short-lived state token (→ brand_id + client_id) in Redis so the\ncallback can bind the resulting credential to this tenant.","operationId":"gsc_connect_start_api_v1_dashboard_content_search_console_connect_start_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscConnectStartResponse"}}}}}}},"/api/v1/dashboard/content/search-console/connect/callback":{"post":{"tags":["Search Console"],"summary":"Gsc Connect Callback","description":"Exchange the OAuth code for tokens and store the credential for this brand.\n\nIdempotent per brand: an existing active GSC credential is deactivated first\n(a brand has one live Google connection at a time).","operationId":"gsc_connect_callback_api_v1_dashboard_content_search_console_connect_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscConnectCallbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/search-console/status":{"get":{"tags":["Search Console"],"summary":"Gsc Status","description":"Whether this tenant has a live GSC connection + which properties it can read.","operationId":"gsc_status_api_v1_dashboard_content_search_console_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscStatusResponse"}}}}}}},"/api/v1/dashboard/content/search-console/connect":{"delete":{"tags":["Search Console"],"summary":"Gsc Disconnect","description":"Deactivate this tenant's GSC credential. Idempotent.","operationId":"gsc_disconnect_api_v1_dashboard_content_search_console_connect_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/content/search-console/performance":{"get":{"tags":["Search Console"],"summary":"Gsc Performance","description":"Live GSC Search Analytics for this tenant: summary + daily timeseries +\ntop queries + top pages.","operationId":"gsc_performance_api_v1_dashboard_content_search_console_performance_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Lookback window (GSC data lags ~3 days).","default":28,"title":"Days"},"description":"Lookback window (GSC data lags ~3 days)."},{"name":"property_url","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Specific GSC property; defaults to the tenant's primary verified domain.","title":"Property Url"},"description":"Specific GSC property; defaults to the tenant's primary verified domain."},{"name":"top_n","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Top N"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/search-console/index-coverage":{"get":{"tags":["Search Console"],"summary":"Gsc Index Coverage","description":"Cached index-coverage report for this tenant's project: summary counts,\na coverage-state histogram, and per-page rows. Empty until first refresh.","operationId":"gsc_index_coverage_api_v1_dashboard_content_search_console_index_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/content/search-console/index-coverage/refresh":{"post":{"tags":["Search Console"],"summary":"Gsc Index Coverage Refresh","description":"Re-inspect this tenant's published pages via the GSC URL Inspection API\nand refresh the cache. Bounded by `max_urls` (response carries `truncated`)\nand a 60 s server-side cooldown that serves cache on rapid re-refresh.","operationId":"gsc_index_coverage_refresh_api_v1_dashboard_content_search_console_index_coverage_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/content/search-console/page-index-status":{"get":{"tags":["Search Console"],"summary":"Gsc Page Index Status","description":"The cached index status for a single page slug (for the editor chip /\nSEO card). `found=False` when the page hasn't been inspected yet.","operationId":"gsc_page_index_status_api_v1_dashboard_content_search_console_page_index_status_get","parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Page/post/doc slug to look up.","title":"Slug"},"description":"Page/post/doc slug to look up."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/connect/start":{"post":{"tags":["Search Console"],"summary":"Gsc Connect Start","description":"Build the Google consent URL for read-only Search Console access.\n\nStores a short-lived state token (→ brand_id + client_id) in Redis so the\ncallback can bind the resulting credential to this tenant.","operationId":"gsc_connect_start_api_v1_dashboard_projects__project_id__content_search_console_connect_start_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscConnectStartResponse"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/connect/callback":{"post":{"tags":["Search Console"],"summary":"Gsc Connect Callback","description":"Exchange the OAuth code for tokens and store the credential for this brand.\n\nIdempotent per brand: an existing active GSC credential is deactivated first\n(a brand has one live Google connection at a time).","operationId":"gsc_connect_callback_api_v1_dashboard_projects__project_id__content_search_console_connect_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscConnectCallbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/status":{"get":{"tags":["Search Console"],"summary":"Gsc Status","description":"Whether this tenant has a live GSC connection + which properties it can read.","operationId":"gsc_status_api_v1_dashboard_projects__project_id__content_search_console_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GscStatusResponse"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/connect":{"delete":{"tags":["Search Console"],"summary":"Gsc Disconnect","description":"Deactivate this tenant's GSC credential. Idempotent.","operationId":"gsc_disconnect_api_v1_dashboard_projects__project_id__content_search_console_connect_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/performance":{"get":{"tags":["Search Console"],"summary":"Gsc Performance","description":"Live GSC Search Analytics for this tenant: summary + daily timeseries +\ntop queries + top pages.","operationId":"gsc_performance_api_v1_dashboard_projects__project_id__content_search_console_performance_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Lookback window (GSC data lags ~3 days).","default":28,"title":"Days"},"description":"Lookback window (GSC data lags ~3 days)."},{"name":"property_url","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Specific GSC property; defaults to the tenant's primary verified domain.","title":"Property Url"},"description":"Specific GSC property; defaults to the tenant's primary verified domain."},{"name":"top_n","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Top N"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/index-coverage":{"get":{"tags":["Search Console"],"summary":"Gsc Index Coverage","description":"Cached index-coverage report for this tenant's project: summary counts,\na coverage-state histogram, and per-page rows. Empty until first refresh.","operationId":"gsc_index_coverage_api_v1_dashboard_projects__project_id__content_search_console_index_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/index-coverage/refresh":{"post":{"tags":["Search Console"],"summary":"Gsc Index Coverage Refresh","description":"Re-inspect this tenant's published pages via the GSC URL Inspection API\nand refresh the cache. Bounded by `max_urls` (response carries `truncated`)\nand a 60 s server-side cooldown that serves cache on rapid re-refresh.","operationId":"gsc_index_coverage_refresh_api_v1_dashboard_projects__project_id__content_search_console_index_coverage_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/search-console/page-index-status":{"get":{"tags":["Search Console"],"summary":"Gsc Page Index Status","description":"The cached index status for a single page slug (for the editor chip /\nSEO card). `found=False` when the page hasn't been inspected yet.","operationId":"gsc_page_index_status_api_v1_dashboard_projects__project_id__content_search_console_page_index_status_get","parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Page/post/doc slug to look up.","title":"Slug"},"description":"Page/post/doc slug to look up."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects":{"get":{"tags":["Content Studio - Projects"],"summary":"List Projects","operationId":"list_projects_api_v1_dashboard_projects_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectListResponse"}}}}}},"post":{"tags":["Content Studio - Projects"],"summary":"Create Project","operationId":"create_project_api_v1_dashboard_projects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{proj_ref}":{"patch":{"tags":["Content Studio - Projects"],"summary":"Update Project","operationId":"update_project_api_v1_dashboard_projects__proj_ref__patch","parameters":[{"name":"proj_ref","in":"path","required":true,"schema":{"type":"string","title":"Proj Ref"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio - Projects"],"summary":"Delete Project","operationId":"delete_project_api_v1_dashboard_projects__proj_ref__delete","parameters":[{"name":"proj_ref","in":"path","required":true,"schema":{"type":"string","title":"Proj Ref"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components":{"post":{"tags":["Content Studio - Components"],"summary":"Create Component","description":"Create a new UI component. Phase 11+12 dry_run/confirm_token gated.","operationId":"create_component_api_v1_dashboard_content_components_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content Studio - Components"],"summary":"List Components","description":"List components for the current client. Supports filtering by category,\nmarketplace_category, and status.\n\nAudit nav-shell page-07 Q4 (2026-05-05): super_admin can call this without\nselecting a tenant scope — the dependency returns user.client_id=None, and\nthe service treats None as \"admin view\" (no client_id filter, returns global\nrows + every per-tenant copy across all clients).\n\nCRO bug-fix bundle (2026-05-08, Antigravity report Bug 3): the service layer\nhas supported `marketplace_category` filtering since Phase A, but the API\nlayer wasn't forwarding the query param — `?marketplace_category=...` was\nsilently ignored. Now wired through.","operationId":"list_components_api_v1_dashboard_content_components_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"},{"name":"marketplace_category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by marketplace_category (e.g. 'urgency-scarcity', 'capture-popups', 'social-proof')","title":"Marketplace Category"},"description":"Filter by marketplace_category (e.g. 'urgency-scarcity', 'capture-popups', 'social-proof')"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (draft/published/archived)","title":"Status"},"description":"Filter by status (draft/published/archived)"},{"name":"include_global","in":"query","required":false,"schema":{"type":"boolean","description":"Include system (global) components","default":true,"title":"Include Global"},"description":"Include system (global) components"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/by-slug/{slug}":{"get":{"tags":["Content Studio - Components"],"summary":"Get Component By Slug","description":"Get a component by slug. Returns latest published version if no version specified.","operationId":"get_component_by_slug_api_v1_dashboard_content_components_by_slug__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Specific version (default: latest)","title":"Version"},"description":"Specific version (default: latest)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}":{"get":{"tags":["Content Studio - Components"],"summary":"Get Component","description":"Get a component by ID.","operationId":"get_component_api_v1_dashboard_content_components__component_id__get","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio - Components"],"summary":"Update Component","description":"Update a component. Only provided fields are changed. Phase 11+12 dry_run/confirm_token gated.\n\nUnknown fields in the request body are ignored (Pydantic default) but surfaced\nin the response's `warnings` array with a \"Did you mean X?\" hint when a close\nfield name exists. This catches `css_styles` (should be `css`) silent-drops.\n\nPhase E (2026-05-05) — accepts Phase A 4-class taxonomy (kind, block_type,\njs_runtime, layouts, sources, extension_spec) + agent-discovery axes\n(mood, palette, brand_fit_tags, scene_type, agent_meta). Strict enum\nvalidation at the boundary; cross-field invariants are backstopped by\nDB CHECK constraints (migration 174).","operationId":"update_component_api_v1_dashboard_content_components__component_id__patch","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"DB CHECK constraint violation (e.g. PATCH leaves row with kind='dynamic' but block_type=NULL — Phase A invariant in migration 174).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"403":{"description":"Cannot edit a global component you don't own; or confirm_token mismatch (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Component not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"409":{"description":"confirm_token already consumed (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"410":{"description":"confirm_token expired (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Body failed validation: unknown enum (kind/block_type/js_runtime/scene_type/mood/brand_fit_tag), agent_meta with unknown key (extra='forbid'), invalid Literal (interaction_pattern, trigger_kind), or layouts/sources length cap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}},"delete":{"tags":["Content Studio - Components"],"summary":"Delete Component","description":"Delete a component. Only the owning client can delete (not global components). Phase 11+12 gated.","operationId":"delete_component_api_v1_dashboard_content_components__component_id__delete","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}/preview":{"post":{"tags":["Content Studio - Components"],"summary":"Preview Component","description":"Render a single component in isolation for dashboard iframe preview.\n\nCloses the BrokerZ-report pain: Shadow DOM layout issues were only visible\nafter a full site preview deploy (~60-90 seconds). This endpoint returns\nthe component's html_template + css + js + merged props so the dashboard\ncan iframe-srcdoc it instantly for quick styling checks.\n\nNote: v1 returns the raw html_template — Liquid `{{ props.x }}` tokens are\nNOT interpolated here (the Liquid runtime lives in the Worker bundle). The\nresponse exposes `merged_props` so the dashboard's own renderer can\ninterpolate client-side if needed. Full-fidelity preview still ships via\nthe existing `/deploy/preview` flow.","operationId":"preview_component_api_v1_dashboard_content_components__component_id__preview_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentPreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}/upload-preview":{"post":{"tags":["Content Studio - Components"],"summary":"Upload Component Preview","description":"Upload a preview image (PNG/JPG/GIF/WEBP/MP4) for a component to R2 at\n`components/<slug>.<ext>`, then PATCH `preview_thumbnail_url` on the row.\n\nSingle round-trip — the editor doesn't need a separate PATCH after upload.\n\nPhase E (2026-05-05) — Content-Type and magic-number are now validated\nalongside the extension. Capped at 5 MB; for larger assets, use the\nSpiderMedia `/files/upload` endpoint and reference the URL via PATCH.","operationId":"upload_component_preview_api_v1_dashboard_content_components__component_id__upload_preview_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_component_preview_api_v1_dashboard_content_components__component_id__upload_preview_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"413":{"description":"Upload exceeds 5 MB size limit. Use SpiderMedia for larger assets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"415":{"description":"Unsupported extension, Content-Type/extension mismatch, or magic-number mismatch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}/publish":{"post":{"tags":["Content Studio - Components"],"summary":"Publish Component","description":"Publish a component (makes it available for rendering on live sites).\nFor framework components (Tier 4), returns 202 — build runs async. Phase 11+12 gated.","operationId":"publish_component_api_v1_dashboard_content_components__component_id__publish_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the publish and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the publish and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the publish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the publish."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}/archive":{"post":{"tags":["Content Studio - Components"],"summary":"Archive Component","description":"Archive a component (removes from rendering but preserves data). Phase 11+12 gated.","operationId":"archive_component_api_v1_dashboard_content_components__component_id__archive_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the archive and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the archive and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the archive.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the archive."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}/build-status":{"get":{"tags":["Content Studio - Components"],"summary":"Get Build Status","description":"Get the build status of a framework component (Tier 4).","operationId":"get_build_status_api_v1_dashboard_content_components__component_id__build_status_get","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{component_id}/rebuild":{"post":{"tags":["Content Studio - Components"],"summary":"Rebuild Component","description":"Trigger a rebuild for a framework component (Tier 4). Returns 202.","operationId":"rebuild_component_api_v1_dashboard_content_components__component_id__rebuild_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{slug}/versions":{"get":{"tags":["Content Studio - Components"],"summary":"List Component Versions","description":"List all versions of a component by slug.","operationId":"list_component_versions_api_v1_dashboard_content_components__slug__versions_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{slug}/rollback":{"post":{"tags":["Content Studio - Components"],"summary":"Component Rollback","description":"Restore a component to an earlier version by creating a new published\nversion with the target version's content, and repointing consuming pages.\n\nOne confirm_token covers the whole rollback. Gate action is `component_rollback`\n(distinct from `component_update_and_propagate`), so a forward-update token\ncan't accidentally be consumed against this path.\n\nNever auto-deploys the tenant KV. Block-level updates render live on next request.","operationId":"component_rollback_api_v1_dashboard_content_components__slug__rollback_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentRollbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/components/{slug}/update-and-propagate":{"post":{"tags":["Content Studio - Components"],"summary":"Component Update And Propagate","description":"One-shot: bump a component to a new version AND propagate the pin to every\n(or selected) consuming page, gated by a single confirm_token.\n\nReplaces the 5-step agent choreography (PATCH component → iterate pages →\nupdate block versions → publish each → deploy) with one call.\n\n**Never auto-deploys.** The new component version is published and each\naffected page's blocks are updated in-place; block-level content renders\nlive via the content API on next request. If you also changed templates\nor theme, run `deploy_site_preview` → `deploy_site_production` as usual.\n\n**Phase 11+12 Lock 4.** A single confirm_token covers the whole composite\nmutation (component + all affected pages). Issue with `dry_run=true`,\ninspect the `affected_pages` preview, then send the token back on the\nreal call.","operationId":"component_update_and_propagate_api_v1_dashboard_content_components__slug__update_and_propagate_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentUpdateAndPropagateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components":{"post":{"tags":["Content Studio - Components"],"summary":"Create Component","description":"Create a new UI component. Phase 11+12 dry_run/confirm_token gated.","operationId":"create_component_api_v1_dashboard_projects__project_id__content_components_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content Studio - Components"],"summary":"List Components","description":"List components for the current client. Supports filtering by category,\nmarketplace_category, and status.\n\nAudit nav-shell page-07 Q4 (2026-05-05): super_admin can call this without\nselecting a tenant scope — the dependency returns user.client_id=None, and\nthe service treats None as \"admin view\" (no client_id filter, returns global\nrows + every per-tenant copy across all clients).\n\nCRO bug-fix bundle (2026-05-08, Antigravity report Bug 3): the service layer\nhas supported `marketplace_category` filtering since Phase A, but the API\nlayer wasn't forwarding the query param — `?marketplace_category=...` was\nsilently ignored. Now wired through.","operationId":"list_components_api_v1_dashboard_projects__project_id__content_components_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"},{"name":"marketplace_category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by marketplace_category (e.g. 'urgency-scarcity', 'capture-popups', 'social-proof')","title":"Marketplace Category"},"description":"Filter by marketplace_category (e.g. 'urgency-scarcity', 'capture-popups', 'social-proof')"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (draft/published/archived)","title":"Status"},"description":"Filter by status (draft/published/archived)"},{"name":"include_global","in":"query","required":false,"schema":{"type":"boolean","description":"Include system (global) components","default":true,"title":"Include Global"},"description":"Include system (global) components"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/by-slug/{slug}":{"get":{"tags":["Content Studio - Components"],"summary":"Get Component By Slug","description":"Get a component by slug. Returns latest published version if no version specified.","operationId":"get_component_by_slug_api_v1_dashboard_projects__project_id__content_components_by_slug__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Specific version (default: latest)","title":"Version"},"description":"Specific version (default: latest)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}":{"get":{"tags":["Content Studio - Components"],"summary":"Get Component","description":"Get a component by ID.","operationId":"get_component_api_v1_dashboard_projects__project_id__content_components__component_id__get","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Content Studio - Components"],"summary":"Update Component","description":"Update a component. Only provided fields are changed. Phase 11+12 dry_run/confirm_token gated.\n\nUnknown fields in the request body are ignored (Pydantic default) but surfaced\nin the response's `warnings` array with a \"Did you mean X?\" hint when a close\nfield name exists. This catches `css_styles` (should be `css`) silent-drops.\n\nPhase E (2026-05-05) — accepts Phase A 4-class taxonomy (kind, block_type,\njs_runtime, layouts, sources, extension_spec) + agent-discovery axes\n(mood, palette, brand_fit_tags, scene_type, agent_meta). Strict enum\nvalidation at the boundary; cross-field invariants are backstopped by\nDB CHECK constraints (migration 174).","operationId":"update_component_api_v1_dashboard_projects__project_id__content_components__component_id__patch","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the update and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the update and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the update.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the update."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"DB CHECK constraint violation (e.g. PATCH leaves row with kind='dynamic' but block_type=NULL — Phase A invariant in migration 174).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"403":{"description":"Cannot edit a global component you don't own; or confirm_token mismatch (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Component not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"409":{"description":"confirm_token already consumed (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"410":{"description":"confirm_token expired (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Body failed validation: unknown enum (kind/block_type/js_runtime/scene_type/mood/brand_fit_tag), agent_meta with unknown key (extra='forbid'), invalid Literal (interaction_pattern, trigger_kind), or layouts/sources length cap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}},"delete":{"tags":["Content Studio - Components"],"summary":"Delete Component","description":"Delete a component. Only the owning client can delete (not global components). Phase 11+12 gated.","operationId":"delete_component_api_v1_dashboard_projects__project_id__content_components__component_id__delete","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}/preview":{"post":{"tags":["Content Studio - Components"],"summary":"Preview Component","description":"Render a single component in isolation for dashboard iframe preview.\n\nCloses the BrokerZ-report pain: Shadow DOM layout issues were only visible\nafter a full site preview deploy (~60-90 seconds). This endpoint returns\nthe component's html_template + css + js + merged props so the dashboard\ncan iframe-srcdoc it instantly for quick styling checks.\n\nNote: v1 returns the raw html_template — Liquid `{{ props.x }}` tokens are\nNOT interpolated here (the Liquid runtime lives in the Worker bundle). The\nresponse exposes `merged_props` so the dashboard's own renderer can\ninterpolate client-side if needed. Full-fidelity preview still ships via\nthe existing `/deploy/preview` flow.","operationId":"preview_component_api_v1_dashboard_projects__project_id__content_components__component_id__preview_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentPreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}/upload-preview":{"post":{"tags":["Content Studio - Components"],"summary":"Upload Component Preview","description":"Upload a preview image (PNG/JPG/GIF/WEBP/MP4) for a component to R2 at\n`components/<slug>.<ext>`, then PATCH `preview_thumbnail_url` on the row.\n\nSingle round-trip — the editor doesn't need a separate PATCH after upload.\n\nPhase E (2026-05-05) — Content-Type and magic-number are now validated\nalongside the extension. Capped at 5 MB; for larger assets, use the\nSpiderMedia `/files/upload` endpoint and reference the URL via PATCH.","operationId":"upload_component_preview_api_v1_dashboard_projects__project_id__content_components__component_id__upload_preview_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_component_preview_api_v1_dashboard_projects__project_id__content_components__component_id__upload_preview_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"413":{"description":"Upload exceeds 5 MB size limit. Use SpiderMedia for larger assets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"415":{"description":"Unsupported extension, Content-Type/extension mismatch, or magic-number mismatch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}/publish":{"post":{"tags":["Content Studio - Components"],"summary":"Publish Component","description":"Publish a component (makes it available for rendering on live sites).\nFor framework components (Tier 4), returns 202 — build runs async. Phase 11+12 gated.","operationId":"publish_component_api_v1_dashboard_projects__project_id__content_components__component_id__publish_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the publish and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the publish and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the publish.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the publish."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}/archive":{"post":{"tags":["Content Studio - Components"],"summary":"Archive Component","description":"Archive a component (removes from rendering but preserves data). Phase 11+12 gated.","operationId":"archive_component_api_v1_dashboard_projects__project_id__content_components__component_id__archive_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the archive and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the archive and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the archive.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the archive."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}/build-status":{"get":{"tags":["Content Studio - Components"],"summary":"Get Build Status","description":"Get the build status of a framework component (Tier 4).","operationId":"get_build_status_api_v1_dashboard_projects__project_id__content_components__component_id__build_status_get","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{component_id}/rebuild":{"post":{"tags":["Content Studio - Components"],"summary":"Rebuild Component","description":"Trigger a rebuild for a framework component (Tier 4). Returns 202.","operationId":"rebuild_component_api_v1_dashboard_projects__project_id__content_components__component_id__rebuild_post","parameters":[{"name":"component_id","in":"path","required":true,"schema":{"type":"string","title":"Component Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{slug}/versions":{"get":{"tags":["Content Studio - Components"],"summary":"List Component Versions","description":"List all versions of a component by slug.","operationId":"list_component_versions_api_v1_dashboard_projects__project_id__content_components__slug__versions_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{slug}/rollback":{"post":{"tags":["Content Studio - Components"],"summary":"Component Rollback","description":"Restore a component to an earlier version by creating a new published\nversion with the target version's content, and repointing consuming pages.\n\nOne confirm_token covers the whole rollback. Gate action is `component_rollback`\n(distinct from `component_update_and_propagate`), so a forward-update token\ncan't accidentally be consumed against this path.\n\nNever auto-deploys the tenant KV. Block-level updates render live on next request.","operationId":"component_rollback_api_v1_dashboard_projects__project_id__content_components__slug__rollback_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentRollbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/components/{slug}/update-and-propagate":{"post":{"tags":["Content Studio - Components"],"summary":"Component Update And Propagate","description":"One-shot: bump a component to a new version AND propagate the pin to every\n(or selected) consuming page, gated by a single confirm_token.\n\nReplaces the 5-step agent choreography (PATCH component → iterate pages →\nupdate block versions → publish each → deploy) with one call.\n\n**Never auto-deploys.** The new component version is published and each\naffected page's blocks are updated in-place; block-level content renders\nlive via the content API on next request. If you also changed templates\nor theme, run `deploy_site_preview` → `deploy_site_production` as usual.\n\n**Phase 11+12 Lock 4.** A single confirm_token covers the whole composite\nmutation (component + all affected pages). Issue with `dry_run=true`,\ninspect the `affected_pages` preview, then send the token back on the\nreal call.","operationId":"component_update_and_propagate_api_v1_dashboard_projects__project_id__content_components__slug__update_and_propagate_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComponentUpdateAndPropagateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/imports":{"post":{"tags":["Content Studio - Imports"],"summary":"Start an import job","description":"Begin importing an external React/Vite/Tailwind site.\n\nProvide exactly one of `source_zip_url` (a SpiderMedia-hosted upload) or\n`source_github_url` (a public repo). The job runs asynchronously; poll\n`GET /imports/{import_id}` for progress.","operationId":"start_import_api_v1_dashboard_content_imports_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content Studio - Imports"],"summary":"List imports for the current tenant","operationId":"list_imports_api_v1_dashboard_content_imports_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ImportStatus"},{"type":"null"}],"description":"Filter by lifecycle status","title":"Status"},"description":"Filter by lifecycle status"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/imports/{import_id}":{"get":{"tags":["Content Studio - Imports"],"summary":"Get import status (and optionally the full manifest)","operationId":"get_import_api_v1_dashboard_content_imports__import_id__get","parameters":[{"name":"import_id","in":"path","required":true,"schema":{"type":"string","title":"Import Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Set to 'manifest' to embed the parsed ImportManifest in the response.","title":"Include"},"description":"Set to 'manifest' to embed the parsed ImportManifest in the response."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/imports/{import_id}/apply":{"post":{"tags":["Content Studio - Imports"],"summary":"Apply a parsed import onto the current tenant (Phase 11+12 gated)","description":"Write the imported pages + components onto the current tenant.\n\nTwo-step flow:\n  1. POST /imports/{id}/apply?dry_run=true → returns confirm_token + preview\n  2. POST /imports/{id}/apply?confirm_token=<token> → consumes token, applies\n\nThe import row must be in `status='parsed'`. After a successful apply it\ntransitions to `status='applied'` with `pages_created` / `components_created`\ncounters populated.","operationId":"apply_import_api_v1_dashboard_content_imports__import_id__apply_post","parameters":[{"name":"import_id","in":"path","required":true,"schema":{"type":"string","title":"Import Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"When true, returns a preview envelope + confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"When true, returns a preview envelope + confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Token obtained from a prior dry_run call. Consuming it performs the mutation.","title":"Confirm Token"},"description":"Token obtained from a prior dry_run call. Consuming it performs the mutation."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/imports":{"post":{"tags":["Content Studio - Imports"],"summary":"Start an import job","description":"Begin importing an external React/Vite/Tailwind site.\n\nProvide exactly one of `source_zip_url` (a SpiderMedia-hosted upload) or\n`source_github_url` (a public repo). The job runs asynchronously; poll\n`GET /imports/{import_id}` for progress.","operationId":"start_import_api_v1_dashboard_projects__project_id__content_imports_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Content Studio - Imports"],"summary":"List imports for the current tenant","operationId":"list_imports_api_v1_dashboard_projects__project_id__content_imports_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ImportStatus"},{"type":"null"}],"description":"Filter by lifecycle status","title":"Status"},"description":"Filter by lifecycle status"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/imports/{import_id}":{"get":{"tags":["Content Studio - Imports"],"summary":"Get import status (and optionally the full manifest)","operationId":"get_import_api_v1_dashboard_projects__project_id__content_imports__import_id__get","parameters":[{"name":"import_id","in":"path","required":true,"schema":{"type":"string","title":"Import Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Set to 'manifest' to embed the parsed ImportManifest in the response.","title":"Include"},"description":"Set to 'manifest' to embed the parsed ImportManifest in the response."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/imports/{import_id}/apply":{"post":{"tags":["Content Studio - Imports"],"summary":"Apply a parsed import onto the current tenant (Phase 11+12 gated)","description":"Write the imported pages + components onto the current tenant.\n\nTwo-step flow:\n  1. POST /imports/{id}/apply?dry_run=true → returns confirm_token + preview\n  2. POST /imports/{id}/apply?confirm_token=<token> → consumes token, applies\n\nThe import row must be in `status='parsed'`. After a successful apply it\ntransitions to `status='applied'` with `pages_created` / `components_created`\ncounters populated.","operationId":"apply_import_api_v1_dashboard_projects__project_id__content_imports__import_id__apply_post","parameters":[{"name":"import_id","in":"path","required":true,"schema":{"type":"string","title":"Import Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"When true, returns a preview envelope + confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"When true, returns a preview envelope + confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Token obtained from a prior dry_run call. Consuming it performs the mutation.","title":"Confirm Token"},"description":"Token obtained from a prior dry_run call. Consuming it performs the mutation."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/cdn-allowlist":{"get":{"tags":["Content Studio - CDN Allowlist"],"summary":"List Cdn Allowlist","description":"List CDN allowlist entries. All authenticated users can read.","operationId":"list_cdn_allowlist_api_v1_dashboard_content_cdn_allowlist_get","parameters":[{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only return active entries","default":true,"title":"Active Only"},"description":"Only return active entries"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CdnAllowlistListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Content Studio - CDN Allowlist"],"summary":"Create Cdn Allowlist Entry","description":"Create a new CDN allowlist entry. Admin only.","operationId":"create_cdn_allowlist_entry_api_v1_dashboard_content_cdn_allowlist_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CdnAllowlistCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CdnAllowlistEntry"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/cdn-allowlist/{entry_id}":{"patch":{"tags":["Content Studio - CDN Allowlist"],"summary":"Update Cdn Allowlist Entry","description":"Update a CDN allowlist entry. Admin only.","operationId":"update_cdn_allowlist_entry_api_v1_dashboard_content_cdn_allowlist__entry_id__patch","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CdnAllowlistUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CdnAllowlistEntry"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Content Studio - CDN Allowlist"],"summary":"Delete Cdn Allowlist Entry","description":"Delete a CDN allowlist entry. Admin only.","operationId":"delete_cdn_allowlist_entry_api_v1_dashboard_content_cdn_allowlist__entry_id__delete","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/templates":{"get":{"tags":["Templates"],"summary":"List Templates","description":"List all custom templates for the current client.","operationId":"list_templates_api_v1_dashboard_templates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateListResponse"}}}}}}},"/api/v1/dashboard/templates/config":{"get":{"tags":["Templates"],"summary":"Get Template Config","description":"Get the template config (theme, routes, settings, data sources).","operationId":"get_template_config_api_v1_dashboard_templates_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateConfigResponse"}}}}}},"patch":{"tags":["Templates"],"summary":"Update Template Config","description":"Update the template config.","operationId":"update_template_config_api_v1_dashboard_templates_config_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateConfigUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/templates/themes":{"get":{"tags":["Templates"],"summary":"List Themes","description":"List available built-in themes.","operationId":"list_themes_api_v1_dashboard_templates_themes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThemeListResponse"}}}}}}},"/api/v1/dashboard/templates/themes/{name}":{"get":{"tags":["Templates"],"summary":"Get Theme Detail","description":"Get a theme's details and all template files.","operationId":"get_theme_detail_api_v1_dashboard_templates_themes__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThemeDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/templates/apply-theme":{"post":{"tags":["Templates"],"summary":"Apply Theme","description":"Apply a built-in theme (copies all template files to client's set). Phase 11+12 dry_run/confirm_token gated.","operationId":"apply_theme_api_v1_dashboard_templates_apply_theme_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the theme apply and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the theme apply and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and apply the theme.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and apply the theme."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyThemeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/templates/apply-site-template/{slug}":{"post":{"tags":["Templates"],"summary":"Apply Site Template","description":"Apply a SpiderIQ-curated site template to the current tenant.\n\nClones every page in `source_page_slugs` from the template-source tenant\ninto this tenant as drafts; copies navigation for every location in\n`source_nav_locations`; copies whitelisted settings keys.\n\nPhase 11+12 dry_run/confirm_token gated. Pages cloned are status='draft' —\nreview + publish individually, or use `content_deploy_site` to push.","operationId":"apply_site_template_api_v1_dashboard_templates_apply_site_template__slug__post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the apply and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the apply and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and apply the site template.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and apply the site template."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/templates/preview":{"post":{"tags":["Templates"],"summary":"Preview Template","description":"Render a template with provided data and return HTML.","operationId":"preview_template_api_v1_dashboard_templates_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePreviewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/templates/{path}":{"get":{"tags":["Templates"],"summary":"Get Template","description":"Get a template by path (e.g. layout/theme.liquid).\n\nEnvelope-every-surface slice 1: the 404 carries a structured error envelope\nexplaining the KV-override-vs-default-theme model (a 404 here is NOT \"feature\nmissing\"), and the 200 carries the agent ``guidance`` block by default for\nPAT/Bearer callers. This is the endpoint behind the 2026-06-16 false-premise\nreports (`template_get('templates/changelog.liquid')` -> 404 read as \"missing\").","operationId":"get_template_api_v1_dashboard_templates__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Templates"],"summary":"Upsert Template","description":"Create or update a template at the given path. Phase 11+12 dry_run/confirm_token gated.","operationId":"upsert_template_api_v1_dashboard_templates__path__put","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the upsert and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the upsert and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the upsert.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the upsert."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Templates"],"summary":"Delete Template","description":"Delete a custom template (reverts to theme default). Phase 11+12 dry_run/confirm_token gated.","operationId":"delete_template_api_v1_dashboard_templates__path__delete","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates":{"get":{"tags":["Templates"],"summary":"List Templates","description":"List all custom templates for the current client.","operationId":"list_templates_api_v1_dashboard_projects__project_id__templates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateListResponse"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/config":{"get":{"tags":["Templates"],"summary":"Get Template Config","description":"Get the template config (theme, routes, settings, data sources).","operationId":"get_template_config_api_v1_dashboard_projects__project_id__templates_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateConfigResponse"}}}}}},"patch":{"tags":["Templates"],"summary":"Update Template Config","description":"Update the template config.","operationId":"update_template_config_api_v1_dashboard_projects__project_id__templates_config_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateConfigUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/themes":{"get":{"tags":["Templates"],"summary":"List Themes","description":"List available built-in themes.","operationId":"list_themes_api_v1_dashboard_projects__project_id__templates_themes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThemeListResponse"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/themes/{name}":{"get":{"tags":["Templates"],"summary":"Get Theme Detail","description":"Get a theme's details and all template files.","operationId":"get_theme_detail_api_v1_dashboard_projects__project_id__templates_themes__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThemeDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/apply-theme":{"post":{"tags":["Templates"],"summary":"Apply Theme","description":"Apply a built-in theme (copies all template files to client's set). Phase 11+12 dry_run/confirm_token gated.","operationId":"apply_theme_api_v1_dashboard_projects__project_id__templates_apply_theme_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the theme apply and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the theme apply and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and apply the theme.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and apply the theme."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyThemeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/apply-site-template/{slug}":{"post":{"tags":["Templates"],"summary":"Apply Site Template","description":"Apply a SpiderIQ-curated site template to the current tenant.\n\nClones every page in `source_page_slugs` from the template-source tenant\ninto this tenant as drafts; copies navigation for every location in\n`source_nav_locations`; copies whitelisted settings keys.\n\nPhase 11+12 dry_run/confirm_token gated. Pages cloned are status='draft' —\nreview + publish individually, or use `content_deploy_site` to push.","operationId":"apply_site_template_api_v1_dashboard_projects__project_id__templates_apply_site_template__slug__post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the apply and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the apply and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and apply the site template.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and apply the site template."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/preview":{"post":{"tags":["Templates"],"summary":"Preview Template","description":"Render a template with provided data and return HTML.","operationId":"preview_template_api_v1_dashboard_projects__project_id__templates_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePreviewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/templates/{path}":{"get":{"tags":["Templates"],"summary":"Get Template","description":"Get a template by path (e.g. layout/theme.liquid).\n\nEnvelope-every-surface slice 1: the 404 carries a structured error envelope\nexplaining the KV-override-vs-default-theme model (a 404 here is NOT \"feature\nmissing\"), and the 200 carries the agent ``guidance`` block by default for\nPAT/Bearer callers. This is the endpoint behind the 2026-06-16 false-premise\nreports (`template_get('templates/changelog.liquid')` -> 404 read as \"missing\").","operationId":"get_template_api_v1_dashboard_projects__project_id__templates__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Templates"],"summary":"Upsert Template","description":"Create or update a template at the given path. Phase 11+12 dry_run/confirm_token gated.","operationId":"upsert_template_api_v1_dashboard_projects__project_id__templates__path__put","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the upsert and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the upsert and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the upsert.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the upsert."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Templates"],"summary":"Delete Template","description":"Delete a custom template (reverts to theme default). Phase 11+12 dry_run/confirm_token gated.","operationId":"delete_template_api_v1_dashboard_projects__project_id__templates__path__delete","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows":{"get":{"tags":["SpiderBook"],"summary":"List Booking Flows","description":"Cursor-paginated list of booking flows scoped to the caller's client.\n\nPagination is keyset on (created_at DESC, flow_id DESC) — same contract as\n:func:`services.idap_service.list_resources`. We do not use OFFSET because\nper the IDAP design, OFFSET is O(n) on large tables.","operationId":"list_booking_flows_api_v1_dashboard_booking_flows_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":32},{"type":"null"}],"description":"Filter by status — one of draft|active|paused|archived.","title":"Status"},"description":"Filter by status — one of draft|active|paused|archived."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}],"description":"Filter by flow kind — one of booking|form|funnel|commerce (SpiderFlow P1.1 discriminator). Omit to list all kinds.","title":"Kind"},"description":"Filter by flow kind — one of booking|form|funnel|commerce (SpiderFlow P1.1 discriminator). Omit to list all kinds."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlowList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Create Booking Flow","description":"Insert a new booking flow (status='draft') in the caller's schema.\n\n``business_id`` is validated by the FK constraint — a cross-tenant\nbusiness_id will fail the INSERT with a ``ForeignKeyViolationError`` which\nwe map to a structured HTTP 404 ``business_not_found`` envelope (P1.Z).\n\nSpiderFlow P1.Z — ``kind='form'`` flows are decoupled from Cal.com /\nSpiderBook businesses. The Pydantic layer forbids the caller from\npassing a business_id when kind='form'; this handler then resolves a\nper-tenant sentinel business so the DB FK stays intact without a\nschema migration. The ``kind`` column is written explicitly (defaults\nto 'booking' for pre-P1.Z callers).","operationId":"create_booking_flow_api_v1_dashboard_booking_flows_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlowCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/clone":{"post":{"tags":["SpiderBook"],"summary":"Clone Booking Flow","description":"Duplicate a flow into a new ``status='draft'`` row in the same schema.\n\nCopies the source's ``flow`` + ``schema`` JSONB, ``kind``, ``model`` and\ntemplate link; the copy gets ``name = \"<source> (copy)\"`` (truncated to\n512), ``version=1`` and fresh actor stamps. Non-destructive → no\ndry_run/confirm gate. Scoped to the caller's schema, so a cross-tenant or\nunknown ``flow_id`` is an indistinguishable 404 (no existence leak).","operationId":"clone_booking_flow_api_v1_dashboard_booking_flows__flow_id__clone_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Flow","operationId":"get_booking_flow_api_v1_dashboard_booking_flows__flow_id__get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Flow","description":"Partial update. Bumps ``version`` whenever ``flow`` or ``schema`` change\nso cached renders of public pages invalidate on the next request (G17).\n\nP1.X — refuses to mutate when ``is_locked=true`` (returns 423 with the\nlock provenance) unless ``force=true`` and the caller is super_admin /\nbrand_admin.","operationId":"patch_booking_flow_api_v1_dashboard_booking_flows__flow_id__patch","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlowPatch"}}}},"responses":{"200":{"description":"Updated flow, OR a Phase 11+12 dry_run preview envelope when dry_run=true","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Flow","description":"Delete a booking flow. Destructive → gated with ``dry_run`` /\n``confirm_token``. P1.X — refuses to delete a locked flow unless\n``force=true`` is passed by a super_admin / brand_admin.","operationId":"delete_booking_flow_api_v1_dashboard_booking_flows__flow_id__delete","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/publish":{"post":{"tags":["SpiderBook"],"summary":"Publish Booking Flow","description":"Publish a booking flow.\n\nProvisions the Cal.com event type via\n:func:`booking_service.provision_event_type` (advisory-lock-guarded).\nDestructive mutation → Phase 11+12 Lock 4 gated.\n\nP1.X — snapshots the post-publish row into ``booking_flow_versions``\nand refuses to publish a locked flow unless ``force=true`` is passed\nby a super_admin / brand_admin.","operationId":"publish_booking_flow_api_v1_dashboard_booking_flows__flow_id__publish_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishFlowRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/unpublish":{"post":{"tags":["SpiderBook"],"summary":"Unpublish Booking Flow","description":"Transition flow to ``status='paused'``. Destructive → gated.\n\nWe keep the Cal.com event type intact (no unlinking) so that a quick\nre-publish simply flips status back to ``active`` without re-provisioning.\nHard-cleanup of Cal.com artifacts is out of scope for P3.2.\n\nP1.X — refuses to unpublish a locked flow unless ``force=true`` is\npassed by a super_admin / brand_admin.","operationId":"unpublish_booking_flow_api_v1_dashboard_booking_flows__flow_id__unpublish_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnpublishFlowRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/validate":{"post":{"tags":["SpiderBook"],"summary":"Validate Booking Flow","description":"Validate a draft flow JSON against ``spiderbook.flow.v1``.\n\nDoes NOT write to the database. Always returns 200 — the ``valid`` field\ndistinguishes success (``true``) from structured failure (``false`` +\n``errors[]``). We still require auth (same as flow CRUD) so anonymous\ncallers can't enumerate the spec via rejection errors.","operationId":"validate_booking_flow_api_v1_dashboard_booking_flows_validate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowValidateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowValidateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/lock":{"post":{"tags":["SpiderBook"],"summary":"Lock Booking Flow","description":"Lock a flow against further mutations.\n\nIdempotent — re-locking refreshes locked_at + locked_reason. Any role\nwith flow-CRUD access can lock; unlocking is more restrictive (the\nlock holder, OR super_admin / brand_admin with ``?force=true``).","operationId":"lock_booking_flow_api_v1_dashboard_booking_flows__flow_id__lock_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"404":{"description":"Flow not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/unlock":{"post":{"tags":["SpiderBook"],"summary":"Unlock Booking Flow","description":"Unlock a flow.\n\nAuthorisation:\n  * Any caller whose ``actor_id`` matches ``locked_by_actor_id`` can unlock.\n  * super_admin / brand_admin can unlock with ``?force=true``.\n  * Otherwise: 403 if force-attempted by an unprivileged role; 404 if\n    the flow is missing OR locked by someone else (so we don't leak the\n    lock-holder identity to unauthorised callers).","operationId":"unlock_booking_flow_api_v1_dashboard_booking_flows__flow_id__unlock_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"super_admin / brand_admin override — unlock regardless of who locked.","default":false,"title":"Force"},"description":"super_admin / brand_admin override — unlock regardless of who locked."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"403":{"description":"Caller is not the lock holder (and not super_admin / brand_admin with force=true)"},"404":{"description":"Flow not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/versions":{"get":{"tags":["SpiderBook"],"summary":"List Booking Flow Versions","description":"List version snapshots for a flow (newest first).\n\nHeavy ``flow`` / ``schema`` / ``translations`` payloads are NOT returned —\nonly metadata + byte counts. Use ``GET /flows/{flow_id}/versions/{N}``\nfor a full snapshot.","operationId":"list_booking_flow_versions_api_v1_dashboard_booking_flows__flow_id__versions_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowVersionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/versions/{version_number}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Flow Version","description":"Fetch a single version snapshot in full (with the JSONB payloads inline).","operationId":"get_booking_flow_version_api_v1_dashboard_booking_flows__flow_id__versions__version_number__get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"version_number","in":"path","required":true,"schema":{"type":"integer","title":"Version Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowVersionDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/restore":{"post":{"tags":["SpiderBook"],"summary":"Restore Booking Flow Version","description":"Restore a flow to a historical version snapshot.\n\nPhase 11+12 gated. ``dry_run=true`` returns a preview envelope with the\nsnapshot summary. The flow lock is enforced — pass ``force=true``\n(super_admin / brand_admin) to override.\n\nAfter a successful restore the flow returns to ``status='draft'`` (matching\nthe page-restore semantics) and a new version row is appended recording\n``change_summary='Restored from v<N>'`` so the audit chain stays linear.","operationId":"restore_booking_flow_version_api_v1_dashboard_booking_flows__flow_id__restore_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"version_number","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Which version to restore (1-indexed).","title":"Version Number"},"description":"Which version to restore (1-indexed)."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRestoreRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"404":{"description":"Flow or version not found"},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/templates/global":{"get":{"tags":["SpiderBook"],"summary":"List Global Booking Templates","description":"Browse the cross-tenant official template library.\n\nAuthentication is still required (any client-scoped user), but there is no\nper-tenant filter: the library is the same for everyone. Agents use this\non onboarding to seed a client's per-client template table.","operationId":"list_global_booking_templates_api_v1_dashboard_booking_templates_global_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"title":"Category"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}],"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all.","title":"Kind"},"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalBookingTemplateList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/template-categories":{"get":{"tags":["SpiderBook"],"summary":"List Form Template Categories Taxonomy","description":"Read-only listing of the form-template category vocabulary.\n\nMirrors the data driving the catalog UI's filter chips. Migration 240\nseeded 5 entries (lead_gen / contact / survey / application / event_rsvp);\nForm Templates v2 (migration 243) adds 15 more. Any authenticated tenant\ncan read this — the table is shared vocabulary, not tenant-private data.\n\nThe admin write surface (POST/PATCH/DELETE) lives under\n``/api/v1/admin/content/form-template-categories``.","operationId":"list_form_template_categories_taxonomy_api_v1_dashboard_booking_template_categories_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormTemplateCategoryItemList"}}}}}}},"/api/v1/dashboard/booking/templates/global/lookup-by-slug":{"get":{"tags":["SpiderBook"],"summary":"Lookup Global Booking Template By Slug","description":"Resolve a global template by its slug (Form Templates v2).\n\nThe catalog UI + the ``form_create_from_template { slug }`` MCP tool key\non slug because it's stable across name edits. This endpoint returns the\nfull row so callers can pass ``template_id`` into ``POST /templates/clone``\nwithout a two-step list-and-filter dance.","operationId":"lookup_global_booking_template_by_slug_api_v1_dashboard_booking_templates_global_lookup_by_slug_get","parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":96,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalBookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/templates/clone":{"post":{"tags":["SpiderBook"],"summary":"Clone Global Booking Template","description":"Clone a global template into the caller's per-client library.\n\nRuns in a single transaction: read global row → insert per-client row →\nbump ``usage_count`` on global. If any step fails, the whole thing rolls\nback (no partial state, no orphaned usage_count increments).","operationId":"clone_global_booking_template_api_v1_dashboard_booking_templates_clone_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplateClone"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/templates":{"post":{"tags":["SpiderBook"],"summary":"Create Booking Template","description":"Create a new per-client template row from scratch.\n\nCompletes the per-tenant booking_templates CRUD that previously only\nsurfaced clone-from-global. The admin form-template authoring surface\n(SpiderFlow P1.M2) uses this to write new form templates; the same\nendpoint serves any future ``kind`` (booking/funnel/commerce) since the\nJSONB ``flow`` column is opaque.\n\nAuth: ``_resolve_user`` enforces a client-scoped session — authoring\nbrands (``clients.is_marketplace_authoring=TRUE``) and any other tenant\ncan both call this; the row lives in the caller's schema, so no global\npromotion happens here.","operationId":"create_booking_template_api_v1_dashboard_booking_templates_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplateCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["SpiderBook"],"summary":"List Booking Templates","description":"Cursor-paginated list of the caller's per-client templates.","operationId":"list_booking_templates_api_v1_dashboard_booking_templates_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"title":"Category"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}],"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all.","title":"Kind"},"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplateList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/templates/{template_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Template","operationId":"get_booking_template_api_v1_dashboard_booking_templates__template_id__get","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Template","operationId":"patch_booking_template_api_v1_dashboard_booking_templates__template_id__patch","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplatePatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Template","operationId":"delete_booking_template_api_v1_dashboard_booking_templates__template_id__delete","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/services":{"get":{"tags":["SpiderBook"],"summary":"List Booking Services","description":"Cursor-paginated list of services scoped to the caller.\n\nBy default returns only non-soft-deleted rows. Sort is\n(created_at DESC, service_id DESC) to match the cursor key used across\nthe booking routers.","operationId":"list_booking_services_api_v1_dashboard_booking_services_get","parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Also return soft-deleted rows.","default":false,"title":"Include Inactive"},"description":"Also return soft-deleted rows."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingServiceList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Create Booking Service","operationId":"create_booking_service_api_v1_dashboard_booking_services_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingServiceCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingService"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/services/{service_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Service","operationId":"get_booking_service_api_v1_dashboard_booking_services__service_id__get","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Service Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingService"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Service","operationId":"patch_booking_service_api_v1_dashboard_booking_services__service_id__patch","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Service Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingServicePatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingService"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Service","description":"Soft-delete a service (``deleted_at = now(), active = false``).\n\nPreserves ``service_id`` so that historical bookings / service_staff rows\ncontinue to resolve. The resource becomes invisible in default list\nresponses and GET returns it only when present (lookup by id still works,\nwhich is fine — tenant isolation is preserved).","operationId":"delete_booking_service_api_v1_dashboard_booking_services__service_id__delete","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Service Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the soft-delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the soft-delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the soft-delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the soft-delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/bookings":{"get":{"tags":["SpiderBook"],"summary":"List Bookings","operationId":"list_bookings_api_v1_dashboard_booking_bookings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BookingSummary"},"type":"array","title":"Response List Bookings Api V1 Dashboard Booking Bookings Get"}}}}}}},"/api/v1/dashboard/booking/analytics/funnel":{"get":{"tags":["SpiderBook"],"summary":"Analytics Funnel","description":"Conversion funnel = renders / submits / confirmed for a flow.\n\n``conversion_rate = confirmed / renders`` (0 when renders=0). The query\ntargets an index-only scan on the ``flow_render_events_flow_idx`` covering\nindex; EXPLAIN ANALYZE for the expected plan lives in research §6.1.","operationId":"analytics_funnel_api_v1_dashboard_booking_analytics_funnel_get","parameters":[{"name":"flow_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Target flow UUID.","title":"Flow Id"},"description":"Target flow UUID."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionFunnelResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/funnel":{"get":{"tags":["SpiderBook"],"summary":"Form Funnel","description":"Views → Starts → Submissions funnel for a ``kind='form'`` flow.\n\nBacks the dashboard form **Results** tab. ``views``/``starts`` are 0 until\nthe form renderer emits render events (Phase 3c) — ``views_tracked`` flags\nthat so the UI shows 'activating soon' rather than a misleading 0%.\nSupports ``?format=yaml|md`` for agent callers.","operationId":"form_funnel_api_v1_dashboard_booking_flows__flow_id__funnel_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/funnel/by-step":{"get":{"tags":["SpiderBook"],"summary":"Form Drop Off By Step","description":"Per-question reach counts for a ``kind='form'`` flow's Results tab.\n\nReturns ``count(DISTINCT visitor_hash)`` per field reached, plus the\n``views`` baseline. Unordered — the dashboard zips these onto the flow's\nown ordered ``fields[]`` to draw the funnel and compute drop-off. ``steps``\nis empty until the renderer emits per-step events (Phase 3c);\n``views_tracked`` flags that so the UI shows 'activating soon'. Supports\n``?format=yaml|md`` for agent callers.","operationId":"form_drop_off_by_step_api_v1_dashboard_booking_flows__flow_id__funnel_by_step_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/analytics/no-show":{"get":{"tags":["SpiderBook"],"summary":"Analytics No Show Rate","description":"No-show rate = no_show / (completed + no_show) over the window.","operationId":"analytics_no_show_rate_api_v1_dashboard_booking_analytics_no_show_get","parameters":[{"name":"business_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Target business UUID.","title":"Business Id"},"description":"Target business UUID."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoShowRateResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/analytics/busiest-slots":{"get":{"tags":["SpiderBook"],"summary":"Analytics Busiest Slots","description":"7×24 day-of-week × hour-of-day heatmap of confirmed bookings.","operationId":"analytics_busiest_slots_api_v1_dashboard_booking_analytics_busiest_slots_get","parameters":[{"name":"business_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Target business UUID.","title":"Business Id"},"description":"Target business UUID."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusiestSlotsResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/resources":{"get":{"tags":["SpiderBook"],"summary":"List Booking Resources","description":"List resources for the caller's tenant, keyset-paginated.\n\nSort order mirrors the resources page UX: ``(sort_order ASC, created_at DESC,\nid DESC)`` — pinned rows first, then recency.","operationId":"list_booking_resources_api_v1_dashboard_booking_resources_get","parameters":[{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"enum":["staff","room","equipment"],"type":"string"},{"type":"null"}],"description":"Filter by kind.","title":"Kind"},"description":"Filter by kind."},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter by active flag.","title":"Is Active"},"description":"Filter by active flag."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Create Booking Resource","description":"Create a new resource in the caller's tenant.","operationId":"create_booking_resource_api_v1_dashboard_booking_resources_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/resources/{resource_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Resource","operationId":"get_booking_resource_api_v1_dashboard_booking_resources__resource_id__get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Resource Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Resource","description":"Partial update; ``kind`` is immutable and enforced at the schema layer\n(see :class:`ResourceUpdate` — no ``kind`` field).","operationId":"patch_booking_resource_api_v1_dashboard_booking_resources__resource_id__patch","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Resource Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Resource","description":"Soft-delete by default (sets ``is_active=false``). Hard-delete only\nwhen ``?hard=true`` AND there are zero future assignments AND the\nPhase 11+12 destructive gate approves.","operationId":"delete_booking_resource_api_v1_dashboard_booking_resources__resource_id__delete","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Resource Id"}},{"name":"hard","in":"query","required":false,"schema":{"type":"boolean","description":"Hard-delete (only if 0 future assignments).","default":false,"title":"Hard"},"description":"Hard-delete (only if 0 future assignments)."},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12 preview.","default":false,"title":"Dry Run"},"description":"Phase 11+12 preview."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirm Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/calendar":{"get":{"tags":["SpiderBook"],"summary":"Get Calendar View","description":"Return resources + bookings for a single UTC day, shaped for the\nswimlane view.\n\nThe frontend does its own timezone display; the backend returns\ntimestamps in UTC. If the caller needs a different TZ, they pass the\nappropriate UTC date for that TZ — we do NOT apply a\n``clients.booking_settings.general.timezone`` shift here (keeping the\nendpoint stateless against settings).","operationId":"get_calendar_view_api_v1_dashboard_booking_calendar_get","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","minLength":10,"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"UTC date (YYYY-MM-DD) — swimlane covers [00:00, 24:00) UTC.","title":"Date"},"description":"UTC date (YYYY-MM-DD) — swimlane covers [00:00, 24:00) UTC."},{"name":"resource_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional filter: staff | room | equipment | all.","title":"Resource Kind"},"description":"Optional filter: staff | room | equipment | all."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/customers":{"get":{"tags":["SpiderBook"],"summary":"List Booking Customers","description":"List customers aggregated from ``bookings.answers``.\n\nAggregation is per-request (no materialized view yet). For tenants with\n>100k bookings this will get slow — revisit when that becomes real.","operationId":"list_booking_customers_api_v1_dashboard_booking_customers_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":120},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/customers/{customer_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Customer","operationId":"get_booking_customer_api_v1_dashboard_booking_customers__customer_id__get","parameters":[{"name":"customer_id","in":"path","required":true,"schema":{"type":"string","title":"Customer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerProfile"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/settings":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Settings","description":"Return the current tenant's booking settings. Unset sub-sections are\npopulated with :meth:`BookingSettings.default` values so the frontend\nalways sees a complete shape.","operationId":"get_booking_settings_api_v1_dashboard_booking_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSettings"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Settings","description":"Deep-merge the provided sub-sections into the stored settings.\n\nValidation order:\n\n1. Read stored JSONB into a dict.\n2. For each provided sub-section, shallow-merge the patch onto the\n   current value for that key.\n3. Run the merged dict through :class:`BookingSettings` validation — 422\n   on rejection (so a bad color hex / out-of-range percent doesn't land).\n4. Persist via one ``UPDATE public.clients`` and return the validated\n   envelope.","operationId":"patch_booking_settings_api_v1_dashboard_booking_settings_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSettingsPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSettings"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/submissions":{"get":{"tags":["SpiderBook"],"summary":"List Flow Submissions","description":"Cursor-paginated list of form submissions for a flow.\n\nAlways returns 200 — an empty flow returns ``{items: [], next_cursor: null}``,\nnot a 404. Cross-tenant rows are filtered out via ``client_id = user.client_id``.","operationId":"list_flow_submissions_api_v1_dashboard_booking_flows__flow_id__submissions_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmissionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/flows/{flow_id}/submissions.csv":{"get":{"tags":["SpiderBook"],"summary":"Export Flow Submissions Csv","description":"Export all of a form's submissions as CSV.\n\nColumns: ``submitted_at`` · one column per distinct answer key (sorted) ·\n``source`` (utm_source hidden field) · ``crm_status``. Tenant-scoped via\n``_resolve_user`` (impersonation-aware). Capped at ``_CSV_MAX_ROWS`` rows.\nReturns ``text/csv`` with a download ``Content-Disposition``.","operationId":"export_flow_submissions_csv_api_v1_dashboard_booking_flows__flow_id__submissions_csv_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/submissions/{submission_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Flow Submission","description":"Fetch one submission by id. 404 if missing or owned by another tenant.","operationId":"get_flow_submission_api_v1_dashboard_booking_submissions__submission_id__get","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmissionItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/booking/submissions/{submission_id}/crm-replay":{"post":{"tags":["SpiderBook"],"summary":"Replay Submission Crm Write","description":"Re-run the CRM dual-write for one form submission.\n\nW10.2 dual-writes mapped answers to the tenant's CRM tables at submit\ntime and stamps ``public.results.crm_status``. When that write fails\n(schema drift, a transient constraint violation) the row keeps its raw\nJSONB audit and ``crm_status='failed'``. This endpoint lets a dashboard\noperator retry the write per-row — the canonical JSONB row is the\nsource of truth, so a replay is just ``dual_write_form_answers`` run\nagain against the *current* tenant schema and form definition.\n\nIdempotent: every per-resource write is an UPSERT, so replaying a row\nthat already synced is harmless. Returns the freshly re-stamped outcome.\nCron-based bulk replay is out of P1.W10 scope — this is per-row only.","operationId":"replay_submission_crm_write_api_v1_dashboard_booking_submissions__submission_id__crm_replay_post","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrmReplayResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows":{"get":{"tags":["SpiderBook"],"summary":"List Booking Flows","description":"Cursor-paginated list of booking flows scoped to the caller's client.\n\nPagination is keyset on (created_at DESC, flow_id DESC) — same contract as\n:func:`services.idap_service.list_resources`. We do not use OFFSET because\nper the IDAP design, OFFSET is O(n) on large tables.","operationId":"list_booking_flows_api_v1_dashboard_projects__project_id__booking_flows_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":32},{"type":"null"}],"description":"Filter by status — one of draft|active|paused|archived.","title":"Status"},"description":"Filter by status — one of draft|active|paused|archived."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}],"description":"Filter by flow kind — one of booking|form|funnel|commerce (SpiderFlow P1.1 discriminator). Omit to list all kinds.","title":"Kind"},"description":"Filter by flow kind — one of booking|form|funnel|commerce (SpiderFlow P1.1 discriminator). Omit to list all kinds."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlowList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Create Booking Flow","description":"Insert a new booking flow (status='draft') in the caller's schema.\n\n``business_id`` is validated by the FK constraint — a cross-tenant\nbusiness_id will fail the INSERT with a ``ForeignKeyViolationError`` which\nwe map to a structured HTTP 404 ``business_not_found`` envelope (P1.Z).\n\nSpiderFlow P1.Z — ``kind='form'`` flows are decoupled from Cal.com /\nSpiderBook businesses. The Pydantic layer forbids the caller from\npassing a business_id when kind='form'; this handler then resolves a\nper-tenant sentinel business so the DB FK stays intact without a\nschema migration. The ``kind`` column is written explicitly (defaults\nto 'booking' for pre-P1.Z callers).","operationId":"create_booking_flow_api_v1_dashboard_projects__project_id__booking_flows_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlowCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/clone":{"post":{"tags":["SpiderBook"],"summary":"Clone Booking Flow","description":"Duplicate a flow into a new ``status='draft'`` row in the same schema.\n\nCopies the source's ``flow`` + ``schema`` JSONB, ``kind``, ``model`` and\ntemplate link; the copy gets ``name = \"<source> (copy)\"`` (truncated to\n512), ``version=1`` and fresh actor stamps. Non-destructive → no\ndry_run/confirm gate. Scoped to the caller's schema, so a cross-tenant or\nunknown ``flow_id`` is an indistinguishable 404 (no existence leak).","operationId":"clone_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__clone_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Flow","operationId":"get_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Flow","description":"Partial update. Bumps ``version`` whenever ``flow`` or ``schema`` change\nso cached renders of public pages invalidate on the next request (G17).\n\nP1.X — refuses to mutate when ``is_locked=true`` (returns 423 with the\nlock provenance) unless ``force=true`` and the caller is super_admin /\nbrand_admin.","operationId":"patch_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__patch","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlowPatch"}}}},"responses":{"200":{"description":"Updated flow, OR a Phase 11+12 dry_run preview envelope when dry_run=true","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Flow","description":"Delete a booking flow. Destructive → gated with ``dry_run`` /\n``confirm_token``. P1.X — refuses to delete a locked flow unless\n``force=true`` is passed by a super_admin / brand_admin.","operationId":"delete_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__delete","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/publish":{"post":{"tags":["SpiderBook"],"summary":"Publish Booking Flow","description":"Publish a booking flow.\n\nProvisions the Cal.com event type via\n:func:`booking_service.provision_event_type` (advisory-lock-guarded).\nDestructive mutation → Phase 11+12 Lock 4 gated.\n\nP1.X — snapshots the post-publish row into ``booking_flow_versions``\nand refuses to publish a locked flow unless ``force=true`` is passed\nby a super_admin / brand_admin.","operationId":"publish_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__publish_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishFlowRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/unpublish":{"post":{"tags":["SpiderBook"],"summary":"Unpublish Booking Flow","description":"Transition flow to ``status='paused'``. Destructive → gated.\n\nWe keep the Cal.com event type intact (no unlinking) so that a quick\nre-publish simply flips status back to ``active`` without re-provisioning.\nHard-cleanup of Cal.com artifacts is out of scope for P3.2.\n\nP1.X — refuses to unpublish a locked flow unless ``force=true`` is\npassed by a super_admin / brand_admin.","operationId":"unpublish_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__unpublish_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnpublishFlowRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/validate":{"post":{"tags":["SpiderBook"],"summary":"Validate Booking Flow","description":"Validate a draft flow JSON against ``spiderbook.flow.v1``.\n\nDoes NOT write to the database. Always returns 200 — the ``valid`` field\ndistinguishes success (``true``) from structured failure (``false`` +\n``errors[]``). We still require auth (same as flow CRUD) so anonymous\ncallers can't enumerate the spec via rejection errors.","operationId":"validate_booking_flow_api_v1_dashboard_projects__project_id__booking_flows_validate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowValidateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowValidateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/lock":{"post":{"tags":["SpiderBook"],"summary":"Lock Booking Flow","description":"Lock a flow against further mutations.\n\nIdempotent — re-locking refreshes locked_at + locked_reason. Any role\nwith flow-CRUD access can lock; unlocking is more restrictive (the\nlock holder, OR super_admin / brand_admin with ``?force=true``).","operationId":"lock_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__lock_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"404":{"description":"Flow not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/unlock":{"post":{"tags":["SpiderBook"],"summary":"Unlock Booking Flow","description":"Unlock a flow.\n\nAuthorisation:\n  * Any caller whose ``actor_id`` matches ``locked_by_actor_id`` can unlock.\n  * super_admin / brand_admin can unlock with ``?force=true``.\n  * Otherwise: 403 if force-attempted by an unprivileged role; 404 if\n    the flow is missing OR locked by someone else (so we don't leak the\n    lock-holder identity to unauthorised callers).","operationId":"unlock_booking_flow_api_v1_dashboard_projects__project_id__booking_flows__flow_id__unlock_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"super_admin / brand_admin override — unlock regardless of who locked.","default":false,"title":"Force"},"description":"super_admin / brand_admin override — unlock regardless of who locked."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingFlow"}}}},"403":{"description":"Caller is not the lock holder (and not super_admin / brand_admin with force=true)"},"404":{"description":"Flow not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/versions":{"get":{"tags":["SpiderBook"],"summary":"List Booking Flow Versions","description":"List version snapshots for a flow (newest first).\n\nHeavy ``flow`` / ``schema`` / ``translations`` payloads are NOT returned —\nonly metadata + byte counts. Use ``GET /flows/{flow_id}/versions/{N}``\nfor a full snapshot.","operationId":"list_booking_flow_versions_api_v1_dashboard_projects__project_id__booking_flows__flow_id__versions_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowVersionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/versions/{version_number}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Flow Version","description":"Fetch a single version snapshot in full (with the JSONB payloads inline).","operationId":"get_booking_flow_version_api_v1_dashboard_projects__project_id__booking_flows__flow_id__versions__version_number__get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"version_number","in":"path","required":true,"schema":{"type":"integer","title":"Version Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowVersionDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/restore":{"post":{"tags":["SpiderBook"],"summary":"Restore Booking Flow Version","description":"Restore a flow to a historical version snapshot.\n\nPhase 11+12 gated. ``dry_run=true`` returns a preview envelope with the\nsnapshot summary. The flow lock is enforced — pass ``force=true``\n(super_admin / brand_admin) to override.\n\nAfter a successful restore the flow returns to ``status='draft'`` (matching\nthe page-restore semantics) and a new version row is appended recording\n``change_summary='Restored from v<N>'`` so the audit chain stays linear.","operationId":"restore_booking_flow_version_api_v1_dashboard_projects__project_id__booking_flows__flow_id__restore_post","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"version_number","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"Which version to restore (1-indexed).","title":"Version Number"},"description":"Which version to restore (1-indexed)."},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"P1.X: bypass the flow lock (super_admin / brand_admin only).","default":false,"title":"Force"},"description":"P1.X: bypass the flow lock (super_admin / brand_admin only)."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRestoreRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"404":{"description":"Flow or version not found"},"423":{"description":"Flow is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowLockedErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/templates/global":{"get":{"tags":["SpiderBook"],"summary":"List Global Booking Templates","description":"Browse the cross-tenant official template library.\n\nAuthentication is still required (any client-scoped user), but there is no\nper-tenant filter: the library is the same for everyone. Agents use this\non onboarding to seed a client's per-client template table.","operationId":"list_global_booking_templates_api_v1_dashboard_projects__project_id__booking_templates_global_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"title":"Category"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}],"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all.","title":"Kind"},"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalBookingTemplateList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/template-categories":{"get":{"tags":["SpiderBook"],"summary":"List Form Template Categories Taxonomy","description":"Read-only listing of the form-template category vocabulary.\n\nMirrors the data driving the catalog UI's filter chips. Migration 240\nseeded 5 entries (lead_gen / contact / survey / application / event_rsvp);\nForm Templates v2 (migration 243) adds 15 more. Any authenticated tenant\ncan read this — the table is shared vocabulary, not tenant-private data.\n\nThe admin write surface (POST/PATCH/DELETE) lives under\n``/api/v1/admin/content/form-template-categories``.","operationId":"list_form_template_categories_taxonomy_api_v1_dashboard_projects__project_id__booking_template_categories_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormTemplateCategoryItemList"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/templates/global/lookup-by-slug":{"get":{"tags":["SpiderBook"],"summary":"Lookup Global Booking Template By Slug","description":"Resolve a global template by its slug (Form Templates v2).\n\nThe catalog UI + the ``form_create_from_template { slug }`` MCP tool key\non slug because it's stable across name edits. This endpoint returns the\nfull row so callers can pass ``template_id`` into ``POST /templates/clone``\nwithout a two-step list-and-filter dance.","operationId":"lookup_global_booking_template_by_slug_api_v1_dashboard_projects__project_id__booking_templates_global_lookup_by_slug_get","parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":96,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalBookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/templates/clone":{"post":{"tags":["SpiderBook"],"summary":"Clone Global Booking Template","description":"Clone a global template into the caller's per-client library.\n\nRuns in a single transaction: read global row → insert per-client row →\nbump ``usage_count`` on global. If any step fails, the whole thing rolls\nback (no partial state, no orphaned usage_count increments).","operationId":"clone_global_booking_template_api_v1_dashboard_projects__project_id__booking_templates_clone_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplateClone"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/templates":{"post":{"tags":["SpiderBook"],"summary":"Create Booking Template","description":"Create a new per-client template row from scratch.\n\nCompletes the per-tenant booking_templates CRUD that previously only\nsurfaced clone-from-global. The admin form-template authoring surface\n(SpiderFlow P1.M2) uses this to write new form templates; the same\nendpoint serves any future ``kind`` (booking/funnel/commerce) since the\nJSONB ``flow`` column is opaque.\n\nAuth: ``_resolve_user`` enforces a client-scoped session — authoring\nbrands (``clients.is_marketplace_authoring=TRUE``) and any other tenant\ncan both call this; the row lives in the caller's schema, so no global\npromotion happens here.","operationId":"create_booking_template_api_v1_dashboard_projects__project_id__booking_templates_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplateCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["SpiderBook"],"summary":"List Booking Templates","description":"Cursor-paginated list of the caller's per-client templates.","operationId":"list_booking_templates_api_v1_dashboard_projects__project_id__booking_templates_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"title":"Category"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}],"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all.","title":"Kind"},"description":"Filter by FlowKind stored inside ``flow.kind`` (SpiderFlow P1.M2). One of booking|form|funnel|commerce; omit for all."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplateList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/templates/{template_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Template","operationId":"get_booking_template_api_v1_dashboard_projects__project_id__booking_templates__template_id__get","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Template","operationId":"patch_booking_template_api_v1_dashboard_projects__project_id__booking_templates__template_id__patch","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplatePatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTemplate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Template","operationId":"delete_booking_template_api_v1_dashboard_projects__project_id__booking_templates__template_id__delete","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/services":{"get":{"tags":["SpiderBook"],"summary":"List Booking Services","description":"Cursor-paginated list of services scoped to the caller.\n\nBy default returns only non-soft-deleted rows. Sort is\n(created_at DESC, service_id DESC) to match the cursor key used across\nthe booking routers.","operationId":"list_booking_services_api_v1_dashboard_projects__project_id__booking_services_get","parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Also return soft-deleted rows.","default":false,"title":"Include Inactive"},"description":"Also return soft-deleted rows."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingServiceList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Create Booking Service","operationId":"create_booking_service_api_v1_dashboard_projects__project_id__booking_services_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingServiceCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingService"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/services/{service_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Service","operationId":"get_booking_service_api_v1_dashboard_projects__project_id__booking_services__service_id__get","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Service Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingService"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Service","operationId":"patch_booking_service_api_v1_dashboard_projects__project_id__booking_services__service_id__patch","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Service Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingServicePatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingService"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Service","description":"Soft-delete a service (``deleted_at = now(), active = false``).\n\nPreserves ``service_id`` so that historical bookings / service_staff rows\ncontinue to resolve. The resource becomes invisible in default list\nresponses and GET returns it only when present (lookup by id still works,\nwhich is fine — tenant isolation is preserved).","operationId":"delete_booking_service_api_v1_dashboard_projects__project_id__booking_services__service_id__delete","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Service Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the soft-delete and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the soft-delete and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the soft-delete.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the soft-delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/bookings":{"get":{"tags":["SpiderBook"],"summary":"List Bookings","operationId":"list_bookings_api_v1_dashboard_projects__project_id__booking_bookings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BookingSummary"},"type":"array","title":"Response List Bookings Api V1 Dashboard Projects  Project Id  Booking Bookings Get"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/analytics/funnel":{"get":{"tags":["SpiderBook"],"summary":"Analytics Funnel","description":"Conversion funnel = renders / submits / confirmed for a flow.\n\n``conversion_rate = confirmed / renders`` (0 when renders=0). The query\ntargets an index-only scan on the ``flow_render_events_flow_idx`` covering\nindex; EXPLAIN ANALYZE for the expected plan lives in research §6.1.","operationId":"analytics_funnel_api_v1_dashboard_projects__project_id__booking_analytics_funnel_get","parameters":[{"name":"flow_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Target flow UUID.","title":"Flow Id"},"description":"Target flow UUID."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionFunnelResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/funnel":{"get":{"tags":["SpiderBook"],"summary":"Form Funnel","description":"Views → Starts → Submissions funnel for a ``kind='form'`` flow.\n\nBacks the dashboard form **Results** tab. ``views``/``starts`` are 0 until\nthe form renderer emits render events (Phase 3c) — ``views_tracked`` flags\nthat so the UI shows 'activating soon' rather than a misleading 0%.\nSupports ``?format=yaml|md`` for agent callers.","operationId":"form_funnel_api_v1_dashboard_projects__project_id__booking_flows__flow_id__funnel_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/funnel/by-step":{"get":{"tags":["SpiderBook"],"summary":"Form Drop Off By Step","description":"Per-question reach counts for a ``kind='form'`` flow's Results tab.\n\nReturns ``count(DISTINCT visitor_hash)`` per field reached, plus the\n``views`` baseline. Unordered — the dashboard zips these onto the flow's\nown ordered ``fields[]`` to draw the funnel and compute drop-off. ``steps``\nis empty until the renderer emits per-step events (Phase 3c);\n``views_tracked`` flags that so the UI shows 'activating soon'. Supports\n``?format=yaml|md`` for agent callers.","operationId":"form_drop_off_by_step_api_v1_dashboard_projects__project_id__booking_flows__flow_id__funnel_by_step_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/analytics/no-show":{"get":{"tags":["SpiderBook"],"summary":"Analytics No Show Rate","description":"No-show rate = no_show / (completed + no_show) over the window.","operationId":"analytics_no_show_rate_api_v1_dashboard_projects__project_id__booking_analytics_no_show_get","parameters":[{"name":"business_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Target business UUID.","title":"Business Id"},"description":"Target business UUID."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoShowRateResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/analytics/busiest-slots":{"get":{"tags":["SpiderBook"],"summary":"Analytics Busiest Slots","description":"7×24 day-of-week × hour-of-day heatmap of confirmed bookings.","operationId":"analytics_busiest_slots_api_v1_dashboard_projects__project_id__booking_analytics_busiest_slots_get","parameters":[{"name":"business_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Target business UUID.","title":"Business Id"},"description":"Target business UUID."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Inclusive lower bound (UTC). Defaults to now - 30 days.","title":"Since"},"description":"Inclusive lower bound (UTC). Defaults to now - 30 days."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusiestSlotsResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/resources":{"get":{"tags":["SpiderBook"],"summary":"List Booking Resources","description":"List resources for the caller's tenant, keyset-paginated.\n\nSort order mirrors the resources page UX: ``(sort_order ASC, created_at DESC,\nid DESC)`` — pinned rows first, then recency.","operationId":"list_booking_resources_api_v1_dashboard_projects__project_id__booking_resources_get","parameters":[{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"enum":["staff","room","equipment"],"type":"string"},{"type":"null"}],"description":"Filter by kind.","title":"Kind"},"description":"Filter by kind."},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter by active flag.","title":"Is Active"},"description":"Filter by active flag."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SpiderBook"],"summary":"Create Booking Resource","description":"Create a new resource in the caller's tenant.","operationId":"create_booking_resource_api_v1_dashboard_projects__project_id__booking_resources_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/resources/{resource_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Resource","operationId":"get_booking_resource_api_v1_dashboard_projects__project_id__booking_resources__resource_id__get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Resource Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Resource","description":"Partial update; ``kind`` is immutable and enforced at the schema layer\n(see :class:`ResourceUpdate` — no ``kind`` field).","operationId":"patch_booking_resource_api_v1_dashboard_projects__project_id__booking_resources__resource_id__patch","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Resource Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SpiderBook"],"summary":"Delete Booking Resource","description":"Soft-delete by default (sets ``is_active=false``). Hard-delete only\nwhen ``?hard=true`` AND there are zero future assignments AND the\nPhase 11+12 destructive gate approves.","operationId":"delete_booking_resource_api_v1_dashboard_projects__project_id__booking_resources__resource_id__delete","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Resource Id"}},{"name":"hard","in":"query","required":false,"schema":{"type":"boolean","description":"Hard-delete (only if 0 future assignments).","default":false,"title":"Hard"},"description":"Hard-delete (only if 0 future assignments)."},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12 preview.","default":false,"title":"Dry Run"},"description":"Phase 11+12 preview."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirm Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/calendar":{"get":{"tags":["SpiderBook"],"summary":"Get Calendar View","description":"Return resources + bookings for a single UTC day, shaped for the\nswimlane view.\n\nThe frontend does its own timezone display; the backend returns\ntimestamps in UTC. If the caller needs a different TZ, they pass the\nappropriate UTC date for that TZ — we do NOT apply a\n``clients.booking_settings.general.timezone`` shift here (keeping the\nendpoint stateless against settings).","operationId":"get_calendar_view_api_v1_dashboard_projects__project_id__booking_calendar_get","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","minLength":10,"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"UTC date (YYYY-MM-DD) — swimlane covers [00:00, 24:00) UTC.","title":"Date"},"description":"UTC date (YYYY-MM-DD) — swimlane covers [00:00, 24:00) UTC."},{"name":"resource_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional filter: staff | room | equipment | all.","title":"Resource Kind"},"description":"Optional filter: staff | room | equipment | all."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/customers":{"get":{"tags":["SpiderBook"],"summary":"List Booking Customers","description":"List customers aggregated from ``bookings.answers``.\n\nAggregation is per-request (no materialized view yet). For tenants with\n>100k bookings this will get slow — revisit when that becomes real.","operationId":"list_booking_customers_api_v1_dashboard_projects__project_id__booking_customers_get","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":120},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/customers/{customer_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Customer","operationId":"get_booking_customer_api_v1_dashboard_projects__project_id__booking_customers__customer_id__get","parameters":[{"name":"customer_id","in":"path","required":true,"schema":{"type":"string","title":"Customer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerProfile"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/settings":{"get":{"tags":["SpiderBook"],"summary":"Get Booking Settings","description":"Return the current tenant's booking settings. Unset sub-sections are\npopulated with :meth:`BookingSettings.default` values so the frontend\nalways sees a complete shape.","operationId":"get_booking_settings_api_v1_dashboard_projects__project_id__booking_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSettings"}}}}}},"patch":{"tags":["SpiderBook"],"summary":"Patch Booking Settings","description":"Deep-merge the provided sub-sections into the stored settings.\n\nValidation order:\n\n1. Read stored JSONB into a dict.\n2. For each provided sub-section, shallow-merge the patch onto the\n   current value for that key.\n3. Run the merged dict through :class:`BookingSettings` validation — 422\n   on rejection (so a bad color hex / out-of-range percent doesn't land).\n4. Persist via one ``UPDATE public.clients`` and return the validated\n   envelope.","operationId":"patch_booking_settings_api_v1_dashboard_projects__project_id__booking_settings_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSettingsPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSettings"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/submissions":{"get":{"tags":["SpiderBook"],"summary":"List Flow Submissions","description":"Cursor-paginated list of form submissions for a flow.\n\nAlways returns 200 — an empty flow returns ``{items: [], next_cursor: null}``,\nnot a 404. Cross-tenant rows are filtered out via ``client_id = user.client_id``.","operationId":"list_flow_submissions_api_v1_dashboard_projects__project_id__booking_flows__flow_id__submissions_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmissionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/flows/{flow_id}/submissions.csv":{"get":{"tags":["SpiderBook"],"summary":"Export Flow Submissions Csv","description":"Export all of a form's submissions as CSV.\n\nColumns: ``submitted_at`` · one column per distinct answer key (sorted) ·\n``source`` (utm_source hidden field) · ``crm_status``. Tenant-scoped via\n``_resolve_user`` (impersonation-aware). Capped at ``_CSV_MAX_ROWS`` rows.\nReturns ``text/csv`` with a download ``Content-Disposition``.","operationId":"export_flow_submissions_csv_api_v1_dashboard_projects__project_id__booking_flows__flow_id__submissions_csv_get","parameters":[{"name":"flow_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Flow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/submissions/{submission_id}":{"get":{"tags":["SpiderBook"],"summary":"Get Flow Submission","description":"Fetch one submission by id. 404 if missing or owned by another tenant.","operationId":"get_flow_submission_api_v1_dashboard_projects__project_id__booking_submissions__submission_id__get","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmissionItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/booking/submissions/{submission_id}/crm-replay":{"post":{"tags":["SpiderBook"],"summary":"Replay Submission Crm Write","description":"Re-run the CRM dual-write for one form submission.\n\nW10.2 dual-writes mapped answers to the tenant's CRM tables at submit\ntime and stamps ``public.results.crm_status``. When that write fails\n(schema drift, a transient constraint violation) the row keeps its raw\nJSONB audit and ``crm_status='failed'``. This endpoint lets a dashboard\noperator retry the write per-row — the canonical JSONB row is the\nsource of truth, so a replay is just ``dual_write_form_answers`` run\nagain against the *current* tenant schema and form definition.\n\nIdempotent: every per-resource write is an UPSERT, so replaying a row\nthat already synced is harmless. Returns the freshly re-stamped outcome.\nCron-based bulk replay is out of P1.W10 scope — this is per-row only.","operationId":"replay_submission_crm_write_api_v1_dashboard_projects__project_id__booking_submissions__submission_id__crm_replay_post","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrmReplayResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/scroll-sequence/from-video":{"post":{"tags":["Scroll Sequence"],"summary":"One-shot video -> scroll-sequence block on a draft page","description":"Runs `extract_frames` against the source video, polls to completion, and inserts a `sys-scroll-sequence` component block into the target page as a new draft. Caller then runs `content_deploy_site_preview` -> `content_deploy_site_production` with a confirm_token.","operationId":"create_scroll_sequence_api_v1_dashboard_scroll_sequence_from_video_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrollSequenceFromVideoRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrollSequenceFromVideoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/scroll-sequence/from-video":{"post":{"tags":["Scroll Sequence"],"summary":"One-shot video -> scroll-sequence block on a draft page","description":"Runs `extract_frames` against the source video, polls to completion, and inserts a `sys-scroll-sequence` component block into the target page as a new draft. Caller then runs `content_deploy_site_preview` -> `content_deploy_site_production` with a confirm_token.","operationId":"create_scroll_sequence_api_v1_dashboard_projects__project_id__scroll_sequence_from_video_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrollSequenceFromVideoRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrollSequenceFromVideoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/scroll-sequence/from-video":{"post":{"tags":["Scroll Sequence"],"summary":"One-shot video -> scroll-sequence block on a draft page","description":"Runs `extract_frames` against the source video, polls to completion, and inserts a `sys-scroll-sequence` component block into the target page as a new draft. Caller then runs `content_deploy_site_preview` -> `content_deploy_site_production` with a confirm_token.","operationId":"create_scroll_sequence_api_v1_dashboard_content_scroll_sequence_from_video_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrollSequenceFromVideoRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrollSequenceFromVideoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/agent-shifts":{"get":{"tags":["dashboard-agent-shifts"],"summary":"List Shifts","description":"List shifts for the requesting client in the time window.","operationId":"list_shifts_api_v1_dashboard_content_agent_shifts_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD — single day (UTC)","title":"Date"},"description":"YYYY-MM-DD — single day (UTC)"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD — range start (inclusive)","title":"From"},"description":"YYYY-MM-DD — range start (inclusive)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD — range end (inclusive)","title":"To"},"description":"YYYY-MM-DD — range end (inclusive)"},{"name":"actor_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(api_client|dashboard_user)$"},{"type":"null"}],"description":"Filter by actor role","title":"Role"},"description":"Filter by actor role"},{"name":"gap_minutes","in":"query","required":false,"schema":{"type":"integer","maximum":120,"minimum":1,"description":"Gap threshold that splits one shift into two","default":10,"title":"Gap Minutes"},"description":"Gap threshold that splits one shift into two"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Shifts Api V1 Dashboard Content Agent Shifts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/agent-shifts/detail":{"get":{"tags":["dashboard-agent-shifts"],"summary":"Shift Detail","description":"Full detail view for a single shift — hourly histogram + shipped lists.\n\n`started_at` and `ended_at` are taken verbatim from the list response; the\nservice re-queries the audit table for the exact window so the caller\nnever needs to round-trip a persisted \"shift_id\".","operationId":"shift_detail_api_v1_dashboard_content_agent_shifts_detail_get","parameters":[{"name":"actor_id","in":"query","required":true,"schema":{"type":"string","description":"Actor id from the list response","title":"Actor Id"},"description":"Actor id from the list response"},{"name":"started_at","in":"query","required":true,"schema":{"type":"string","description":"ISO-8601 UTC of shift start","title":"Started At"},"description":"ISO-8601 UTC of shift start"},{"name":"ended_at","in":"query","required":true,"schema":{"type":"string","description":"ISO-8601 UTC of shift end","title":"Ended At"},"description":"ISO-8601 UTC of shift end"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Shift Detail Api V1 Dashboard Content Agent Shifts Detail Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/agent-shifts/export.csv":{"get":{"tags":["dashboard-agent-shifts"],"summary":"Export Shifts Csv","description":"One row per shift in the window. Billing- and spreadsheet-friendly.","operationId":"export_shifts_csv_api_v1_dashboard_content_agent_shifts_export_csv_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"}},{"name":"actor_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(api_client|dashboard_user)$"},{"type":"null"}],"title":"Role"}},{"name":"gap_minutes","in":"query","required":false,"schema":{"type":"integer","maximum":120,"minimum":1,"default":10,"title":"Gap Minutes"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/agent-shifts/share":{"post":{"tags":["dashboard-agent-shifts"],"summary":"Create Share Link","description":"Issue a 30-day HMAC-signed share URL for the given date range.","operationId":"create_share_link_api_v1_dashboard_content_agent_shifts_share_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Create Share Link Api V1 Dashboard Content Agent Shifts Share Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/agent-shifts/digest-settings":{"get":{"tags":["dashboard-agent-shifts"],"summary":"Get Digest Settings","description":"Return the tenant's current digest cadence + last-sent timestamp.","operationId":"get_digest_settings_api_v1_dashboard_content_agent_shifts_digest_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSettingsResponse"}}}}}},"patch":{"tags":["dashboard-agent-shifts"],"summary":"Update Digest Settings","description":"Change the tenant's digest cadence. Resets last_sent_at when cadence\nchanges so the next eligible tick fires immediately.","operationId":"update_digest_settings_api_v1_dashboard_content_agent_shifts_digest_settings_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSettingsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/agent-shifts/anomalies":{"get":{"tags":["dashboard-agent-shifts"],"summary":"List Anomalies","description":"Per-client anomaly findings (last 7 days vs 30-day baseline).","operationId":"list_anomalies_api_v1_dashboard_content_agent_shifts_anomalies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Anomalies Api V1 Dashboard Content Agent Shifts Anomalies Get"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/agent-shifts":{"get":{"tags":["dashboard-agent-shifts"],"summary":"List Shifts","description":"List shifts for the requesting client in the time window.","operationId":"list_shifts_api_v1_dashboard_projects__project_id__content_agent_shifts_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD — single day (UTC)","title":"Date"},"description":"YYYY-MM-DD — single day (UTC)"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD — range start (inclusive)","title":"From"},"description":"YYYY-MM-DD — range start (inclusive)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD — range end (inclusive)","title":"To"},"description":"YYYY-MM-DD — range end (inclusive)"},{"name":"actor_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(api_client|dashboard_user)$"},{"type":"null"}],"description":"Filter by actor role","title":"Role"},"description":"Filter by actor role"},{"name":"gap_minutes","in":"query","required":false,"schema":{"type":"integer","maximum":120,"minimum":1,"description":"Gap threshold that splits one shift into two","default":10,"title":"Gap Minutes"},"description":"Gap threshold that splits one shift into two"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Shifts Api V1 Dashboard Projects  Project Id  Content Agent Shifts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/agent-shifts/detail":{"get":{"tags":["dashboard-agent-shifts"],"summary":"Shift Detail","description":"Full detail view for a single shift — hourly histogram + shipped lists.\n\n`started_at` and `ended_at` are taken verbatim from the list response; the\nservice re-queries the audit table for the exact window so the caller\nnever needs to round-trip a persisted \"shift_id\".","operationId":"shift_detail_api_v1_dashboard_projects__project_id__content_agent_shifts_detail_get","parameters":[{"name":"actor_id","in":"query","required":true,"schema":{"type":"string","description":"Actor id from the list response","title":"Actor Id"},"description":"Actor id from the list response"},{"name":"started_at","in":"query","required":true,"schema":{"type":"string","description":"ISO-8601 UTC of shift start","title":"Started At"},"description":"ISO-8601 UTC of shift start"},{"name":"ended_at","in":"query","required":true,"schema":{"type":"string","description":"ISO-8601 UTC of shift end","title":"Ended At"},"description":"ISO-8601 UTC of shift end"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Shift Detail Api V1 Dashboard Projects  Project Id  Content Agent Shifts Detail Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/agent-shifts/export.csv":{"get":{"tags":["dashboard-agent-shifts"],"summary":"Export Shifts Csv","description":"One row per shift in the window. Billing- and spreadsheet-friendly.","operationId":"export_shifts_csv_api_v1_dashboard_projects__project_id__content_agent_shifts_export_csv_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"}},{"name":"actor_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(api_client|dashboard_user)$"},{"type":"null"}],"title":"Role"}},{"name":"gap_minutes","in":"query","required":false,"schema":{"type":"integer","maximum":120,"minimum":1,"default":10,"title":"Gap Minutes"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/agent-shifts/share":{"post":{"tags":["dashboard-agent-shifts"],"summary":"Create Share Link","description":"Issue a 30-day HMAC-signed share URL for the given date range.","operationId":"create_share_link_api_v1_dashboard_projects__project_id__content_agent_shifts_share_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Create Share Link Api V1 Dashboard Projects  Project Id  Content Agent Shifts Share Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/agent-shifts/digest-settings":{"get":{"tags":["dashboard-agent-shifts"],"summary":"Get Digest Settings","description":"Return the tenant's current digest cadence + last-sent timestamp.","operationId":"get_digest_settings_api_v1_dashboard_projects__project_id__content_agent_shifts_digest_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSettingsResponse"}}}}}},"patch":{"tags":["dashboard-agent-shifts"],"summary":"Update Digest Settings","description":"Change the tenant's digest cadence. Resets last_sent_at when cadence\nchanges so the next eligible tick fires immediately.","operationId":"update_digest_settings_api_v1_dashboard_projects__project_id__content_agent_shifts_digest_settings_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSettingsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/agent-shifts/anomalies":{"get":{"tags":["dashboard-agent-shifts"],"summary":"List Anomalies","description":"Per-client anomaly findings (last 7 days vs 30-day baseline).","operationId":"list_anomalies_api_v1_dashboard_projects__project_id__content_agent_shifts_anomalies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Anomalies Api V1 Dashboard Projects  Project Id  Content Agent Shifts Anomalies Get"}}}}}}},"/api/v1/admin/agent-shifts":{"get":{"tags":["admin-agent-shifts"],"summary":"List Shifts Cross Tenant","description":"Cross-tenant shift list. Groups shifts by client when no client_id filter.","operationId":"list_shifts_cross_tenant_api_v1_admin_agent_shifts_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"}},{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional — narrow to one tenant. Must be cli_xxx or UUID.","title":"Client Id"},"description":"Optional — narrow to one tenant. Must be cli_xxx or UUID."},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(api_client|dashboard_user)$"},{"type":"null"}],"title":"Role"}},{"name":"gap_minutes","in":"query","required":false,"schema":{"type":"integer","maximum":120,"minimum":1,"default":10,"title":"Gap Minutes"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Shifts Cross Tenant Api V1 Admin Agent Shifts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/agent-shifts/anomalies":{"get":{"tags":["admin-agent-shifts"],"summary":"Cross Tenant Anomalies","description":"Platform-wide anomaly sweep (last 7 days vs 30-day baseline per tenant).","operationId":"cross_tenant_anomalies_api_v1_admin_agent_shifts_anomalies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Cross Tenant Anomalies Api V1 Admin Agent Shifts Anomalies Get"}}}}}}},"/api/v1/public/work-diary/{token}":{"get":{"tags":["public-work-diary"],"summary":"Public Work Diary","description":"Public view of shifts for the signed date range in the token.\n\nNo auth header required — the HMAC signature IS the auth. 4xx error\nenvelopes intentionally keep wording generic to avoid oracle attacks.","operationId":"public_work_diary_api_v1_public_work_diary__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","minLength":16,"maxLength":2048,"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Public Work Diary Api V1 Public Work Diary  Token  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/request":{"post":{"tags":["PAT Authentication"],"summary":"Request API access","description":"Agent requests API access. Sends approval email to admin. Returns poll_token for status checking.","operationId":"request_access_api_v1_auth_pat_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATRequestCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATRequestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/request-by-email":{"post":{"tags":["PAT Authentication"],"summary":"Request access by email only","description":"Simplified flow - provide admin email only, discovers client automatically.","operationId":"request_access_by_email_api_v1_auth_pat_request_by_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATRequestByEmail"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATRequestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/request/{request_id}/status":{"get":{"tags":["PAT Authentication"],"summary":"Poll for approval status","description":"CLI polls this endpoint every 3 seconds while waiting for admin approval.","operationId":"poll_status_api_v1_auth_pat_request__request_id__status_get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"poll_token","in":"query","required":true,"schema":{"type":"string","description":"Poll token from initial request","title":"Poll Token"},"description":"Poll token from initial request"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PATStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/request/{request_id}/approve":{"get":{"tags":["PAT Authentication"],"summary":"Approve access request","description":"Admin clicks this link in the approval email to grant access.","operationId":"approve_request_api_v1_auth_pat_request__request_id__approve_get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"approval_token","in":"query","required":true,"schema":{"type":"string","description":"Approval token from email","title":"Approval Token"},"description":"Approval token from email"}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/request/{request_id}/deny":{"get":{"tags":["PAT Authentication"],"summary":"Deny access request","description":"Admin clicks this link in the approval email to deny access.","operationId":"deny_request_api_v1_auth_pat_request__request_id__deny_get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"approval_token","in":"query","required":true,"schema":{"type":"string","description":"Approval token from email","title":"Approval Token"},"description":"Approval token from email"}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/{token_id}":{"delete":{"tags":["PAT Authentication"],"summary":"Revoke an agent token","description":"Revoke a specific agent token. Requires authentication.","operationId":"revoke_token_api_v1_auth_pat__token_id__delete","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","title":"Token Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRevokeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/agents":{"get":{"tags":["PAT Authentication"],"summary":"List agent tokens","description":"List all agent tokens for the authenticated client. Admin can see all.","operationId":"list_agent_tokens_api_v1_auth_pat_agents_get","parameters":[{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by client (admin only)","title":"Client Id"},"description":"Filter by client (admin only)"},{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Include revoked/expired tokens","default":false,"title":"Include Inactive"},"description":"Include revoked/expired tokens"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTokenListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/pat/status":{"get":{"tags":["PAT Authentication"],"summary":"Check authentication status","description":"Check if the current token is valid and return its info.","operationId":"check_auth_status_api_v1_auth_pat_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthStatusResponse"}}}}}}},"/api/v1/auth/broker/login":{"post":{"tags":["Auth Broker"],"summary":"Broker Login","description":"Validate creds against the sidecar → return an origin-bound one-time code.","operationId":"broker_login_api_v1_auth_broker_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerLoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/broker/signup":{"post":{"tags":["Auth Broker"],"summary":"Broker Signup","description":"Create a new dashboard account + free workspace; send a verification email.\n\nNo session is minted — the account is unverified until the emailed link is\nclicked, then the user signs in via /login. The response is generic and\nNON-enumerating: a brand-new account and a duplicate email both return the\nsame 200 (the brick shows \"check your email\" either way).","operationId":"broker_signup_api_v1_auth_broker_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerSignupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerSignupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/broker/forgot-password":{"post":{"tags":["Auth Broker"],"summary":"Broker Forgot Password","operationId":"broker_forgot_password_api_v1_auth_broker_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerOkResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/broker/reset-password":{"post":{"tags":["Auth Broker"],"summary":"Broker Reset Password","operationId":"broker_reset_password_api_v1_auth_broker_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerOkResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/broker/oauth/{provider}/start":{"get":{"tags":["Auth Broker"],"summary":"Broker Oauth Start","operationId":"broker_oauth_start_api_v1_auth_broker_oauth__provider__start_get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":32,"title":"Provider"}},{"name":"origin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Origin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/broker/oauth/{provider}/callback":{"get":{"tags":["Auth Broker"],"summary":"Broker Oauth Callback","operationId":"broker_oauth_callback_api_v1_auth_broker_oauth__provider__callback_get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":32,"title":"Provider"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":512,"title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/broker/exchange":{"post":{"tags":["Auth Broker"],"summary":"Broker Exchange","description":"Consume a one-time code (single-use, origin-bound) → verified session.","operationId":"broker_exchange_api_v1_auth_broker_exchange_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerExchangeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerExchangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/skills":{"get":{"tags":["Skills"],"summary":"List Skills","description":"List all available SpiderIQ skills with metadata.\n\nNo authentication required - this is a discovery endpoint.\n\nReturns:\n    Skill catalog with type, description, use cases, and API info for each skill.","operationId":"list_skills_api_v1_skills_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: json)","title":"Format"},"description":"Response format: yaml or md (default: json)"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category: scraping, enrichment, social, automation, media, communication","title":"Category"},"description":"Filter by category: scraping, enrichment, social, automation, media, communication"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkillCatalogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/skills/categories":{"get":{"tags":["Skills"],"summary":"List Categories","description":"List skill categories with counts.\n\nReturns:\n    List of categories and how many skills each contains.","operationId":"list_categories_api_v1_skills_categories_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkillCategoryResponse"}}}}}}},"/api/v1/skills/{skill_type}":{"get":{"tags":["Skills"],"summary":"Get Skill Details","description":"Get detailed metadata for a specific skill.\n\nNo authentication required.\n\nArgs:\n    skill_type: The skill type identifier (e.g., 'spiderSite', 'spiderMaps')\n\nReturns:\n    Full skill metadata including input schema, examples, and limitations.","operationId":"get_skill_details_api_v1_skills__skill_type__get","parameters":[{"name":"skill_type","in":"path","required":true,"schema":{"type":"string","title":"Skill Type"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Response format: yaml or md (default: json)","title":"Format"},"description":"Response format: yaml or md (default: json)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkillMetadata"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chat/completions":{"post":{"tags":["LLM Proxy"],"summary":"Chat Completions","description":"OpenAI-compatible chat completions endpoint.\n\nThis endpoint acts as a proxy to various LLM providers, with:\n- Automatic API key selection via SpiderGate\n- Full request/response tracing via Langfuse\n- Token and cost tracking per brand\n\nThe model can include a provider prefix (e.g., \"openrouter/gpt-4\")\nor the provider will be auto-detected from the model name.","operationId":"chat_completions_api_v1_chat_completions_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-Job-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Job-Id"}},{"name":"X-Job-Type","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Job-Type"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__llm__ChatCompletionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__llm__ChatCompletionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models":{"get":{"tags":["LLM Proxy"],"summary":"List Models","description":"List available models.\n\nReturns SpiderIQ model aliases and available providers.","operationId":"list_models_api_v1_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/gate/v1/chat/completions":{"post":{"tags":["SpiderGate","Chat Completions"],"summary":"Create Chat Completion","description":"Create a chat completion with automatic provider routing and fallback.\n\nThis endpoint is fully compatible with the OpenAI Chat Completions API.\nSupports streaming via `stream: true` parameter.\n\n**SpiderGate Extensions:**\n- `spidergate_options.fallback_models`: List of fallback models on failure\n- `spidergate_options.timeout_ms`: Request timeout in milliseconds\n- `spidergate_options.cache_enabled`: Enable response caching\n- `spidergate_options.max_cost_usd`: Maximum cost for this request","operationId":"create_chat_completion_api_gate_v1_chat_completions_post","parameters":[{"name":"include_route_trace","in":"query","required":false,"schema":{"type":"boolean","description":"When true, include a `route_trace` array in the response body detailing each dispatch attempt (codex bypass, Router) with outcome, model, latency, and error details. Non-streaming only. (OPVS #4)","default":false,"title":"Include Route Trace"},"description":"When true, include a `route_trace` array in the response body detailing each dispatch attempt (codex bypass, Router) with outcome, model, latency, and error details. Non-streaming only. (OPVS #4)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ChatCompletionRequest"}}}},"responses":{"200":{"description":"Successful completion","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ChatCompletionResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"401":{"description":"Authentication error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/images/generations":{"post":{"tags":["SpiderGate","Images"],"summary":"Generate Images","description":"Create one or more images from a text prompt. OpenAI-compatible — drop-in for the ``openai`` Python SDK by setting ``base_url='https://spideriq.ai/api/gate/v1'``. Uses an OpenAI ``sk-...`` key from the SpiderGate pool (brand-scoped first, then shared pool fallback). Per-call billed against that OpenAI account.","operationId":"create_image_generation_api_gate_v1_images_generations_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationRequest"}}},"required":true},"responses":{"200":{"description":"Successful image generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"401":{"description":"Authentication error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"503":{"description":"No OpenAI key available in the pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/audio/speech":{"post":{"tags":["SpiderGate","Audio"],"summary":"Generate Speech (TTS)","description":"Synthesise speech from text using an OpenAI TTS model. Streams the audio bytes back in the requested format (mp3 default). OpenAI-compatible — drop-in for the ``openai`` SDK's ``audio.speech.create`` with ``base_url='https://spideriq.ai/api/gate/v1'``.","operationId":"create_speech_api_gate_v1_audio_speech_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechRequest"}}},"required":true},"responses":{"200":{"description":"Audio bytes in the requested format","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"401":{"description":"Authentication error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"503":{"description":"No OpenAI key available in the pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/audio/transcriptions":{"post":{"tags":["SpiderGate","Audio"],"summary":"Transcribe Audio (STT)","description":"Transcribe audio to text using OpenAI's Whisper (or gpt-4o-transcribe / gpt-4o-mini-transcribe). Accepts ``multipart/form-data`` with a ``file`` field — same shape as ``openai.Audio.transcriptions.create()``. Drop-in for the ``openai`` SDK with ``base_url='https://spideriq.ai/api/gate/v1'``.","operationId":"create_transcription_api_gate_v1_audio_transcriptions_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_create_transcription_api_gate_v1_audio_transcriptions_post"}}},"required":true},"responses":{"200":{"description":"Successful transcription","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"401":{"description":"Authentication error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"503":{"description":"No OpenAI key available in the pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/embeddings":{"post":{"tags":["SpiderGate","Embeddings"],"summary":"Create Embeddings","description":"Generate vector embeddings for one or more texts using an OpenAI embedding model. OpenAI-compatible — drop-in for the ``openai`` SDK's ``embeddings.create()`` with ``base_url='https://spideriq.ai/api/gate/v1'``. Per-call billed against the OpenAI pool key resolved at request time.","operationId":"create_embedding_api_gate_v1_embeddings_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingRequest"}}},"required":true},"responses":{"200":{"description":"Successful embedding","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"401":{"description":"Authentication error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"503":{"description":"No OpenAI key available in the pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/models":{"get":{"tags":["SpiderGate","Models"],"summary":"List Models","description":"List all available models across all providers, sorted by usage.\n\nModels are dynamically fetched from provider APIs and cached.\nUsage data (tokens, requests) comes from the last 30 days.\n\n**Filters:**\n- `provider`: Filter by provider name\n- `free_only`: Only free tier models\n- `configured_only`: Only models configured in SpiderGate routing\n- `search`: Search by model name or ID\n- `sort_by`: Sort order (usage, name, provider, context, cost)\n- `capability`: Filter by capability (chat, vision, function_calling)\n\n**SpiderGate Extensions:**\nEach model includes `spidergate_info` with provider details, pricing,\ncapabilities, configuration status, task aliases, and 30-day usage stats.","operationId":"list_models_api_gate_v1_models_get","parameters":[{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by provider","title":"Provider"},"description":"Filter by provider"},{"name":"free_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only show free tier models","default":false,"title":"Free Only"},"description":"Only show free tier models"},{"name":"configured_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only show configured models","default":false,"title":"Configured Only"},"description":"Only show configured models"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search model name or ID","title":"Search"},"description":"Search model name or ID"},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","description":"Sort: usage, name, provider, context, cost","default":"usage","title":"Sort By"},"description":"Sort: usage, name, provider, context, cost"},{"name":"capability","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by capability","title":"Capability"},"description":"Filter by capability"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":2000,"minimum":1,"description":"Max results","default":500,"title":"Limit"},"description":"Max results"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Offset for pagination","default":0,"title":"Offset"},"description":"Offset for pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/models/{model_id}":{"get":{"tags":["SpiderGate","Models"],"summary":"Get Model","description":"Get details for a specific model.","operationId":"get_model_api_gate_v1_models__model_id__get","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","title":"Model Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelInfo"}}}},"404":{"description":"Model not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__schemas__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/gate/v1/aliases":{"get":{"tags":["SpiderGate","Models"],"summary":"List Task Aliases","description":"List all SpiderGate task aliases with their model chains and usage.\n\nTask aliases like `spideriq/coding` or `spideriq/extraction` route to\nan ordered fallback chain of models. This endpoint shows which models\neach alias uses and 30-day usage statistics.","operationId":"list_aliases_api_gate_v1_aliases_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/gate/v1/providers":{"get":{"tags":["SpiderGate","Models","Providers"],"summary":"List Providers","description":"List all available providers, model counts, and key status.","operationId":"list_providers_api_gate_v1_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/gate/v1/health":{"get":{"tags":["SpiderGate","Health"],"summary":"Health Check","description":"Check if the SpiderGate API is operational.","operationId":"health_check_api_gate_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/gate/contribute/{token}":{"get":{"tags":["Public Key Contribution"],"summary":"Get Invite Details","description":"Get invite details for the public contribution page. No auth required.","operationId":"get_invite_details_api_v1_gate_contribute__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/contribute/{token}/api-key":{"post":{"tags":["Public Key Contribution"],"summary":"Submit Api Key","description":"Validate and store a contributed API key. No auth required.","operationId":"submit_api_key_api_v1_gate_contribute__token__api_key_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeySubmitRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeySubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/contribute/{token}/oauth/start":{"post":{"tags":["Public Key Contribution"],"summary":"Contribute Oauth Start","description":"Start OAuth flow for a contributed subscription credential.","operationId":"contribute_oauth_start_api_v1_gate_contribute__token__oauth_start_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthStartResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/contribute/{token}/oauth/callback":{"post":{"tags":["Public Key Contribution"],"summary":"Contribute Oauth Callback","description":"Complete the OAuth flow started by `/oauth/start`.\n\nThe invitee's browser lands back on `/contribute/{token}?code=...&state=...`\nafter the provider consent screen. The frontend POSTs {code, state} here.\nThis endpoint:\n  1. Validates state is the one we stored at start + binds to this invite\n  2. Exchanges the authorization code for tokens (with client_secret)\n  3. Persists encrypted tokens into api_integrations\n  4. For calendar providers: pushes the tokens into self-hosted cal.com's\n     Credential table so cal.com can query the invitee's calendar\n  5. Marks the invite completed","operationId":"contribute_oauth_callback_api_v1_gate_contribute__token__oauth_callback_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__contribute__OAuthCallbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__contribute__OAuthCallbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/contribute/{token}/device-code/start":{"post":{"tags":["Public Key Contribution"],"summary":"Contribute Device Code Start","description":"Start device code flow for a contributed subscription credential.\n\n**Currently disabled.** Codex's device-code flow uses a non-RFC-8628\ncustom protocol (/api/accounts/deviceauth/usercode + /token with JSON\nbodies) that we haven't wired yet. Use the PKCE + paste-back flow at\n/contribute/{token}/oauth/start instead — that's the path the public\ncontribute page uses for codex.\n\nSee docs/services/SpiderGate/CODEX_PROTOCOL.md §1.3.","operationId":"contribute_device_code_start_api_v1_gate_contribute__token__device_code_start_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceCodeStartResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/contribute/{token}/device-code/poll":{"post":{"tags":["Public Key Contribution"],"summary":"Contribute Device Code Poll","description":"Poll for device code authorization.\n\nSee `contribute_device_code_start` — currently disabled. Returns 501\nimmediately; the caller should switch to PKCE + paste-back. Kept as a\nstub so existing frontend code that polls doesn't 404.","operationId":"contribute_device_code_poll_api_v1_gate_contribute__token__device_code_poll_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"poll_id","in":"query","required":true,"schema":{"type":"string","title":"Poll Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceCodePollResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/reauth/{token}":{"get":{"tags":["Public Key Re-authentication"],"summary":"Get Reauth Details","description":"Return credential details for the public re-auth page. No login.","operationId":"get_reauth_details_api_v1_gate_reauth__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReauthDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/reauth/{token}/api-key":{"post":{"tags":["Public Key Re-authentication"],"summary":"Reauth Api Key","description":"Re-authenticate an api_key credential by pasting a fresh key. No login.","operationId":"reauth_api_key_api_v1_gate_reauth__token__api_key_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyReauthRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyReauthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/reauth/{token}/oauth/start":{"post":{"tags":["Public Key Re-authentication"],"summary":"Reauth Oauth Start","description":"Start the OAuth re-login flow for an existing OAuth credential. No login.","operationId":"reauth_oauth_start_api_v1_gate_reauth__token__oauth_start_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthStartResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/gate/reauth/{token}/oauth/callback":{"post":{"tags":["Public Key Re-authentication"],"summary":"Reauth Oauth Callback","description":"Complete the OAuth re-login and UPDATE the existing credential row.\n\nUnlike contribute_oauth_callback (which INSERTs a new row), this UPDATEs\nthe SAME api_integrations row — preserving id, share_with_pool, brand,\nrate limits — and re-activates it.","operationId":"reauth_oauth_callback_api_v1_gate_reauth__token__oauth_callback_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__reauth__OAuthCallbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/api__v1__gate__reauth__OAuthCallbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/bg-videos/upload":{"post":{"tags":["Marketplace - Bg Videos"],"summary":"Upload Bg Video Asset","description":"Upload a single bg-video asset (MP4 video OR poster GIF/PNG/WEBP) to R2 at\n`bg-videos/<slug>.<ext>`. Returns the public URL to persist on the catalog row.\n\nThe route does not write to the database — call POST `/` or PATCH `/{slug}`\nafterwards with the returned URL in `video_url` or `poster_url`.\n\nPhase E (2026-05-05) — Content-Type and magic-number are now validated\nalongside the extension. Posters are capped at 5 MB; videos at 500 MB.","operationId":"upload_bg_video_asset_api_v1_dashboard_content_bg_videos_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_bg_video_asset_api_v1_dashboard_content_bg_videos_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BgVideoUploadResponse"}}}},"413":{"description":"Upload exceeds size limit (5MB poster / 500MB video).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"415":{"description":"Unsupported extension, Content-Type/extension mismatch, or magic-number mismatch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/bg-videos":{"get":{"tags":["Marketplace - Bg Videos"],"summary":"List Bg Videos Admin","description":"List bg-video catalog rows (admin view — no public-only filters).\nUse `?format=yaml|md` for agent-friendly responses (~40-60% smaller).","operationId":"list_bg_videos_admin_api_v1_dashboard_content_bg_videos_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Marketplace - Bg Videos"],"summary":"Create Bg Video","description":"Create a new bg-video catalog row. Phase 11+12 gated.","operationId":"create_bg_video_api_v1_dashboard_content_bg_videos_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BgVideoCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/bg-videos/{slug}":{"get":{"tags":["Marketplace - Bg Videos"],"summary":"Get Bg Video Admin","description":"Get a single bg-video catalog row by slug.\nUse `?format=yaml|md` for agent-friendly responses.","operationId":"get_bg_video_admin_api_v1_dashboard_content_bg_videos__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Marketplace - Bg Videos"],"summary":"Update Bg Video","description":"Update an existing bg-video catalog row. Phase 11+12 gated.\n\nPhase E (2026-05-05) — accepts the universal-axis fields (mood, palette,\nbrand_fit_tags, scene_type) + agent_meta (BgVideoAgentMeta-validated)\n+ replication_prompt. Strict enum validation rejects unknown values at\nthe boundary; vocabulary growth requires a schema PR, not a migration.","operationId":"update_bg_video_api_v1_dashboard_content_bg_videos__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BgVideoUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"403":{"description":"confirm_token does not match this client/action/resource (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Bg-video not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"409":{"description":"confirm_token already consumed (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"410":{"description":"confirm_token expired (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Body failed validation: unknown mood/scene_type/brand_fit_tag enum value, agent_meta has unknown key (extra='forbid'), agent_meta Literal violation (e.g. pace='turbo'), or palette > 12 entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}},"delete":{"tags":["Marketplace - Bg Videos"],"summary":"Delete Bg Video","description":"Delete a bg-video catalog row. R2 objects survive — clean up via R2 console.\nPhase 11+12 gated.","operationId":"delete_bg_video_api_v1_dashboard_content_bg_videos__slug__delete","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/bg-videos/upload":{"post":{"tags":["Marketplace - Bg Videos"],"summary":"Upload Bg Video Asset","description":"Upload a single bg-video asset (MP4 video OR poster GIF/PNG/WEBP) to R2 at\n`bg-videos/<slug>.<ext>`. Returns the public URL to persist on the catalog row.\n\nThe route does not write to the database — call POST `/` or PATCH `/{slug}`\nafterwards with the returned URL in `video_url` or `poster_url`.\n\nPhase E (2026-05-05) — Content-Type and magic-number are now validated\nalongside the extension. Posters are capped at 5 MB; videos at 500 MB.","operationId":"upload_bg_video_asset_api_v1_dashboard_projects__project_id__content_bg_videos_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_bg_video_asset_api_v1_dashboard_projects__project_id__content_bg_videos_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BgVideoUploadResponse"}}}},"413":{"description":"Upload exceeds size limit (5MB poster / 500MB video).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"415":{"description":"Unsupported extension, Content-Type/extension mismatch, or magic-number mismatch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/bg-videos":{"get":{"tags":["Marketplace - Bg Videos"],"summary":"List Bg Videos Admin","description":"List bg-video catalog rows (admin view — no public-only filters).\nUse `?format=yaml|md` for agent-friendly responses (~40-60% smaller).","operationId":"list_bg_videos_admin_api_v1_dashboard_projects__project_id__content_bg_videos_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Marketplace - Bg Videos"],"summary":"Create Bg Video","description":"Create a new bg-video catalog row. Phase 11+12 gated.","operationId":"create_bg_video_api_v1_dashboard_projects__project_id__content_bg_videos_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","description":"Phase 11+12: preview the create and receive a confirm_token without mutating.","default":false,"title":"Dry Run"},"description":"Phase 11+12: preview the create and receive a confirm_token without mutating."},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Phase 11+12: consume a prior preview token and perform the create.","title":"Confirm Token"},"description":"Phase 11+12: consume a prior preview token and perform the create."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BgVideoCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/bg-videos/{slug}":{"get":{"tags":["Marketplace - Bg Videos"],"summary":"Get Bg Video Admin","description":"Get a single bg-video catalog row by slug.\nUse `?format=yaml|md` for agent-friendly responses.","operationId":"get_bg_video_admin_api_v1_dashboard_projects__project_id__content_bg_videos__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Marketplace - Bg Videos"],"summary":"Update Bg Video","description":"Update an existing bg-video catalog row. Phase 11+12 gated.\n\nPhase E (2026-05-05) — accepts the universal-axis fields (mood, palette,\nbrand_fit_tags, scene_type) + agent_meta (BgVideoAgentMeta-validated)\n+ replication_prompt. Strict enum validation rejects unknown values at\nthe boundary; vocabulary growth requires a schema PR, not a migration.","operationId":"update_bg_video_api_v1_dashboard_projects__project_id__content_bg_videos__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BgVideoUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"403":{"description":"confirm_token does not match this client/action/resource (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Bg-video not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"409":{"description":"confirm_token already consumed (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"410":{"description":"confirm_token expired (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Body failed validation: unknown mood/scene_type/brand_fit_tag enum value, agent_meta has unknown key (extra='forbid'), agent_meta Literal violation (e.g. pace='turbo'), or palette > 12 entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}},"delete":{"tags":["Marketplace - Bg Videos"],"summary":"Delete Bg Video","description":"Delete a bg-video catalog row. R2 objects survive — clean up via R2 console.\nPhase 11+12 gated.","operationId":"delete_bg_video_api_v1_dashboard_projects__project_id__content_bg_videos__slug__delete","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/site-templates/{slug}/upload-preview":{"post":{"tags":["Marketplace - Site Templates"],"summary":"Upload Site Template Preview","description":"Upload a preview image (PNG/GIF/JPG/WebP) for a site_template to R2 at\n`templates/<slug>.<ext>`. Returns the public URL.\n\nCaller is responsible for persisting the URL to `preview_thumbnail_url`\nvia PATCH `/{slug}` afterwards.\n\nPhase E (2026-05-05) — Content-Type and magic-number are now validated\nalongside the extension. Capped at 5 MB.","operationId":"upload_site_template_preview_api_v1_dashboard_content_site_templates__slug__upload_preview_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_site_template_preview_api_v1_dashboard_content_site_templates__slug__upload_preview_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePreviewUploadResponse"}}}},"413":{"description":"Upload exceeds 5 MB size limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"415":{"description":"Unsupported extension, Content-Type/extension mismatch, or magic-number mismatch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/site-templates":{"get":{"tags":["Marketplace - Site Templates"],"summary":"List Site Templates Admin","description":"List site_template catalog rows.\nUse `?format=yaml|md` for agent-friendly responses (~40-60% smaller).","operationId":"list_site_templates_admin_api_v1_dashboard_content_site_templates_get","parameters":[{"name":"industry","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"}},{"name":"use_case","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"include_unpublished","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Unpublished"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Marketplace - Site Templates"],"summary":"Create Site Template","description":"Create a new site_template catalog row. Phase 11+12 gated.","operationId":"create_site_template_api_v1_dashboard_content_site_templates_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteTemplateCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/site-templates/{slug}":{"get":{"tags":["Marketplace - Site Templates"],"summary":"Get Site Template Admin","description":"Get a single site_template (admin — includes drafts).\nUse `?format=yaml|md` for agent-friendly responses.","operationId":"get_site_template_admin_api_v1_dashboard_content_site_templates__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Marketplace - Site Templates"],"summary":"Update Site Template","description":"Update an existing site_template. Phase 11+12 gated.\n\nPhase E (2026-05-05) — accepts the universal-axis fields (mood, palette,\nbrand_fit_tags, scene_type) + agent_meta (SiteTemplateAgentMeta-validated).","operationId":"update_site_template_api_v1_dashboard_content_site_templates__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteTemplateUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"403":{"description":"confirm_token does not match this client/action/resource (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Site template not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"409":{"description":"confirm_token already consumed (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"410":{"description":"confirm_token expired (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Body failed validation: unknown mood/scene_type/brand_fit_tag, agent_meta with unknown key (extra='forbid'), or invalid Literal (e.g. style_aesthetic='kawaii', page_count out of [1,200]).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}},"delete":{"tags":["Marketplace - Site Templates"],"summary":"Delete Site Template","description":"Delete a site_template. R2 preview images are NOT auto-removed.\nPhase 11+12 gated.","operationId":"delete_site_template_api_v1_dashboard_content_site_templates__slug__delete","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/site-templates/{slug}/upload-preview":{"post":{"tags":["Marketplace - Site Templates"],"summary":"Upload Site Template Preview","description":"Upload a preview image (PNG/GIF/JPG/WebP) for a site_template to R2 at\n`templates/<slug>.<ext>`. Returns the public URL.\n\nCaller is responsible for persisting the URL to `preview_thumbnail_url`\nvia PATCH `/{slug}` afterwards.\n\nPhase E (2026-05-05) — Content-Type and magic-number are now validated\nalongside the extension. Capped at 5 MB.","operationId":"upload_site_template_preview_api_v1_dashboard_projects__project_id__content_site_templates__slug__upload_preview_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_site_template_preview_api_v1_dashboard_projects__project_id__content_site_templates__slug__upload_preview_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatePreviewUploadResponse"}}}},"413":{"description":"Upload exceeds 5 MB size limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"415":{"description":"Unsupported extension, Content-Type/extension mismatch, or magic-number mismatch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/site-templates":{"get":{"tags":["Marketplace - Site Templates"],"summary":"List Site Templates Admin","description":"List site_template catalog rows.\nUse `?format=yaml|md` for agent-friendly responses (~40-60% smaller).","operationId":"list_site_templates_admin_api_v1_dashboard_projects__project_id__content_site_templates_get","parameters":[{"name":"industry","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"}},{"name":"use_case","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"}},{"name":"is_featured","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"}},{"name":"include_unpublished","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Unpublished"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Marketplace - Site Templates"],"summary":"Create Site Template","description":"Create a new site_template catalog row. Phase 11+12 gated.","operationId":"create_site_template_api_v1_dashboard_projects__project_id__content_site_templates_post","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteTemplateCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/site-templates/{slug}":{"get":{"tags":["Marketplace - Site Templates"],"summary":"Get Site Template Admin","description":"Get a single site_template (admin — includes drafts).\nUse `?format=yaml|md` for agent-friendly responses.","operationId":"get_site_template_admin_api_v1_dashboard_projects__project_id__content_site_templates__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yaml|md)$"},{"type":"null"}],"description":"Token-efficient format for AI agents.","title":"Format"},"description":"Token-efficient format for AI agents."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Marketplace - Site Templates"],"summary":"Update Site Template","description":"Update an existing site_template. Phase 11+12 gated.\n\nPhase E (2026-05-05) — accepts the universal-axis fields (mood, palette,\nbrand_fit_tags, scene_type) + agent_meta (SiteTemplateAgentMeta-validated).","operationId":"update_site_template_api_v1_dashboard_projects__project_id__content_site_templates__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteTemplateUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"403":{"description":"confirm_token does not match this client/action/resource (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Site template not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"409":{"description":"confirm_token already consumed (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"410":{"description":"confirm_token expired (Phase 11+12).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Body failed validation: unknown mood/scene_type/brand_fit_tag, agent_meta with unknown key (extra='forbid'), or invalid Literal (e.g. style_aesthetic='kawaii', page_count out of [1,200]).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}}}},"delete":{"tags":["Marketplace - Site Templates"],"summary":"Delete Site Template","description":"Delete a site_template. R2 preview images are NOT auto-removed.\nPhase 11+12 gated.","operationId":"delete_site_template_api_v1_dashboard_projects__project_id__content_site_templates__slug__delete","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"confirm_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/idap/merge-tags":{"get":{"tags":["dashboard-idap"],"summary":"Dashboard Merge Tags","description":"Cookie-authenticated merge-tag reference for the dashboard page editor.\n\nReturns the same vocabulary as `GET /api/v1/content/variables` (the\ncanonical Bearer-auth endpoint), plus an `editor_context` block tuned\nfor the Phase MA-2 PR-D Merge Tags panel.\n\nMulti-tenant: scoped via the dashboard auth dependency. Super_admin\nimpersonation works through `X-Selected-Client-Id` / `X-Brand-ID`\nheaders — same as every other `/dashboard/*` route.","operationId":"dashboard_merge_tags_api_v1_dashboard_idap_merge_tags_get","parameters":[{"name":"page_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional page UUID. If provided, the response includes an `editor_context.applies_to_this_page` flag — true when the page's template renders merge tags, false otherwise. The tag vocabulary is identical either way; the flag drives the editor's 'tags will/won't render here' hint.","title":"Page Id"},"description":"Optional page UUID. If provided, the response includes an `editor_context.applies_to_this_page` flag — true when the page's template renders merge tags, false otherwise. The tag vocabulary is identical either way; the flag drives the editor's 'tags will/won't render here' hint."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/idap/{resource_type}/resolve":{"get":{"tags":["dashboard-idap"],"summary":"Resolve an IDAP resource by external id (cookie auth)","description":"Cookie-authenticated mirror of `GET /api/v1/idap/{resource_type}/resolve`. Look up a single norm_cli_* record by an external identifier (Google `place_id`, `domain`, `email`, contact `linkedin`, registry `vat`/`registration_number`/`lei`/`tax_id`, …) within the dashboard's selected client scope. Powers the CRM detail panels. Exactly one identifier query param must be supplied; `include=` expands related resources (e.g. `emails,phones,contacts,domains,company_registry`).","operationId":"dashboard_idap_resolve_api_v1_dashboard_idap__resource_type__resolve_get","parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"place_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Place Id"}},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Domain"}},{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Email"}},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Url"}},{"name":"linkedin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin"}},{"name":"twitter","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Twitter"}},{"name":"vat","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Vat"}},{"name":"registration_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Registration Number"}},{"name":"lei","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Lei"}},{"name":"tax_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Tax Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include.","title":"Include"},"description":"Comma-separated related types to include."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapResource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/idap/{resource_type}/{resource_id}":{"get":{"tags":["dashboard-idap"],"summary":"Fetch an IDAP resource by id (cookie auth)","description":"Cookie-authenticated mirror of `GET /api/v1/idap/{resource_type}/{resource_id}`. Returns the canonical norm_cli_* record for a single resource within the dashboard's selected client scope, with optional `include=` expansion. Used by the CRM contact panel and as the company-panel fallback when a card's `external_ref` is a UUID. An invalid UUID is treated as 404, not 500.","operationId":"dashboard_idap_get_api_v1_dashboard_idap__resource_type___resource_id__get","parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"title":"Resource Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include.","title":"Include"},"description":"Comma-separated related types to include."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapResource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/idap/merge-tags":{"get":{"tags":["dashboard-idap"],"summary":"Dashboard Merge Tags","description":"Cookie-authenticated merge-tag reference for the dashboard page editor.\n\nReturns the same vocabulary as `GET /api/v1/content/variables` (the\ncanonical Bearer-auth endpoint), plus an `editor_context` block tuned\nfor the Phase MA-2 PR-D Merge Tags panel.\n\nMulti-tenant: scoped via the dashboard auth dependency. Super_admin\nimpersonation works through `X-Selected-Client-Id` / `X-Brand-ID`\nheaders — same as every other `/dashboard/*` route.","operationId":"dashboard_merge_tags_api_v1_dashboard_projects__project_id__idap_merge_tags_get","parameters":[{"name":"page_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional page UUID. If provided, the response includes an `editor_context.applies_to_this_page` flag — true when the page's template renders merge tags, false otherwise. The tag vocabulary is identical either way; the flag drives the editor's 'tags will/won't render here' hint.","title":"Page Id"},"description":"Optional page UUID. If provided, the response includes an `editor_context.applies_to_this_page` flag — true when the page's template renders merge tags, false otherwise. The tag vocabulary is identical either way; the flag drives the editor's 'tags will/won't render here' hint."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/idap/{resource_type}/resolve":{"get":{"tags":["dashboard-idap"],"summary":"Resolve an IDAP resource by external id (cookie auth)","description":"Cookie-authenticated mirror of `GET /api/v1/idap/{resource_type}/resolve`. Look up a single norm_cli_* record by an external identifier (Google `place_id`, `domain`, `email`, contact `linkedin`, registry `vat`/`registration_number`/`lei`/`tax_id`, …) within the dashboard's selected client scope. Powers the CRM detail panels. Exactly one identifier query param must be supplied; `include=` expands related resources (e.g. `emails,phones,contacts,domains,company_registry`).","operationId":"dashboard_idap_resolve_api_v1_dashboard_projects__project_id__idap__resource_type__resolve_get","parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"place_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Place Id"}},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Domain"}},{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Email"}},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Url"}},{"name":"linkedin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin"}},{"name":"twitter","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Twitter"}},{"name":"vat","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Vat"}},{"name":"registration_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Registration Number"}},{"name":"lei","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Lei"}},{"name":"tax_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Tax Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include.","title":"Include"},"description":"Comma-separated related types to include."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapResource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/idap/{resource_type}/{resource_id}":{"get":{"tags":["dashboard-idap"],"summary":"Fetch an IDAP resource by id (cookie auth)","description":"Cookie-authenticated mirror of `GET /api/v1/idap/{resource_type}/{resource_id}`. Returns the canonical norm_cli_* record for a single resource within the dashboard's selected client scope, with optional `include=` expansion. Used by the CRM contact panel and as the company-panel fallback when a card's `external_ref` is a UUID. An invalid UUID is treated as 404, not 500.","operationId":"dashboard_idap_get_api_v1_dashboard_projects__project_id__idap__resource_type___resource_id__get","parameters":[{"name":"resource_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ResourceType"}},{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"title":"Resource Id"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Comma-separated related types to include.","title":"Include"},"description":"Comma-separated related types to include."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdapResource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/marketplace/coverage":{"get":{"tags":["Marketplace - Coverage Audit"],"summary":"Marketplace V2 coverage audit","description":"Returns per-table fill state across the 4 universal axes (mood / palette / brand_fit_tags / scene_type) + agent_meta JSONB. Used by Fill-the-Bank slice 4 bulk runs and slice 7 drift cron. Super-admin only — coverage data exposes which global marketplace rows are missing metadata, which is operational state, not public catalog data.","operationId":"get_marketplace_coverage_api_v1_dashboard_content_marketplace_coverage_get","parameters":[{"name":"low_coverage_limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":0,"description":"Cap on the `low_coverage_assets` sample. 0 = omit the array entirely.","default":50,"title":"Low Coverage Limit"},"description":"Cap on the `low_coverage_assets` sample. 0 = omit the array entirely."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverageResponse"}}}},"403":{"description":"Caller is not super_admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/marketplace/coverage":{"get":{"tags":["Marketplace - Coverage Audit"],"summary":"Marketplace V2 coverage audit","description":"Returns per-table fill state across the 4 universal axes (mood / palette / brand_fit_tags / scene_type) + agent_meta JSONB. Used by Fill-the-Bank slice 4 bulk runs and slice 7 drift cron. Super-admin only — coverage data exposes which global marketplace rows are missing metadata, which is operational state, not public catalog data.","operationId":"get_marketplace_coverage_api_v1_dashboard_projects__project_id__content_marketplace_coverage_get","parameters":[{"name":"low_coverage_limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":0,"description":"Cap on the `low_coverage_assets` sample. 0 = omit the array entirely.","default":50,"title":"Low Coverage Limit"},"description":"Cap on the `low_coverage_assets` sample. 0 = omit the array entirely."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverageResponse"}}}},"403":{"description":"Caller is not super_admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"500":{"description":"Internal server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/content/marketplace/suggest/{asset_type}/{slug}":{"post":{"tags":["Marketplace - Inference Suggester"],"summary":"Run the marketplace V2 inference engine for one asset","description":"Suggests metadata (mood / palette / brand_fit_tags / scene_type / agent_meta) for the named marketplace asset using SpiderGate V2. Output is validated against the existing Pydantic enums BEFORE returning — off-vocabulary values are dropped and listed in `dropped_keys`. No DB write side effect; the caller applies via the existing PATCH endpoints with `agent_meta_source='llm_inferred'`.","operationId":"suggest_marketplace_metadata_api_v1_dashboard_content_marketplace_suggest__asset_type___slug__post","parameters":[{"name":"asset_type","in":"path","required":true,"schema":{"enum":["bg_video","component","site_template"],"type":"string","title":"Asset Type"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestEnvelope"}}}},"403":{"description":"Caller is not super_admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Asset not found in catalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"502":{"description":"SpiderGate engine error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"503":{"description":"DB or LLM engine unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/projects/{project_id}/content/marketplace/suggest/{asset_type}/{slug}":{"post":{"tags":["Marketplace - Inference Suggester"],"summary":"Run the marketplace V2 inference engine for one asset","description":"Suggests metadata (mood / palette / brand_fit_tags / scene_type / agent_meta) for the named marketplace asset using SpiderGate V2. Output is validated against the existing Pydantic enums BEFORE returning — off-vocabulary values are dropped and listed in `dropped_keys`. No DB write side effect; the caller applies via the existing PATCH endpoints with `agent_meta_source='llm_inferred'`.","operationId":"suggest_marketplace_metadata_api_v1_dashboard_projects__project_id__content_marketplace_suggest__asset_type___slug__post","parameters":[{"name":"asset_type","in":"path","required":true,"schema":{"enum":["bg_video","component","site_template"],"type":"string","title":"Asset Type"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestEnvelope"}}}},"403":{"description":"Caller is not super_admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"404":{"description":"Asset not found in catalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"502":{"description":"SpiderGate engine error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"503":{"description":"DB or LLM engine unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/schemas__marketplace__ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search/businesses":{"get":{"tags":["Search"],"summary":"Search businesses","description":"Full-text search across SpiderMaps business data.\n\n    Supports:\n    - Full-text query on name, description, category\n    - Geo-distance filtering (lat, lon, radius_km)\n    - Category and rating filters\n    - Faceted results for filter counts\n\n    **Use cases:** VayaPin restaurant search, Gun Guard retailer finder","operationId":"search_businesses_api_v1_search_businesses_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Search query","title":"Query"},"description":"Search query"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5.0,"minimum":0.0},{"type":"null"}],"description":"Minimum rating","title":"Min Rating"},"description":"Minimum rating"},{"name":"lat","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":90.0,"minimum":-90.0},{"type":"null"}],"description":"Latitude for geo search","title":"Lat"},"description":"Latitude for geo search"},{"name":"lon","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":180.0,"minimum":-180.0},{"type":"null"}],"description":"Longitude for geo search","title":"Lon"},"description":"Longitude for geo search"},{"name":"radius_km","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Search radius in km","default":50,"title":"Radius Km"},"description":"Search radius in km"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page","default":20,"title":"Per Page"},"description":"Results per page"},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","pattern":"^(_score|rating|reviews_count|newest)$","description":"Sort by field","default":"_score","title":"Sort By"},"description":"Sort by field"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search/leads":{"get":{"tags":["Search"],"summary":"Search leads","description":"Full-text search across SpiderSite lead data.\n\n    Supports:\n    - Full-text query on company name, description, compendium\n    - Email verification filter\n    - ICP score filtering\n    - Industry filter\n\n    **Use cases:** Di-Atomic Lead Stream search","operationId":"search_leads_api_v1_search_leads_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Search query","title":"Query"},"description":"Search query"},{"name":"email_verified","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter by email verification","title":"Email Verified"},"description":"Filter by email verification"},{"name":"min_icp_score","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":0},{"type":"null"}],"description":"Minimum ICP score","title":"Min Icp Score"},"description":"Minimum ICP score"},{"name":"industry","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by industry","title":"Industry"},"description":"Filter by industry"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page","default":20,"title":"Per Page"},"description":"Results per page"},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","description":"Sort by field","default":"_score","title":"Sort By"},"description":"Sort by field"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search/all":{"get":{"tags":["Search"],"summary":"Search all results","description":"Full-text search across ALL job results (SpiderMaps, SpiderSite, etc.).\n\n    Use this endpoint when you need to search across multiple job types.","operationId":"search_all_api_v1_search_all_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Search query","title":"Query"},"description":"Search query"},{"name":"job_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by job type","title":"Job Type"},"description":"Filter by job type"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5.0,"minimum":0.0},{"type":"null"}],"description":"Minimum rating","title":"Min Rating"},"description":"Minimum rating"},{"name":"lat","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":90.0,"minimum":-90.0},{"type":"null"}],"description":"Latitude for geo search","title":"Lat"},"description":"Latitude for geo search"},{"name":"lon","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":180.0,"minimum":-180.0},{"type":"null"}],"description":"Longitude for geo search","title":"Lon"},"description":"Longitude for geo search"},{"name":"radius_km","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Search radius in km","default":50,"title":"Radius Km"},"description":"Search radius in km"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page","default":20,"title":"Per Page"},"description":"Results per page"},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","description":"Sort by field","default":"_score","title":"Sort By"},"description":"Sort by field"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search":{"post":{"tags":["Search"],"summary":"Advanced search","description":"Advanced search with full control over query, filters, and options.\n\n    Use POST for complex queries with many parameters.","operationId":"advanced_search_api_v1_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/search/autocomplete":{"get":{"tags":["Search"],"summary":"Autocomplete suggestions","description":"Get autocomplete suggestions for business/lead names.\n\n    Returns up to 10 suggestions based on prefix matching.\n    Supports fuzzy matching for typo tolerance.","operationId":"autocomplete_api_v1_search_autocomplete_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"prefix","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":50,"description":"Search prefix","title":"Prefix"},"description":"Search prefix"},{"name":"size","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"description":"Number of suggestions","default":10,"title":"Size"},"description":"Number of suggestions"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutocompleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search/aggregations":{"get":{"tags":["Search"],"summary":"Get aggregations","description":"Get analytics aggregations for dashboard statistics.\n\n    Returns:\n    - Total document count\n    - Documents by job type\n    - Documents by category\n    - Documents by country\n    - Average rating\n    - Total reviews\n    - Verified emails count\n    - Indexing trend over time","operationId":"get_aggregations_api_v1_search_aggregations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by job type","title":"Job Type"},"description":"Filter by job type"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AggregationsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/inngest":{"get":{"summary":"Get Api Inngest","operationId":"get_api_inngest_api_inngest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"put":{"summary":"Put Inngest Api","operationId":"put_inngest_api_api_inngest_put","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Post Inngest Api","operationId":"post_inngest_api_api_inngest_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Root","description":"Root endpoint - redirect to docs","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AIComposeAction":{"type":"string","enum":["write","rewrite","expand","shorten","formal","casual","fix_grammar"],"title":"AIComposeAction","description":"Actions for AI email composition assistant."},"AIComposeRequest":{"properties":{"action":{"allOf":[{"$ref":"#/components/schemas/AIComposeAction"}],"description":"The action to perform on the content"},"context":{"type":"string","title":"Context","description":"Current email content to transform (empty for 'write' action)","default":""},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject","description":"Email subject for context"},"tone":{"allOf":[{"$ref":"#/components/schemas/AIComposeTone"}],"description":"Desired tone of the output","default":"professional"},"thread_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thread Context","description":"Previous messages in thread for context"}},"type":"object","required":["action"],"title":"AIComposeRequest","description":"Request for AI email composition assistance."},"AIComposeResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"content":{"type":"string","title":"Content","description":"Generated or transformed email content"},"tokens_used":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tokens Used","description":"Tokens consumed by the request"}},"type":"object","required":["content"],"title":"AIComposeResponse","description":"Response from AI email composition assistant."},"AIComposeTone":{"type":"string","enum":["professional","friendly","casual","formal"],"title":"AIComposeTone","description":"Tone options for AI email composition."},"ActiveLocationItem":{"properties":{"id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id"},"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"lat":{"type":"number","title":"Lat"},"lng":{"type":"number","title":"Lng"},"location_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location Name"},"dot_type":{"type":"string","title":"Dot Type","default":"location_queued"}},"type":"object","required":["campaign_id","status","lat","lng"],"title":"ActiveLocationItem","description":"A dot on the Live Theater Tactical Map.\n\nV1.0 (Stage C): only queued/in-progress location dots (gray).\nV1.1: also completed locations (dim gold) and per-business dots (bright gold).\nThe ``dot_type`` field distinguishes the three styles for the frontend."},"ActiveLocationsResponse":{"properties":{"locations":{"items":{"$ref":"#/components/schemas/ActiveLocationItem"},"type":"array","title":"Locations"}},"type":"object","required":["locations"],"title":"ActiveLocationsResponse","description":"List of dots across the user's active campaigns for the Tactical Map."},"ActivityItem":{"properties":{"id":{"type":"string","title":"Id"},"timestamp":{"type":"string","title":"Timestamp"},"actor":{"type":"string","title":"Actor"},"type":{"type":"string","title":"Type"},"action":{"type":"string","title":"Action"},"targetId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Targetid"},"targetLabel":{"type":"string","title":"Targetlabel"}},"type":"object","required":["id","timestamp","actor","type","action","targetLabel"],"title":"ActivityItem"},"ActivityResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ActivityItem"},"type":"array","title":"Data"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nextcursor"}},"type":"object","title":"ActivityResponse"},"AddUserToBrandRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"brand_id":{"type":"integer","title":"Brand Id"},"role":{"type":"string","title":"Role","default":"member"}},"type":"object","required":["user_id","brand_id"],"title":"AddUserToBrandRequest","description":"Request to add a user directly to a brand."},"AddressComponent":{"properties":{"long_name":{"type":"string","title":"Long Name"},"short_name":{"type":"string","title":"Short Name"},"types":{"items":{"type":"string"},"type":"array","title":"Types"}},"additionalProperties":false,"type":"object","required":["long_name","short_name","types"],"title":"AddressComponent","description":"One Google address-component row, normalised."},"AgentFingerprint":{"properties":{"agent_type":{"type":"string","title":"Agent Type","description":"Detected agent type","default":"unknown"},"hostname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hostname","description":"Machine hostname"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cwd","description":"Current working directory"},"platform":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform","description":"OS platform (darwin, linux, win32)"},"node_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Node Version","description":"Node.js version"},"project_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Name","description":"Project name from CLI"}},"type":"object","title":"AgentFingerprint","description":"Fingerprint identifying the requesting agent."},"AgentHeartbeat":{"properties":{"location_code":{"type":"string","title":"Location Code"},"agent_version":{"type":"string","title":"Agent Version"},"uptime_seconds":{"type":"integer","title":"Uptime Seconds"},"modems":{"items":{"$ref":"#/components/schemas/ModemHeartbeatData"},"type":"array","title":"Modems"},"system_stats":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"System Stats"}},"type":"object","required":["location_code","agent_version","uptime_seconds","modems"],"title":"AgentHeartbeat","description":"Agent heartbeat payload"},"AgentHeartbeatResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"server_time":{"type":"string","format":"date-time","title":"Server Time"},"pending_commands":{"items":{"$ref":"#/components/schemas/CommandResponse"},"type":"array","title":"Pending Commands"},"config_updates":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Config Updates"}},"type":"object","required":["success","server_time"],"title":"AgentHeartbeatResponse","description":"Response to agent heartbeat"},"AgentTokenInfo":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"token_prefix":{"type":"string","title":"Token Prefix","description":"First 12 chars of token"},"client_id":{"type":"string","title":"Client Id"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"fingerprint":{"type":"object","title":"Fingerprint"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"is_active":{"type":"boolean","title":"Is Active"},"usage_count":{"type":"integer","title":"Usage Count","default":0},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"photo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photo"},"monthly_budget_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Monthly Budget Usd"},"soft_budget_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Soft Budget Usd"},"current_spend_usd":{"type":"number","title":"Current Spend Usd","default":0},"budget_duration":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Duration"},"budget_reset_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Budget Reset At"},"rate_limit_rpm":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rate Limit Rpm"},"rate_limit_rpd":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rate Limit Rpd"},"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"model_spend":{"additionalProperties":{"type":"number"},"type":"object","title":"Model Spend"},"metadata":{"type":"object","title":"Metadata"}},"type":"object","required":["id","token_prefix","client_id","scopes","created_at","is_active"],"title":"AgentTokenInfo","description":"Information about an agent token."},"AgentTokenListResponse":{"properties":{"tokens":{"items":{"$ref":"#/components/schemas/AgentTokenInfo"},"type":"array","title":"Tokens"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["tokens","total"],"title":"AgentTokenListResponse","description":"Response for listing agent tokens (admin only)."},"AgentType":{"type":"string","enum":["human","ai"],"title":"AgentType"},"AgentUserListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"owner_email":{"type":"string","title":"Owner Email"},"opvs_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opvs Address"},"opvs_domain":{"type":"string","title":"Opvs Domain","default":"opvs.run"},"full_opvs_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Opvs Address"},"photo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photo"},"fingerprint_summary":{"type":"string","title":"Fingerprint Summary","description":"Short \"Agent · host\" label for dashboard column"},"token_status":{"type":"string","title":"Token Status","description":"active | expired | revoked (computed from agent_token_status view)"},"token_last_used_at":{"anyOf":[{},{"type":"null"}],"title":"Token Last Used At"},"token_created_at":{"anyOf":[{},{"type":"null"}],"title":"Token Created At"},"token_id":{"type":"string","format":"uuid","title":"Token Id"}},"type":"object","required":["id","owner_email","fingerprint_summary","token_status","token_id"],"title":"AgentUserListItem","description":"One row in the brand's Agents table."},"AgentUserListResponse":{"properties":{"agent_users":{"items":{"$ref":"#/components/schemas/AgentUserListItem"},"type":"array","title":"Agent Users"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["agent_users","total"],"title":"AgentUserListResponse"},"AggregationsResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"total_documents":{"type":"integer","title":"Total Documents","description":"Total indexed documents","default":0},"by_job_type":{"items":{"$ref":"#/components/schemas/FacetBucket"},"type":"array","title":"By Job Type","description":"Documents by job type","default":[]},"by_category":{"items":{"$ref":"#/components/schemas/FacetBucket"},"type":"array","title":"By Category","description":"Documents by category","default":[]},"by_country":{"items":{"$ref":"#/components/schemas/FacetBucket"},"type":"array","title":"By Country","description":"Documents by country","default":[]},"avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Rating","description":"Average rating"},"total_reviews":{"type":"integer","title":"Total Reviews","description":"Total reviews count","default":0},"verified_emails_count":{"type":"integer","title":"Verified Emails Count","description":"Verified emails count","default":0},"indexed_over_time":{"items":{"$ref":"#/components/schemas/TimeSeriesBucket"},"type":"array","title":"Indexed Over Time","description":"Indexing trend over time","default":[]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if any"}},"type":"object","title":"AggregationsResponse","description":"Analytics aggregations response."},"AiUsage":{"properties":{"model_used":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Used"},"total_cost":{"type":"number","title":"Total Cost","default":0.0},"total_tokens":{"type":"integer","title":"Total Tokens","default":0}},"type":"object","title":"AiUsage","description":"AI usage stats - always present even if empty"},"ApiKeyReauthRequest":{"properties":{"api_key":{"type":"string","maxLength":4096,"minLength":1,"title":"Api Key"}},"type":"object","required":["api_key"],"title":"ApiKeyReauthRequest"},"ApiKeyReauthResponse":{"properties":{"status":{"type":"string","title":"Status"},"valid":{"type":"boolean","title":"Valid"},"credential_id":{"type":"integer","title":"Credential Id"},"message":{"type":"string","title":"Message"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["status","valid","credential_id","message"],"title":"ApiKeyReauthResponse"},"ApiKeySubmitRequest":{"properties":{"api_key":{"type":"string","minLength":1,"title":"Api Key"},"contributor_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contributor Name"}},"type":"object","required":["api_key"],"title":"ApiKeySubmitRequest"},"ApiKeySubmitResponse":{"properties":{"status":{"type":"string","title":"Status"},"valid":{"type":"boolean","title":"Valid"},"models_count":{"type":"integer","title":"Models Count","default":0},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"message":{"type":"string","title":"Message"}},"type":"object","required":["status","valid","message"],"title":"ApiKeySubmitResponse"},"ApplyThemeRequest":{"properties":{"theme":{"type":"string","maxLength":100,"title":"Theme","default":"default"}},"type":"object","title":"ApplyThemeRequest"},"AttachmentSummary":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"filename":{"type":"string","title":"Filename"},"mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type"},"size_bytes":{"type":"integer","title":"Size Bytes"},"size_human":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size Human","description":"Human-readable size (e.g., '1.5MB')"},"type":{"$ref":"#/components/schemas/AttachmentType"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"Layer 1: LLM-generated summary (~100 tokens)"},"preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview","description":"Layer 2: First 1500 chars of extracted text"},"page_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page Count"},"row_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Row Count"},"column_names":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Column Names"},"ocr_used":{"type":"boolean","title":"Ocr Used","default":false},"retrieve_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retrieve Key","description":"Key for retrieving full content on demand"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["filename","size_bytes","type"],"title":"AttachmentSummary","description":"Attachment summary for agent payload and API responses.\n\nIncludes Layer 1 (summary) and Layer 2 (preview) of three-layer\nprogressive disclosure pattern."},"AttachmentType":{"type":"string","enum":["pdf","docx","xlsx","pptx","csv","image","text","html","json","email","archive","audio","video","unknown"],"title":"AttachmentType","description":"Classification of email attachment types."},"AttachmentUpload":{"properties":{"filename":{"type":"string","title":"Filename","description":"Filename with extension"},"content_base64":{"type":"string","title":"Content Base64","description":"Base64-encoded file content"},"mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type","description":"MIME type (auto-detected if not provided)"}},"type":"object","required":["filename","content_base64"],"title":"AttachmentUpload","description":"Attachment for outbound email (v2.53.0)\n\nUsed when sending emails with attachments via SpiderMail job."},"AuthStatusResponse":{"properties":{"authenticated":{"type":"boolean","title":"Authenticated"},"token_type":{"type":"string","title":"Token Type","description":"'pat' for agent token, 'bearer' for client credentials"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"fingerprint":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Fingerprint"},"usage_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usage Count"}},"type":"object","required":["authenticated","token_type"],"title":"AuthStatusResponse","description":"Response for auth status check."},"AuthorCreate":{"properties":{"full_name":{"type":"string","maxLength":200,"minLength":1,"title":"Full Name"},"slug":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Slug"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"email":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Email"},"role":{"allOf":[{"$ref":"#/components/schemas/AuthorRole"}],"default":"author"},"agent_type":{"allOf":[{"$ref":"#/components/schemas/AgentType"}],"default":"human"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"City"}},"type":"object","required":["full_name"],"title":"AuthorCreate","description":"Create a new author."},"AuthorListResponse":{"properties":{"authors":{"items":{"$ref":"#/components/schemas/AuthorResponse"},"type":"array","title":"Authors"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page","default":1},"page_size":{"type":"integer","title":"Page Size","default":50}},"type":"object","required":["authors","total"],"title":"AuthorListResponse","description":"List of authors."},"AuthorResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"full_name":{"type":"string","title":"Full Name"},"slug":{"type":"string","title":"Slug"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"role":{"type":"string","title":"Role","default":"author"},"agent_type":{"type":"string","title":"Agent Type","default":"human"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"is_active":{"type":"boolean","title":"Is Active","default":true},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","client_id","full_name","slug","created_at","updated_at"],"title":"AuthorResponse","description":"Author response with all fields."},"AuthorRole":{"type":"string","enum":["author","editor","contributor","administrator"],"title":"AuthorRole"},"AuthorSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"full_name":{"type":"string","title":"Full Name"},"slug":{"type":"string","title":"Slug"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"}},"type":"object","required":["id","full_name","slug"],"title":"AuthorSummary","description":"Minimal author info embedded in post responses."},"AuthorUpdate":{"properties":{"full_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Full Name"},"slug":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Slug"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"email":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Email"},"role":{"anyOf":[{"$ref":"#/components/schemas/AuthorRole"},{"type":"null"}]},"agent_type":{"anyOf":[{"$ref":"#/components/schemas/AgentType"},{"type":"null"}]},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"City"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"AuthorUpdate","description":"Update author fields."},"AuthoringHints":{"properties":{"preferred_path":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Preferred Path","description":"One-line nudge toward the canonical authoring tool, e.g. 'Use video_to_scroll_sequence MCP tool, not manual insert'."},"common_mistakes":{"items":{"type":"string"},"type":"array","title":"Common Mistakes","description":"Free-form list of historical agent mistakes for this component."},"must_set":{"items":{"type":"string"},"type":"array","title":"Must Set","description":"Prop keys that MUST be present (and non-empty) when inserting a block."},"must_not_set":{"items":{"type":"string"},"type":"array","title":"Must Not Set","description":"Prop keys that the component handles internally; setting them breaks runtime behaviour."}},"type":"object","title":"AuthoringHints","description":"Component-author-written hints surfaced in ``_rules.authored``.\n\nStored on ``content_components.authoring_hints`` JSONB (P5 migration 230).\nEmpty {} = no hints; the column defaults to that and degrades cleanly."},"AutoSearchCancelResponse":{"properties":{"auto_search_id":{"type":"string","title":"Auto Search Id","description":"Auto-search job UUID"},"status":{"allOf":[{"$ref":"#/components/schemas/AutoSearchStatus"}],"description":"New status (cancelled)"},"message":{"type":"string","title":"Message","description":"Confirmation message"},"pending_jobs_cancelled":{"type":"integer","title":"Pending Jobs Cancelled","description":"Number of pending queries that were cancelled"}},"type":"object","required":["auto_search_id","status","message","pending_jobs_cancelled"],"title":"AutoSearchCancelResponse","description":"Response schema for cancelling an auto-search","example":{"auto_search_id":"550e8400-e29b-41d4-a716-446655440000","message":"Auto-search cancelled successfully","pending_jobs_cancelled":25,"status":"cancelled"}},"AutoSearchListResponse":{"properties":{"auto_searches":{"items":{"$ref":"#/components/schemas/AutoSearchStatusResponse"},"type":"array","title":"Auto Searches","description":"List of auto-search jobs"},"total":{"type":"integer","title":"Total","description":"Total number of auto-search jobs for this client"},"page":{"type":"integer","title":"Page","description":"Current page number"},"page_size":{"type":"integer","title":"Page Size","description":"Number of items per page"}},"type":"object","required":["auto_searches","total","page","page_size"],"title":"AutoSearchListResponse","description":"Response schema for listing auto-search jobs","example":{"auto_searches":[],"page":1,"page_size":20,"total":5}},"AutoSearchResultsResponse":{"properties":{"auto_search_id":{"type":"string","title":"Auto Search Id","description":"Auto-search job UUID"},"status":{"allOf":[{"$ref":"#/components/schemas/AutoSearchStatus"}],"description":"Job status"},"results_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Results Url","description":"R2 CDN URL to download full results JSON file"},"unique_results_count":{"type":"integer","title":"Unique Results Count","description":"Total number of unique results"},"file_size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Size Bytes","description":"Size of results file in bytes"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When the job was completed"}},"type":"object","required":["auto_search_id","status","unique_results_count"],"title":"AutoSearchResultsResponse","description":"Response schema for auto-search results","example":{"auto_search_id":"550e8400-e29b-41d4-a716-446655440000","completed_at":"2025-10-30T12:15:30Z","file_size_bytes":1524288,"results_url":"https://cdn.spideriq.ai/auto-search/550e8400-e29b-41d4-a716-446655440000.json","status":"completed","unique_results_count":487}},"AutoSearchStatus":{"type":"string","enum":["pending","running","completed","failed","cancelled"],"title":"AutoSearchStatus","description":"Auto-search job status enumeration"},"AutoSearchStatusResponse":{"properties":{"auto_search_id":{"type":"string","title":"Auto Search Id","description":"Auto-search job UUID"},"client_id":{"type":"string","title":"Client Id","description":"Client identifier"},"keyword":{"type":"string","title":"Keyword","description":"Search keyword"},"country":{"type":"string","title":"Country","description":"Country code"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State","description":"State code"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City name"},"total_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Limit","description":"Maximum unique results"},"status":{"allOf":[{"$ref":"#/components/schemas/AutoSearchStatus"}],"description":"Current status"},"unique_results_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Unique Results Count","description":"Number of unique results collected so far"},"queries_submitted":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Queries Submitted","description":"Number of queries submitted"},"queries_completed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Queries Completed","description":"Number of queries completed"},"results_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Results Url","description":"R2 CDN URL to download results (available when status=completed)"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Job creation time"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"Job completion time"}},"type":"object","required":["auto_search_id","client_id","keyword","country","status","created_at"],"title":"AutoSearchStatusResponse","description":"Response schema for auto-search status","example":{"auto_search_id":"550e8400-e29b-41d4-a716-446655440000","city":"Los Angeles","client_id":"client_123","country":"US","created_at":"2025-10-30T12:00:00Z","keyword":"pizza restaurant","queries_completed":10,"queries_submitted":15,"state":"CA","status":"running","total_limit":500,"unique_results_count":234}},"AutoSearchSubmitRequest":{"properties":{"keyword":{"type":"string","maxLength":255,"minLength":1,"title":"Keyword","description":"Search keyword (e.g., 'pizza restaurant', 'dentist', 'hotel')","example":"pizza restaurant"},"country":{"type":"string","maxLength":2,"minLength":2,"title":"Country","description":"ISO 2-letter country code (e.g., 'US')","example":"US"},"state":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"State","description":"State code or name (e.g., 'CA', 'California'). If omitted, searches entire country.","example":"CA"},"city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"City","description":"City name (e.g., 'Los Angeles'). Requires state to be specified.","example":"Los Angeles"},"total_limit":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Total Limit","description":"Maximum number of unique results to collect. If omitted, collects all available results.","example":500}},"type":"object","required":["keyword","country"],"title":"AutoSearchSubmitRequest","description":"Request schema for submitting a new auto-search job.\n\nThe auto-search feature automatically generates location-specific queries\nand collects unique business results across a geographic area.","example":{"city":"Los Angeles","country":"US","keyword":"pizza restaurant","state":"CA","total_limit":500}},"AutoSearchSubmitResponse":{"properties":{"auto_search_id":{"type":"string","title":"Auto Search Id","description":"Unique identifier for the auto-search job"},"status":{"allOf":[{"$ref":"#/components/schemas/AutoSearchStatus"}],"description":"Initial status of the auto-search job"},"message":{"type":"string","title":"Message","description":"Human-readable message"},"estimated_queries":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Queries","description":"Estimated number of queries that will be generated"}},"type":"object","required":["auto_search_id","status","message"],"title":"AutoSearchSubmitResponse","description":"Response schema for auto-search submission","example":{"auto_search_id":"550e8400-e29b-41d4-a716-446655440000","estimated_queries":50,"message":"Auto-search job created successfully","status":"pending"}},"AutocompleteResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"suggestions":{"items":{"$ref":"#/components/schemas/AutocompleteSuggestion"},"type":"array","title":"Suggestions","description":"Autocomplete suggestions"}},"type":"object","required":["suggestions"],"title":"AutocompleteResponse","description":"Autocomplete response."},"AutocompleteSuggestion":{"properties":{"text":{"type":"string","title":"Text","description":"Suggested text"},"score":{"type":"number","title":"Score","description":"Suggestion score"}},"type":"object","required":["text","score"],"title":"AutocompleteSuggestion","description":"Autocomplete suggestion."},"AvailabilitySettings":{"properties":{"default_buffer_minutes":{"anyOf":[{"type":"integer","maximum":240.0,"minimum":0.0},{"type":"null"}],"title":"Default Buffer Minutes"},"default_lead_time_minutes":{"anyOf":[{"type":"integer","maximum":60000.0,"minimum":0.0},{"type":"null"}],"title":"Default Lead Time Minutes"},"default_max_advance_days":{"anyOf":[{"type":"integer","maximum":730.0,"minimum":1.0},{"type":"null"}],"title":"Default Max Advance Days"},"same_day_cutoff_time":{"anyOf":[{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},{"type":"null"}],"title":"Same Day Cutoff Time"}},"additionalProperties":true,"type":"object","title":"AvailabilitySettings"},"BgVideoAgentMeta":{"properties":{"pace":{"anyOf":[{"type":"string","enum":["slow","medium","fast"]},{"type":"null"}],"title":"Pace","description":"Visual pacing of the clip. slow=meditative, medium=narrative, fast=energetic."},"time_of_day":{"anyOf":[{"type":"string","enum":["dawn","day","dusk","night"]},{"type":"null"}],"title":"Time Of Day","description":"Time of day depicted, when discernible."},"weather":{"anyOf":[{"type":"string","enum":["clear","cloudy","rain","snow","fog","stormy"]},{"type":"null"}],"title":"Weather","description":"Weather depicted, when discernible. NULL = indoor / abstract / not weather-relevant."},"has_people":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has People","description":"TRUE if humans are visible. NULL = unknown (not yet curated)."},"aspect_ratio":{"anyOf":[{"type":"string","enum":["16:9","9:16","1:1","4:3","21:9"]},{"type":"null"}],"title":"Aspect Ratio","description":"Aspect ratio of the source clip. Renderer adapts; agent filters by display target."},"has_audio":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Audio","description":"TRUE if the source clip has an audio track. Most bg-videos play muted; this flags those that have audio worth preserving."},"music_tempo_bpm":{"anyOf":[{"type":"integer","maximum":300.0,"minimum":20.0},{"type":"null"}],"title":"Music Tempo Bpm","description":"If has_audio: source music BPM. Agents filter by mood/tempo combinations."},"transcript":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Transcript","description":"Optional transcript or scene description for accessibility + agent context. SHORT — under 2000 chars."}},"additionalProperties":false,"type":"object","title":"BgVideoAgentMeta","description":"Per-bg-video discovery axes.\n\nReading agent: Antigravity Vibe-Tool, Claude Code, OpenClaw skills.\nUsed when an agent picks a hero video for a brand brief."},"BgVideoCreateRequest":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"title":"Slug"},"name":{"type":"string","maxLength":256,"minLength":1,"title":"Name"},"video_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Video Url"},"poster_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Poster Url"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"duration_seconds":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Duration Seconds"},"loop_seconds":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Loop Seconds"},"category":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Category"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"file_size_bytes":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"File Size Bytes"},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"mood":{"anyOf":[{"items":{"$ref":"#/components/schemas/Mood"},"type":"array"},{"type":"null"}],"title":"Mood"},"palette":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":12},{"type":"null"}],"title":"Palette","description":"Free-form palette tokens — DB column is permissive TEXT[]; cap list length at 12."},"brand_fit_tags":{"anyOf":[{"items":{"$ref":"#/components/schemas/BrandFit"},"type":"array"},{"type":"null"}],"title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"$ref":"#/components/schemas/SceneType"},{"type":"null"}]},"agent_meta":{"anyOf":[{"$ref":"#/components/schemas/BgVideoAgentMeta"},{"type":"null"}]},"replication_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Replication Prompt"}},"type":"object","required":["slug","name","video_url"],"title":"BgVideoCreateRequest","description":"Create a new bg-video catalog row."},"BgVideoUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":256,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"video_url":{"anyOf":[{"type":"string","maxLength":2048,"minLength":1},{"type":"null"}],"title":"Video Url"},"poster_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Poster Url"},"duration_seconds":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Duration Seconds"},"loop_seconds":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Loop Seconds"},"category":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Category"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"file_size_bytes":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"File Size Bytes"},"is_featured":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"},"mood":{"anyOf":[{"items":{"$ref":"#/components/schemas/Mood"},"type":"array"},{"type":"null"}],"title":"Mood"},"palette":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":12},{"type":"null"}],"title":"Palette"},"brand_fit_tags":{"anyOf":[{"items":{"$ref":"#/components/schemas/BrandFit"},"type":"array"},{"type":"null"}],"title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"$ref":"#/components/schemas/SceneType"},{"type":"null"}]},"agent_meta":{"anyOf":[{"$ref":"#/components/schemas/BgVideoAgentMeta"},{"type":"null"}]},"replication_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Replication Prompt"}},"type":"object","title":"BgVideoUpdateRequest","description":"Patch an existing bg-video catalog row."},"BgVideoUploadResponse":{"properties":{"key":{"type":"string","title":"Key"},"url":{"type":"string","title":"Url"},"size_bytes":{"type":"integer","title":"Size Bytes"},"content_type":{"type":"string","title":"Content Type"}},"type":"object","required":["key","url","size_bytes","content_type"],"title":"BgVideoUploadResponse","description":"Returned by POST /dashboard/content/bg-videos/upload."},"BillingCycle":{"properties":{"used":{"type":"integer","title":"Used","default":0},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit"},"periodEnd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Periodend"}},"type":"object","title":"BillingCycle"},"BillingPortalResponse":{"properties":{"url":{"type":"string","minLength":1,"title":"Url"}},"type":"object","required":["url"],"title":"BillingPortalResponse"},"BlockType":{"type":"string","enum":["hero","features_grid","stats_bar","rich_text","cta_section","pricing_table","testimonials","faq","code_example","logo_cloud","comparison_table","image","video_embed","spacer","component"],"title":"BlockType"},"BoardCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Icon"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"board_type":{"type":"string","pattern":"^(companies|contacts|deals)$","title":"Board Type"},"seed_default_columns":{"type":"boolean","title":"Seed Default Columns","default":true},"column_preset":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Column Preset"}},"type":"object","required":["name","board_type"],"title":"BoardCreate","description":"Create a CRM board. board_type restricted to {companies, contacts, deals}."},"BoardGroupResponse":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"kind":{"type":"string","title":"Kind"},"settings":{"type":"object","title":"Settings"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"boards":{"items":{"$ref":"#/components/schemas/BoardResponse"},"type":"array","title":"Boards"}},"type":"object","required":["name","id","client_id","kind","settings","created_at","updated_at"],"title":"BoardGroupResponse"},"BoardGroupUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"settings":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Settings"}},"type":"object","title":"BoardGroupUpdate"},"BoardMeta":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"board_type":{"type":"string","title":"Board Type"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"task_count":{"type":"integer","title":"Task Count","default":0}},"type":"object","required":["id","name","board_type"],"title":"BoardMeta"},"BoardResponse":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Icon"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"board_type":{"type":"string","title":"Board Type"},"is_default":{"type":"boolean","title":"Is Default"},"is_archived":{"type":"boolean","title":"Is Archived"},"settings":{"type":"object","title":"Settings"},"field_config":{"items":{"type":"object"},"type":"array","title":"Field Config"},"produced_field_config":{"items":{"type":"object"},"type":"array","title":"Produced Field Config"},"reserved_field_keys":{"items":{"type":"string"},"type":"array","title":"Reserved Field Keys"},"group_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Group Id"},"group_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group Role"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"column_count":{"type":"integer","title":"Column Count","default":0},"task_count":{"type":"integer","title":"Task Count","default":0}},"type":"object","required":["name","id","client_id","board_type","is_default","is_archived","settings","created_at","updated_at"],"title":"BoardResponse"},"BoardUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Icon"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"is_archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Archived"},"settings":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Settings"},"field_config":{"anyOf":[{"items":{"$ref":"#/components/schemas/FieldConfigItem"},"type":"array"},{"type":"null"}],"title":"Field Config"},"idap_source":{"anyOf":[{"$ref":"#/components/schemas/IdapSourceConfig"},{"type":"null"}]}},"type":"object","title":"BoardUpdate"},"Body_create_transcription_api_gate_v1_audio_transcriptions_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"Audio file (mp3, mp4, mpeg, mpga, m4a, wav, webm). Max 25MB."},"model":{"type":"string","title":"Model","description":"Transcription model: 'whisper-1', 'gpt-4o-transcribe', 'gpt-4o-mini-transcribe'."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"ISO-639-1 language code (improves accuracy). 'en', 'de', 'es', ..."},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt","description":"Optional context prompt to bias the transcription style."},"response_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Format","description":"Output format: 'json' (default), 'text', 'srt', 'verbose_json', 'vtt'."},"temperature":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Temperature","description":"Sampling temperature [0, 1]. 0 = deterministic."}},"type":"object","required":["file","model"],"title":"Body_create_transcription_api_gate_v1_audio_transcriptions_post"},"Body_upload_agent_avatar_api_v1_brands__brand_id__gate_agent_keys_avatar_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_agent_avatar_api_v1_brands__brand_id__gate_agent_keys_avatar_post"},"Body_upload_bg_video_asset_api_v1_dashboard_content_bg_videos_upload_post":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"title":"Slug"},"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["slug","ext","file"],"title":"Body_upload_bg_video_asset_api_v1_dashboard_content_bg_videos_upload_post"},"Body_upload_bg_video_asset_api_v1_dashboard_projects__project_id__content_bg_videos_upload_post":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"title":"Slug"},"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["slug","ext","file"],"title":"Body_upload_bg_video_asset_api_v1_dashboard_projects__project_id__content_bg_videos_upload_post"},"Body_upload_brand_logo_api_v1_brands__brand_id__logo_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_brand_logo_api_v1_brands__brand_id__logo_post"},"Body_upload_component_preview_api_v1_dashboard_content_components__component_id__upload_preview_post":{"properties":{"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["ext","file"],"title":"Body_upload_component_preview_api_v1_dashboard_content_components__component_id__upload_preview_post"},"Body_upload_component_preview_api_v1_dashboard_projects__project_id__content_components__component_id__upload_preview_post":{"properties":{"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["ext","file"],"title":"Body_upload_component_preview_api_v1_dashboard_projects__project_id__content_components__component_id__upload_preview_post"},"Body_upload_media_api_v1_dashboard_content_media_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"folder":{"type":"string","title":"Folder","default":"/"},"alt_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alt Text"}},"type":"object","required":["file"],"title":"Body_upload_media_api_v1_dashboard_content_media_upload_post"},"Body_upload_media_api_v1_dashboard_projects__project_id__content_media_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"folder":{"type":"string","title":"Folder","default":"/"},"alt_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alt Text"}},"type":"object","required":["file"],"title":"Body_upload_media_api_v1_dashboard_projects__project_id__content_media_upload_post"},"Body_upload_media_batch_api_v1_dashboard_content_media_upload_batch_post":{"properties":{"files":{"items":{"type":"string","format":"binary"},"type":"array","title":"Files","description":"Files to upload (one or many)"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder","description":"Target folder in the bucket, e.g. 'scroll-sequences/hero' or 'migrations/homepage'"},"preserve_filename":{"type":"boolean","title":"Preserve Filename","description":"If true, keys are {folder}/{filename} exactly (LLM11 2026-04-18+). Required when uploading for sys-scroll-sequence `{base_url, pattern, count}` shape.","default":false},"is_scroll_sequence":{"type":"boolean","title":"Is Scroll Sequence","description":"Force scroll-sequence weight policy regardless of folder name. Default: folder starting with 'scroll-sequences/' auto-enables this.","default":false}},"type":"object","required":["files"],"title":"Body_upload_media_batch_api_v1_dashboard_content_media_upload_batch_post"},"Body_upload_media_batch_api_v1_dashboard_media_upload_batch_post":{"properties":{"files":{"items":{"type":"string","format":"binary"},"type":"array","title":"Files","description":"One or many files to upload"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder","description":"Target folder in the bucket"},"preserve_filename":{"type":"boolean","title":"Preserve Filename","description":"If true, keys are {folder}/{filename} exactly (no timestamp prefix)","default":false}},"type":"object","required":["files"],"title":"Body_upload_media_batch_api_v1_dashboard_media_upload_batch_post"},"Body_upload_media_batch_api_v1_dashboard_projects__project_id__content_media_upload_batch_post":{"properties":{"files":{"items":{"type":"string","format":"binary"},"type":"array","title":"Files","description":"Files to upload (one or many)"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder","description":"Target folder in the bucket, e.g. 'scroll-sequences/hero' or 'migrations/homepage'"},"preserve_filename":{"type":"boolean","title":"Preserve Filename","description":"If true, keys are {folder}/{filename} exactly (LLM11 2026-04-18+). Required when uploading for sys-scroll-sequence `{base_url, pattern, count}` shape.","default":false},"is_scroll_sequence":{"type":"boolean","title":"Is Scroll Sequence","description":"Force scroll-sequence weight policy regardless of folder name. Default: folder starting with 'scroll-sequences/' auto-enables this.","default":false}},"type":"object","required":["files"],"title":"Body_upload_media_batch_api_v1_dashboard_projects__project_id__content_media_upload_batch_post"},"Body_upload_media_image_api_v1_dashboard_media_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"Image file (JPEG/PNG/GIF/WebP/BMP, ≤5MB) — transcoded to WebP"},"folder":{"type":"string","title":"Folder","description":"Target DAM folder, e.g. 'logos' or 'hero'","default":"/"},"alt_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alt Text","description":"Alt text for accessibility"}},"type":"object","required":["file"],"title":"Body_upload_media_image_api_v1_dashboard_media_upload_post"},"Body_upload_profile_photo_api_v1_auth_profile_photo_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_profile_photo_api_v1_auth_profile_photo_post"},"Body_upload_site_template_preview_api_v1_dashboard_content_site_templates__slug__upload_preview_post":{"properties":{"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["ext","file"],"title":"Body_upload_site_template_preview_api_v1_dashboard_content_site_templates__slug__upload_preview_post"},"Body_upload_site_template_preview_api_v1_dashboard_projects__project_id__content_site_templates__slug__upload_preview_post":{"properties":{"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["ext","file"],"title":"Body_upload_site_template_preview_api_v1_dashboard_projects__project_id__content_site_templates__slug__upload_preview_post"},"BookingFlow":{"properties":{"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"business_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Business Id"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"flow":{"type":"object","title":"Flow"},"schema":{"type":"object","title":"Schema"},"kind":{"type":"string","maxLength":16,"minLength":1,"title":"Kind","default":"booking"},"cal_event_type_id":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Cal Event Type Id"},"cal_team_id":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Cal Team Id"},"version":{"type":"integer","minimum":1.0,"title":"Version"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"},"model":{"type":"string","maxLength":48,"minLength":1,"title":"Model","default":"one_to_one_meeting"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"is_locked":{"type":"boolean","title":"Is Locked","default":false},"locked_by_actor_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Locked By Actor Id"},"locked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Locked At"},"locked_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked Reason"},"last_edited_by_actor_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Last Edited By Actor Id"},"last_edited_by_actor_role":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Last Edited By Actor Role"},"last_edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Edited At"},"submission_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Submission Count"}},"type":"object","required":["flow_id","name","version","status","created_at","updated_at"],"title":"BookingFlow","description":"Row shape returned by single-row and list endpoints."},"BookingFlowCreate":{"properties":{"business_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Business Id"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"flow":{"type":"object","title":"Flow"},"schema":{"type":"object","title":"Schema"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"model":{"anyOf":[{"$ref":"#/components/schemas/FlowModel"},{"type":"null"}]},"kind":{"type":"string","maxLength":16,"minLength":1,"title":"Kind","default":"booking"}},"type":"object","required":["name"],"title":"BookingFlowCreate","description":"Create payload — ``status`` starts at ``draft``; caller cannot set it.\n\nSpiderFlow P1.Z + P2.1 — ``business_id`` semantics by kind:\n\n* ``kind='booking'`` / ``'commerce'`` — REQUIRED. The flow targets a\n  real ``norm_cli_*.businesses`` row and inherits booking semantics\n  (Cal.com event-type, slot holds, ICS, etc.).\n* ``kind='form'`` — FORBIDDEN. Forms don't carry booking semantics;\n  the create path resolves a per-tenant sentinel business to satisfy\n  the FK from mig 123 without polluting the surface contract.\n* ``kind='funnel'`` (P2.1) — FORBIDDEN. Funnels sequence SpiderPublish\n  pages; they aren't scoped to a SpiderBook business either. The\n  create path resolves the same per-tenant sentinel as forms at draft\n  time, then at publish time swaps to a per-funnel sentinel\n  (``{{ spiderflow.funnels.sentinel:<flow_uuid> }}``) so the partial\n  UNIQUE ``booking_flows_one_active_per_business_idx`` is satisfied\n  vacuously — mirrors the P1.W3 form correction.\n\nThe ``kind`` field is also new on this payload (P1.Z) — pre-P1.Z callers\nthat omit it get ``kind='booking'`` (back-compat with every SpiderBook\nP2.1+ booking flow that ever shipped)."},"BookingFlowList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BookingFlow"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"BookingFlowList","description":"Envelope for the paginated list endpoint."},"BookingFlowPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":512,"minLength":1},{"type":"null"}],"title":"Name"},"flow":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Flow"},"schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Schema"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"model":{"anyOf":[{"$ref":"#/components/schemas/FlowModel"},{"type":"null"}]},"translations":{"anyOf":[{"additionalProperties":{"additionalProperties":{"type":"string"},"type":"object"},"type":"object"},{"type":"null"}],"title":"Translations"},"dry_run":{"type":"boolean","title":"Dry Run","default":false},"confirm_token":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirm Token"}},"type":"object","title":"BookingFlowPatch","description":"Partial update. Only provided fields are applied.\n\n``version`` is never user-settable — we bump it server-side whenever\n``flow`` or ``schema`` change (G17 versioning rule: flow edits invalidate\nany cached public render).\n\nSpiderFlow P1.W (2026-05-13) — opt-in Phase 11+12 gate. When ``dry_run=true``\nthe endpoint returns a preview envelope with a confirm_token (no mutation).\nCalling again with the issued ``confirm_token`` performs the real update.\nOmitting both leaves the legacy direct-mutation path intact (back-compat\nwith the booking_flow_update behaviour every existing caller depends on)."},"BookingService":{"properties":{"service_id":{"type":"string","format":"uuid","title":"Service Id"},"business_id":{"type":"string","format":"uuid","title":"Business Id"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Description"},"duration_minutes":{"type":"integer","maximum":480.0,"minimum":1.0,"title":"Duration Minutes"},"price_cents":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Price Cents"},"currency":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Currency"},"image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Image Url"},"sort_order":{"type":"integer","title":"Sort Order","default":0},"active":{"type":"boolean","title":"Active"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["service_id","business_id","name","duration_minutes","active","created_at","updated_at"],"title":"BookingService"},"BookingServiceCreate":{"properties":{"business_id":{"type":"string","format":"uuid","title":"Business Id"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Description"},"duration_minutes":{"type":"integer","maximum":480.0,"minimum":1.0,"title":"Duration Minutes"},"price_cents":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Price Cents"},"currency":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Currency","default":"USD"},"image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Image Url"},"sort_order":{"type":"integer","title":"Sort Order","default":0},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["business_id","name","duration_minutes"],"title":"BookingServiceCreate"},"BookingServiceList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BookingService"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"BookingServiceList"},"BookingServicePatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":512,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Description"},"duration_minutes":{"anyOf":[{"type":"integer","maximum":480.0,"minimum":1.0},{"type":"null"}],"title":"Duration Minutes"},"price_cents":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Price Cents"},"currency":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Currency"},"image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Image Url"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"BookingServicePatch"},"BookingSettings":{"properties":{"general":{"$ref":"#/components/schemas/GeneralSettings"},"availability":{"$ref":"#/components/schemas/AvailabilitySettings"},"notifications":{"$ref":"#/components/schemas/NotificationsSettings"},"payments":{"$ref":"#/components/schemas/PaymentsSettings"},"policies":{"$ref":"#/components/schemas/PoliciesSettings"},"integrations":{"$ref":"#/components/schemas/IntegrationsSettings"},"branding":{"$ref":"#/components/schemas/BrandingSettings"},"team":{"items":{"type":"object"},"type":"array","maxItems":200,"title":"Team"}},"additionalProperties":false,"type":"object","title":"BookingSettings","description":"Full per-tenant booking settings.\n\nWritten to ``public.clients.booking_settings`` JSONB. The 8 nested\nsections mirror the 8 tabs in the Settings UI. Missing sub-sections\nare populated with defaults by :meth:`default`; the frontend can round-trip\nany stored extras via the ``extra='allow'`` on sub-sections."},"BookingSettingsPatch":{"properties":{"general":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"General"},"availability":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Availability"},"notifications":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Notifications"},"payments":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Payments"},"policies":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Policies"},"integrations":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Integrations"},"branding":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Branding"},"team":{"anyOf":[{"items":{"type":"object"},"type":"array","maxItems":200},{"type":"null"}],"title":"Team"}},"additionalProperties":false,"type":"object","title":"BookingSettingsPatch","description":"Partial update — only provided sub-sections are merged. Unspecified\nkeys within a provided sub-section are left at existing values."},"BookingSummary":{"properties":{"booking_id":{"type":"string","format":"uuid","title":"Booking Id"},"cal_booking_uid":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Cal Booking Uid"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"},"slot_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Slot Start"},"slot_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Slot End"}},"type":"object","required":["booking_id","status"],"title":"BookingSummary"},"BookingTemplate":{"properties":{"template_id":{"type":"string","format":"uuid","title":"Template Id"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"category":{"type":"string","maxLength":64,"minLength":1,"title":"Category"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"flow":{"type":"object","title":"Flow"},"schema":{"type":"object","title":"Schema"},"usage_count":{"type":"integer","minimum":0.0,"title":"Usage Count"},"cloned_from":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cloned From"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["template_id","name","category","usage_count","created_at","updated_at"],"title":"BookingTemplate","description":"Per-client template shape (``norm_cli_*.booking_templates``)."},"BookingTemplateClone":{"properties":{"global_template_id":{"type":"string","format":"uuid","title":"Global Template Id"},"name_override":{"anyOf":[{"type":"string","maxLength":512,"minLength":1},{"type":"null"}],"title":"Name Override"}},"type":"object","required":["global_template_id"],"title":"BookingTemplateClone","description":"Clone a global template into the caller's per-client library.\n\n``name_override`` lets the caller rename at clone time (e.g. appending a\ncity suffix). Defaults to the global template's ``name``."},"BookingTemplateCreate":{"properties":{"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"category":{"type":"string","maxLength":64,"minLength":1,"title":"Category"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"flow":{"type":"object","title":"Flow"},"schema":{"type":"object","title":"Schema"}},"type":"object","required":["name","category"],"title":"BookingTemplateCreate","description":"Create a new per-client template from scratch (SpiderFlow P1.M2).\n\nUsed by the admin form-template authoring surface so authoring brands\n(``clients.is_marketplace_authoring=TRUE``) and super_admins can write new\ntemplate rows without going through the clone-from-global flow. ``flow``\ntypically carries ``kind='form'`` and the full FormFlow shape from\n``app/schemas/booking.py`` — the JSONB columns are stored verbatim."},"BookingTemplateList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BookingTemplate"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"BookingTemplateList"},"BookingTemplatePatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":512,"minLength":1},{"type":"null"}],"title":"Name"},"category":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Category"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"flow":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Flow"},"schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Schema"}},"type":"object","title":"BookingTemplatePatch"},"BrandCreate":{"properties":{"brand_name":{"type":"string","maxLength":255,"minLength":1,"title":"Brand Name"},"slug":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Slug"}},"type":"object","required":["brand_name"],"title":"BrandCreate","description":"Create a new brand."},"BrandFit":{"type":"string","enum":["saas","agency","ecommerce","fintech","real-estate","hospitality","restaurant","wellness","healthcare","blog","publication","personal","tech","design","consulting","outdoor","lifestyle"],"title":"BrandFit","description":"Universal axis — multi-value industry vertical fit.\n\nStored in brand_fit_tags TEXT[] columns. Multi-value: an asset can fit\nsaas + agency + tech."},"BrandInformationResponse":{"properties":{"id":{"type":"integer","title":"Id"},"brand_id":{"type":"integer","title":"Brand Id"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"mobile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mobile"},"street1":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street1"},"street2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street2"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"postcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postcode"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"vat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Id"},"support_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Support Email"},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin"},"youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram"},"x":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Facebook"},"tiktok":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tiktok"}},"type":"object","required":["id","brand_id"],"title":"BrandInformationResponse","description":"Brand information response."},"BrandInformationUpdate":{"properties":{"company_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Company Name"},"website":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Website"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"industry":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Industry"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone"},"mobile":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Mobile"},"street1":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Street1"},"street2":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Street2"},"city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"City"},"state":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"State"},"postcode":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Postcode"},"country":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Country"},"tax_id":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Tax Id"},"vat_id":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Vat Id"},"support_email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Support Email"},"linkedin":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin"},"youtube":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Youtube"},"instagram":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Instagram"},"x":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"X"},"facebook":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Facebook"},"tiktok":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Tiktok"}},"type":"object","title":"BrandInformationUpdate","description":"Update brand business information."},"BrandListResponse":{"properties":{"brands":{"items":{"$ref":"#/components/schemas/BrandResponse"},"type":"array","title":"Brands"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["brands","total"],"title":"BrandListResponse","description":"List of brands with pagination."},"BrandMemberListResponse":{"properties":{"members":{"items":{"$ref":"#/components/schemas/BrandMemberResponse"},"type":"array","title":"Members"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["members","total"],"title":"BrandMemberListResponse","description":"List of brand members."},"BrandMemberResponse":{"properties":{"id":{"type":"integer","title":"Id"},"user_id":{"type":"string","title":"User Id"},"email":{"type":"string","title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image"},"role":{"$ref":"#/components/schemas/MembershipRole"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"status":{"$ref":"#/components/schemas/MembershipStatus"},"joined_at":{"type":"string","format":"date-time","title":"Joined At"}},"type":"object","required":["id","user_id","email","role","status","joined_at"],"title":"BrandMemberResponse","description":"Brand member details."},"BrandMemberUpdate":{"properties":{"role":{"anyOf":[{"$ref":"#/components/schemas/MembershipRole"},{"type":"null"}]},"position":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Position"},"status":{"anyOf":[{"$ref":"#/components/schemas/MembershipStatus"},{"type":"null"}]}},"type":"object","title":"BrandMemberUpdate","description":"Update a brand member."},"BrandReauthRequest":{"properties":{"message":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Message","description":"Optional personal note shown in the re-auth email (mirrors the Invite Contributor message field)."}},"type":"object","title":"BrandReauthRequest"},"BrandReauthResponse":{"properties":{"credential_id":{"type":"integer","title":"Credential Id"},"reauth_url":{"type":"string","title":"Reauth Url"},"expires_at":{"type":"string","title":"Expires At"},"recipients":{"items":{"type":"string"},"type":"array","title":"Recipients"},"email_sent":{"type":"boolean","title":"Email Sent"},"message":{"type":"string","title":"Message"}},"type":"object","required":["credential_id","reauth_url","expires_at","recipients","email_sent","message"],"title":"BrandReauthResponse"},"BrandResponse":{"properties":{"id":{"type":"integer","title":"Id"},"brand_name":{"type":"string","title":"Brand Name"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug"},"subscription_status":{"allOf":[{"$ref":"#/components/schemas/SubscriptionStatus"}],"default":"active"},"is_active":{"type":"boolean","title":"Is Active","default":true},"billing_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billing Email"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"member_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Member Count"},"client_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Client Count"},"client_ids":{"items":{"type":"string"},"type":"array","title":"Client Ids"},"membership_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Membership Role"},"joined_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Joined At"}},"type":"object","required":["id","brand_name","created_at"],"title":"BrandResponse","description":"Brand details response."},"BrandSettingsResponse":{"properties":{"id":{"type":"integer","title":"Id"},"brand_id":{"type":"integer","title":"Brand Id"},"default_rate_limit":{"type":"integer","title":"Default Rate Limit","default":100},"default_campaign_quota_daily":{"type":"integer","title":"Default Campaign Quota Daily","default":10},"default_campaign_quota_monthly":{"type":"integer","title":"Default Campaign Quota Monthly","default":100},"default_campaign_max_concurrent":{"type":"integer","title":"Default Campaign Max Concurrent","default":3},"default_campaign_max_locations":{"type":"integer","title":"Default Campaign Max Locations","default":1000},"default_auto_campaign_enabled":{"type":"boolean","title":"Default Auto Campaign Enabled","default":false},"default_auto_campaign_rate_per_hour":{"type":"integer","title":"Default Auto Campaign Rate Per Hour","default":20},"default_auto_campaign_capacity_threshold":{"type":"integer","title":"Default Auto Campaign Capacity Threshold","default":80},"fuzziq_enabled":{"type":"boolean","title":"Fuzziq Enabled","default":true},"opensearch_enabled":{"type":"boolean","title":"Opensearch Enabled","default":false},"brand_voice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Voice"},"brand_tone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Tone"},"brand_name_meaning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Name Meaning"},"brand_promise":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Promise"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"}},"type":"object","required":["id","brand_id"],"title":"BrandSettingsResponse","description":"Brand settings response."},"BrandSettingsUpdate":{"properties":{"default_rate_limit":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Default Rate Limit"},"default_campaign_quota_daily":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Default Campaign Quota Daily"},"default_campaign_quota_monthly":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Default Campaign Quota Monthly"},"default_campaign_max_concurrent":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Default Campaign Max Concurrent"},"default_campaign_max_locations":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Default Campaign Max Locations"},"default_auto_campaign_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Default Auto Campaign Enabled"},"default_auto_campaign_rate_per_hour":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Default Auto Campaign Rate Per Hour"},"default_auto_campaign_capacity_threshold":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Default Auto Campaign Capacity Threshold"},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled"},"opensearch_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Opensearch Enabled"},"brand_voice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Voice"},"brand_tone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Tone"},"brand_name_meaning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Name Meaning"},"brand_promise":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Promise"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"}},"type":"object","title":"BrandSettingsUpdate","description":"Update brand settings."},"BrandUpdate":{"properties":{"brand_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Brand Name"},"subscription_status":{"anyOf":[{"$ref":"#/components/schemas/SubscriptionStatus"},{"type":"null"}]},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"billing_email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Billing Email"}},"type":"object","title":"BrandUpdate","description":"Update brand fields."},"BrandingSettings":{"properties":{"primary_color":{"anyOf":[{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Primary Color"},"button_radius_px":{"type":"integer","maximum":24.0,"minimum":0.0,"title":"Button Radius Px","default":6},"custom_domain":{"anyOf":[{"type":"string","maxLength":253},{"type":"null"}],"title":"Custom Domain"},"white_label":{"type":"boolean","title":"White Label","default":false}},"additionalProperties":true,"type":"object","title":"BrandingSettings"},"BrokerExchangeRequest":{"properties":{"code":{"type":"string","maxLength":512,"minLength":1,"title":"Code"},"origin":{"type":"string","maxLength":2048,"minLength":1,"title":"Origin"}},"type":"object","required":["code","origin"],"title":"BrokerExchangeRequest"},"BrokerExchangeResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"session":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Session","description":"Verified Better Auth session payload"}},"type":"object","title":"BrokerExchangeResponse"},"BrokerForgotPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"origin":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Origin"}},"type":"object","required":["email"],"title":"BrokerForgotPasswordRequest"},"BrokerLoginRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","maxLength":256,"minLength":1,"title":"Password"},"origin":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Origin"}},"type":"object","required":["email","password"],"title":"BrokerLoginRequest"},"BrokerLoginResponse":{"properties":{"one_time_code":{"type":"string","title":"One Time Code","description":"Single-use, origin-bound, short-TTL handoff code"},"expires_in":{"type":"integer","maximum":120.0,"minimum":1.0,"title":"Expires In","description":"Seconds until the code expires"}},"type":"object","required":["one_time_code","expires_in"],"title":"BrokerLoginResponse"},"BrokerOkResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true}},"type":"object","title":"BrokerOkResponse"},"BrokerResetPasswordRequest":{"properties":{"token":{"type":"string","maxLength":2048,"minLength":1,"title":"Token"},"password":{"type":"string","maxLength":256,"minLength":8,"title":"Password"}},"type":"object","required":["token","password"],"title":"BrokerResetPasswordRequest"},"BrokerSignupRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","maxLength":256,"minLength":8,"title":"Password"},"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"origin":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Origin"}},"type":"object","required":["email","password"],"title":"BrokerSignupRequest","description":"Dashboard self-serve signup (Login Initiative B · B7)."},"BrokerSignupResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"verification_required":{"type":"boolean","title":"Verification Required","default":true}},"type":"object","title":"BrokerSignupResponse","description":"Generic, NON-enumerating signup response.\n\nThe same shape is returned for a brand-new account AND a duplicate email —\nthe brick shows \"check your email\" either way, so a caller can't probe which\naddresses already have an account. ``verification_required`` is always true\n(the account, if created, is unverified until the emailed link is clicked)."},"BrowseBundle":{"properties":{"id":{"type":"string","minLength":1,"title":"Id"},"name":{"type":"string","minLength":1,"title":"Name"},"tariff_ids":{"items":{"type":"string"},"type":"array","title":"Tariff Ids"},"discount_percent":{"type":"number","maximum":100.0,"minimum":0.0,"title":"Discount Percent","default":0},"status":{"type":"string","minLength":1,"title":"Status"},"subscribers":{"type":"integer","minimum":0.0,"title":"Subscribers","default":0},"stripe_coupon_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Coupon Id"},"list_price_usd":{"type":"number","minimum":0.0,"title":"List Price Usd","default":0},"effective_price_usd":{"type":"number","minimum":0.0,"title":"Effective Price Usd","default":0},"is_current":{"type":"boolean","title":"Is Current","default":false}},"type":"object","required":["id","name","status"],"title":"BrowseBundle"},"BrowseTariff":{"properties":{"id":{"type":"string","minLength":1,"title":"Id"},"name":{"type":"string","minLength":1,"title":"Name"},"product":{"type":"string","minLength":1,"title":"Product"},"monthly_price_usd":{"type":"number","minimum":0.0,"title":"Monthly Price Usd"},"annual_price_usd":{"type":"number","minimum":0.0,"title":"Annual Price Usd"},"status":{"type":"string","minLength":1,"title":"Status"},"subscribers":{"type":"integer","minimum":0.0,"title":"Subscribers","default":0},"updated_at":{"type":"string","title":"Updated At"},"lookup_key":{"type":"string","minLength":1,"title":"Lookup Key"},"feature_bullets":{"items":{"type":"string"},"type":"array","title":"Feature Bullets"},"is_current":{"type":"boolean","title":"Is Current","default":false},"tariff_version_id":{"type":"integer","minimum":1.0,"title":"Tariff Version Id"},"monthly_price_cents":{"type":"integer","minimum":0.0,"title":"Monthly Price Cents"},"annual_price_cents":{"type":"integer","minimum":0.0,"title":"Annual Price Cents","default":0},"stripe_price_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Price Id"},"stripe_annual_price_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Annual Price Id"}},"type":"object","required":["id","name","product","monthly_price_usd","annual_price_usd","status","updated_at","lookup_key","tariff_version_id","monthly_price_cents"],"title":"BrowseTariff","description":"A tariff the client can subscribe to. Superset of the stub\n``TariffSummary`` + Wave 7.2 fields the interval-aware card needs."},"BulkRetryResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"retried_count":{"type":"integer","title":"Retried Count"},"retried_locations":{"items":{"type":"integer"},"type":"array","title":"Retried Locations"},"skipped_count":{"type":"integer","title":"Skipped Count"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","retried_count","retried_locations","skipped_count","message"],"title":"BulkRetryResponse","description":"Response for a bulk retry of failed locations."},"BulkUpdateRequest":{"properties":{"message_ids":{"items":{"type":"integer"},"type":"array","maxItems":100,"minItems":1,"title":"Message Ids"},"action":{"type":"string","title":"Action","description":"Action: mark_read, mark_unread, archive, delete, add_label"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Label name for add_label action"}},"type":"object","required":["message_ids","action"],"title":"BulkUpdateRequest","description":"Bulk update multiple messages."},"BulkUpdateResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"updated_count":{"type":"integer","title":"Updated Count"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","updated_count","message"],"title":"BulkUpdateResponse","description":"Response for bulk update."},"BusiestSlotCell":{"properties":{"day_of_week":{"type":"integer","maximum":6.0,"minimum":0.0,"title":"Day Of Week","description":"0=Sunday .. 6=Saturday"},"hour_of_day":{"type":"integer","maximum":23.0,"minimum":0.0,"title":"Hour Of Day"},"bookings":{"type":"integer","minimum":0.0,"title":"Bookings"}},"additionalProperties":false,"type":"object","required":["day_of_week","hour_of_day","bookings"],"title":"BusiestSlotCell","description":"One cell in the 7×24 day-of-week × hour-of-day heatmap."},"BusiestSlotsResult":{"properties":{"business_id":{"type":"string","maxLength":64,"minLength":1,"title":"Business Id"},"since":{"type":"string","format":"date-time","title":"Since"},"total":{"type":"integer","minimum":0.0,"title":"Total"},"cells":{"items":{"$ref":"#/components/schemas/BusiestSlotCell"},"type":"array","title":"Cells"}},"additionalProperties":false,"type":"object","required":["business_id","since","total"],"title":"BusiestSlotsResult","description":"busiest_slots(business_id, since) response shape.\n\n``cells`` is sparse — hours with zero bookings are omitted, callers fill\nin 0s where needed for a full 7×24 grid."},"BusinessDeleteRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason","description":"Free-text reason recorded in `public.idap_deletions_audit.reason`. Conventionally a short phrase like 'duplicate of <other_uuid>' or 'merged into <other_uuid>'. Not interpreted by the server."}},"type":"object","title":"BusinessDeleteRequest","description":"Optional request body for DELETE /idap/businesses/{id}.\n\nThe body itself is optional — the endpoint treats a missing body as `{}`.\n`reason` is recorded verbatim in `public.idap_deletions_audit.reason`."},"BusinessDeleteResponse":{"properties":{"deleted":{"type":"boolean","title":"Deleted","description":"Always `true` on success. 404 envelope on miss; 409 on bookings conflict.","default":true},"business_id":{"type":"string","title":"Business Id","description":"The UUID of the deleted business (echoed back)."},"cascade":{"additionalProperties":{"type":"integer"},"type":"object","title":"Cascade","description":"Per-table rowcount removed by the explicit cascade. Keys: pins, business_contacts, business_registry, company_registry, phones, domains, contacts, linkedin_profiles, businesses. May omit tables the per-tenant schema didn't have."},"auto_cascaded":{"additionalProperties":{"type":"integer"},"type":"object","title":"Auto Cascaded","description":"Per-table rowcount removed by Postgres ON DELETE CASCADE FK on businesses.id. Keys: booking_flows, services. Counted before the business DELETE so the value is visible to the caller."},"vayapin_pins_remain_external":{"type":"boolean","title":"Vayapin Pins Remain External","description":"True if the deleted business had at least one VayaPin pin row. **cs.vayapin.com pages are permanent per VayaPin §10** — this DELETE only removes our internal record; the external pin URLs remain live. Use `vayapin_pin_data_set_ids_orphaned` to drive any external cleanup workflow you may have.","default":false},"vayapin_pin_data_set_ids_orphaned":{"items":{"type":"string"},"type":"array","title":"Vayapin Pin Data Set Ids Orphaned","description":"Pin data set IDs that were on the deleted business. Empty when `vayapin_pins_remain_external=false`. Also preserved in `public.idap_deletions_audit.vayapin_pin_data_set_ids` for forensic recovery."}},"type":"object","required":["business_id"],"title":"BusinessDeleteResponse","description":"200 response body for a successful DELETE /idap/businesses/{id}."},"CalendarBookingEntry":{"properties":{"booking_id":{"type":"string","format":"uuid","title":"Booking Id"},"resource_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":20,"title":"Resource Ids"},"start_at":{"type":"string","format":"date-time","title":"Start At"},"end_at":{"type":"string","format":"date-time","title":"End At"},"customer_name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Customer Name"},"service_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Service Name"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"}},"additionalProperties":false,"type":"object","required":["booking_id","start_at","end_at","status"],"title":"CalendarBookingEntry","description":"One booking as it appears on a calendar swimlane."},"CalendarResourceEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["staff","room","equipment"],"title":"Kind"},"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name"},"sort_order":{"type":"integer","maximum":10000.0,"minimum":0.0,"title":"Sort Order","default":0}},"additionalProperties":false,"type":"object","required":["id","kind","name"],"title":"CalendarResourceEntry","description":"Column header for the swimlane: one resource + identifying metadata."},"CalendarView":{"properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Date"},"resources":{"items":{"$ref":"#/components/schemas/CalendarResourceEntry"},"type":"array","title":"Resources"},"bookings":{"items":{"$ref":"#/components/schemas/CalendarBookingEntry"},"type":"array","title":"Bookings"}},"additionalProperties":false,"type":"object","required":["date","resources","bookings"],"title":"CalendarView","description":"Response envelope for ``GET /booking/calendar?date=...``."},"CampaignActionResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"},"remaining_locations":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining Locations"}},"type":"object","required":["campaign_id","status","message"],"title":"CampaignActionResponse","description":"Response for campaign actions (stop, continue)."},"CampaignCreate":{"properties":{"search_query":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Search Query","description":"Search query (e.g., 'restaurants', 'plumbers'). Use this instead of 'query'."},"query":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Query","description":"DEPRECATED: Use 'search_query' instead. Kept for backwards compatibility."},"country_code":{"type":"string","maxLength":2,"minLength":2,"title":"Country Code","description":"ISO 2-letter country code"},"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name","description":"Campaign name (auto-generated if not provided)"},"filter":{"anyOf":[{"$ref":"#/components/schemas/CampaignFilterConfig"},{"type":"null"}],"description":"Filter configuration for location selection"},"max_results":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Max Results","description":"Maximum results per location (default: 100)","default":100},"extract_reviews":{"type":"boolean","title":"Extract Reviews","description":"Extract customer reviews from Google Maps","default":false},"extract_photos":{"type":"boolean","title":"Extract Photos","description":"Extract photo URLs from Google Maps","default":false},"lang":{"type":"string","title":"Lang","description":"Language code for Google Maps results (e.g., 'en', 'fr', 'de')","default":"en"},"store_images":{"type":"boolean","title":"Store Images","description":"Store Google Maps images in SeaweedFS","default":true},"validate_phones":{"type":"boolean","title":"Validate Phones","description":"Validate phone numbers using libphonenumber","default":true},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"Enable FuzzIQ deduplication (uses client default if None)"},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return only unique records after deduplication"},"skip_proxy":{"type":"boolean","title":"Skip Proxy","description":"Skip mobile proxy (use direct connection)","default":false},"test":{"type":"boolean","title":"Test","description":"Route jobs to test queue","default":false},"workflow":{"anyOf":[{"$ref":"#/components/schemas/WorkflowConfig"},{"type":"null"}],"description":"Workflow configuration for automatic job chaining (SpiderMaps → SpiderSite → SpiderVerify)"}},"type":"object","required":["country_code"],"title":"CampaignCreate","description":"Schema for creating a new campaign.\n\nv2.15.0: Added workflow config for automatic job chaining:\nSpiderMaps → SpiderSite → SpiderVerify\n\nv2.34.0: Unified payload - same SpiderMaps options as single jobs.\nAll SpiderMaps-specific fields are now flat (not nested).\nChanged 'query' → 'search_query' (query still accepted for backwards compat)."},"CampaignFilterConfig":{"properties":{"mode":{"type":"string","enum":["all","population","cities_only","custom","regions","city"],"title":"Mode","description":"Filter mode: all, population, cities_only, custom, regions, city","default":"all"},"min_population":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Min Population","description":"Minimum population (for population mode)"},"max_population":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Population","description":"Maximum population (for population mode)"},"location_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Location Ids","description":"Specific location IDs (for custom mode)"},"admin_regions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Admin Regions","description":"Specific admin regions / states (for regions mode; scopes a postcode explosion to a state)"},"parent_city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Parent City","description":"Parent city display_name (for city mode / narrowing a postcode explosion to one city)"},"include_postcodes":{"type":"boolean","title":"Include Postcodes","description":"Explode the scope into per-ZIP postcode searches. Default False (cities only). When True, requires a state (admin_regions) or city (parent_city) scope.","default":false}},"type":"object","title":"CampaignFilterConfig","description":"Filter configuration for campaign location selection.\n\nLocation-type semantics (the volume guard — see location_service):\n- ``include_postcodes=False`` (DEFAULT): cities only. Existing ``mode='all'`` /\n  ``population`` / ``regions`` campaigns keep returning cities, so loading ZIP rows\n  does NOT balloon any existing US campaign.\n- ``include_postcodes=True``: explode the chosen scope into postcode (ZIP) rows —\n  one Maps search per ZIP. MUST be scoped to a state (``admin_regions``) or a city\n  (``parent_city``); an unscoped all-country ZIP expansion (~42K) is rejected.\n- ``mode='city'``: a city's ZIPs (requires ``parent_city``); implies postcodes.\n- ``mode='cities_only'``: always cities, regardless of ``include_postcodes``."},"CampaignHealth":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"countryCode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Countrycode"},"done":{"type":"integer","title":"Done","default":0},"total":{"type":"integer","title":"Total","default":0},"pct":{"type":"number","title":"Pct","default":0}},"type":"object","required":["id","name"],"title":"CampaignHealth"},"CampaignJobItem":{"properties":{"id":{"type":"integer","title":"Id","description":"Campaign location ID (campaign_locations.id) - for worker runs lookup"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Job UUID (null if location is pending)"},"location_id":{"type":"integer","title":"Location Id","description":"Location ID associated with this job"},"location_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location Type","description":"Location type (city, region, etc.)"},"search_string":{"type":"string","title":"Search String","description":"Location search string (e.g., 'Paris, France')"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Human-readable location name"},"status":{"type":"string","title":"Status","description":"Campaign location status: pending, submitted, completed, failed, skipped"},"job_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Status","description":"Actual job status from jobs table: queued, processing, completed, failed"},"results_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Results Count","description":"Number of businesses found"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Error message if job failed"},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At","description":"When job was submitted to queue"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When job completed"},"sequence_order":{"type":"integer","title":"Sequence Order","description":"Order in campaign sequence"},"businesses_with_domain":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Businesses With Domain","description":"Businesses with website"},"businesses_with_phone":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Businesses With Phone","description":"Businesses with phone number"},"businesses_with_email":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Businesses With Email","description":"Businesses with email from Google"},"spidersite_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Spidersite Total","description":"SpiderSite jobs for this location"},"spidersite_completed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Spidersite Completed","description":"SpiderSite jobs completed"},"spidersite_failed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Spidersite Failed","description":"SpiderSite jobs failed"},"emails_found":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Emails Found","description":"Emails found from SpiderSite"},"emails_verified":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Emails Verified","description":"Emails verified by SpiderVerify"}},"type":"object","required":["id","location_id","search_string","status","sequence_order"],"title":"CampaignJobItem","description":"Single job item in campaign jobs list (enhanced v2.54.4, v2.55.0)."},"CampaignJobStats":{"properties":{"queued":{"type":"integer","title":"Queued","default":0},"processing":{"type":"integer","title":"Processing","default":0},"completed":{"type":"integer","title":"Completed","default":0},"failed":{"type":"integer","title":"Failed","default":0}},"type":"object","title":"CampaignJobStats","description":"Job statistics for a campaign."},"CampaignJobsListResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"total":{"type":"integer","title":"Total","description":"Total jobs matching filter"},"page":{"type":"integer","title":"Page","description":"Current page number"},"page_size":{"type":"integer","title":"Page Size","description":"Items per page"},"jobs":{"items":{"$ref":"#/components/schemas/CampaignJobItem"},"type":"array","title":"Jobs"},"summary":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Summary","description":"Job counts by status: {pending: N, submitted: N, completed: N, failed: N, skipped: N}"}},"type":"object","required":["campaign_id","total","page","page_size"],"title":"CampaignJobsListResponse","description":"Response for listing jobs in a campaign."},"CampaignListItem":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id","description":"Client ID (for admin views)"},"status":{"type":"string","title":"Status"},"search_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Query","description":"Search query (unified naming v2.34.0)"},"query":{"type":"string","title":"Query","description":"Search query"},"country_code":{"type":"string","title":"Country Code"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"total_locations":{"type":"integer","title":"Total Locations"},"progress":{"allOf":[{"$ref":"#/components/schemas/CampaignProgress"}],"description":"Progress stats"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"is_auto_campaign":{"type":"boolean","title":"Is Auto Campaign","description":"True if auto-created from single job","default":false},"auto_submit_enabled":{"type":"boolean","title":"Auto Submit Enabled","description":"True if auto-submit is enabled","default":false},"has_workflow":{"type":"boolean","title":"Has Workflow","description":"True if workflow orchestration is enabled","default":false},"extract_reviews":{"type":"boolean","title":"Extract Reviews","description":"True if review extraction is enabled","default":false},"extract_photos":{"type":"boolean","title":"Extract Photos","description":"True if photo extraction is enabled","default":false},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Kind","description":"Who triggered: user/api/mcp/cli/system"},"actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id","description":"Stable id of the actor"},"actor_display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Display Name","description":"Human-readable label for Created By column"},"actor_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Avatar Url","description":"Avatar URL (NULL for legacy/system)"}},"type":"object","required":["campaign_id","status","query","country_code","total_locations","created_at"],"title":"CampaignListItem","description":"Campaign item for listing."},"CampaignListResponse":{"properties":{"campaigns":{"items":{"$ref":"#/components/schemas/CampaignListItem"},"type":"array","title":"Campaigns"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["campaigns","total","page","page_size"],"title":"CampaignListResponse","description":"Response for listing campaigns."},"CampaignNextResponse":{"properties":{"has_more":{"type":"boolean","title":"Has More"},"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"current_task":{"anyOf":[{"$ref":"#/components/schemas/CurrentTaskInfo"},{"type":"null"}]},"last_location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Location Id"},"next_location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Location Id"},"progress":{"$ref":"#/components/schemas/CampaignProgress"}},"type":"object","required":["has_more","campaign_id","status","progress"],"title":"CampaignNextResponse","description":"Response from /next endpoint."},"CampaignProgress":{"properties":{"completed":{"type":"integer","title":"Completed","default":0},"failed":{"type":"integer","title":"Failed","default":0},"pending":{"type":"integer","title":"Pending","default":0},"total":{"type":"integer","title":"Total","default":0},"percentage":{"type":"number","title":"Percentage","default":0.0}},"type":"object","title":"CampaignProgress","description":"Campaign progress statistics."},"CampaignResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"search_query":{"type":"string","title":"Search Query","description":"Search query (unified naming v2.34.0)"},"query":{"type":"string","title":"Query","description":"DEPRECATED: Use search_query. Kept for backwards compat."},"country_code":{"type":"string","title":"Country Code"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"total_locations":{"type":"integer","title":"Total Locations"},"last_location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Location Id"},"next_location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Location Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"max_results":{"type":"integer","title":"Max Results","description":"Maximum results per location","default":100},"extract_reviews":{"type":"boolean","title":"Extract Reviews","description":"Extract customer reviews","default":false},"extract_photos":{"type":"boolean","title":"Extract Photos","description":"Extract photo URLs","default":false},"lang":{"type":"string","title":"Lang","description":"Language code for Google Maps","default":"en"},"store_images":{"type":"boolean","title":"Store Images","description":"Store images in SeaweedFS","default":true},"validate_phones":{"type":"boolean","title":"Validate Phones","description":"Validate phone numbers","default":true},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"FuzzIQ deduplication enabled"},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return unique records only"},"skip_proxy":{"type":"boolean","title":"Skip Proxy","description":"Skip mobile proxy","default":false},"test":{"type":"boolean","title":"Test","description":"Route to test queue","default":false},"has_workflow":{"type":"boolean","title":"Has Workflow","description":"Whether workflow orchestration is enabled","default":false},"workflow_config":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Workflow Config","description":"Workflow configuration if enabled"}},"type":"object","required":["campaign_id","status","search_query","query","country_code","total_locations","created_at"],"title":"CampaignResponse","description":"Response after creating a campaign."},"CampaignResultStats":{"properties":{"total_businesses":{"type":"integer","title":"Total Businesses","default":0}},"type":"object","title":"CampaignResultStats","description":"Result statistics for a campaign."},"CampaignRun":{"properties":{"id":{"type":"integer","title":"Id"},"campaign_id":{"type":"string","title":"Campaign Id"},"campaign_type":{"type":"string","title":"Campaign Type","description":"maps_workflow, research, or custom"},"trigger_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trigger Type","description":"location_next, auto_submit, workflow_dispatch"},"location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Location Id"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"inngest_event_id":{"type":"string","title":"Inngest Event Id"},"inngest_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Run Id"},"inngest_function_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Function Id"},"inngest_event_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Event Name"},"status":{"type":"string","title":"Status","description":"queued, running, completed, failed, cancelled"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"steps_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Steps Count"},"result_summary":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result Summary"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","campaign_id","campaign_type","inngest_event_id","status","created_at"],"title":"CampaignRun","description":"Inngest run information for a campaign."},"CampaignRunDetail":{"properties":{"id":{"type":"integer","title":"Id"},"campaign_id":{"type":"string","title":"Campaign Id"},"campaign_type":{"type":"string","title":"Campaign Type","description":"maps_workflow, research, or custom"},"trigger_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trigger Type","description":"location_next, auto_submit, workflow_dispatch"},"location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Location Id"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"inngest_event_id":{"type":"string","title":"Inngest Event Id"},"inngest_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Run Id"},"inngest_function_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Function Id"},"inngest_event_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Event Name"},"status":{"type":"string","title":"Status","description":"queued, running, completed, failed, cancelled"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"steps_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Steps Count"},"result_summary":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result Summary"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"steps":{"items":{"$ref":"#/components/schemas/CampaignRunStep"},"type":"array","title":"Steps"}},"type":"object","required":["id","campaign_id","campaign_type","inngest_event_id","status","created_at"],"title":"CampaignRunDetail","description":"Detailed run information including steps (waterfall data)."},"CampaignRunDetailResponse":{"properties":{"run":{"$ref":"#/components/schemas/CampaignRunDetail"},"waterfall_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Waterfall Data","description":"Pre-computed waterfall data for frontend visualization"}},"type":"object","required":["run"],"title":"CampaignRunDetailResponse","description":"Response for single run detail with steps."},"CampaignRunStep":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"op":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Op"},"status":{"type":"string","title":"Status"},"started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Started At"},"ended_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ended At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"}},"type":"object","required":["id","name","status"],"title":"CampaignRunStep","description":"Step within an Inngest run."},"CampaignRunsResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"runs":{"items":{"$ref":"#/components/schemas/CampaignRun"},"type":"array","title":"Runs"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_more":{"type":"boolean","title":"Has More"}},"type":"object","required":["campaign_id","runs","total","page","page_size","has_more"],"title":"CampaignRunsResponse","description":"Response for campaign runs list."},"CampaignStatusResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"search_query":{"type":"string","title":"Search Query","description":"Search query (unified naming v2.34.0)"},"query":{"type":"string","title":"Query","description":"DEPRECATED: Use search_query. Kept for backwards compat."},"country_code":{"type":"string","title":"Country Code"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"filter_config":{"type":"object","title":"Filter Config"},"last_location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Location Id"},"next_location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Location Id"},"progress":{"$ref":"#/components/schemas/CampaignProgress"},"jobs":{"$ref":"#/components/schemas/CampaignJobStats"},"results":{"$ref":"#/components/schemas/CampaignResultStats"},"max_results":{"type":"integer","title":"Max Results","description":"Maximum results per location","default":100},"extract_reviews":{"type":"boolean","title":"Extract Reviews","description":"Extract customer reviews","default":false},"extract_photos":{"type":"boolean","title":"Extract Photos","description":"Extract photo URLs","default":false},"lang":{"type":"string","title":"Lang","description":"Language code for Google Maps","default":"en"},"store_images":{"type":"boolean","title":"Store Images","description":"Store images in SeaweedFS","default":true},"validate_phones":{"type":"boolean","title":"Validate Phones","description":"Validate phone numbers","default":true},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"FuzzIQ deduplication enabled"},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return unique records only"},"skip_proxy":{"type":"boolean","title":"Skip Proxy","description":"Skip mobile proxy","default":false},"test":{"type":"boolean","title":"Test","description":"Route to test queue","default":false},"has_workflow":{"type":"boolean","title":"Has Workflow","description":"Whether workflow orchestration is enabled","default":false},"workflow_config":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Workflow Config","description":"Workflow configuration"},"workflow_progress":{"anyOf":[{"$ref":"#/components/schemas/WorkflowProgress"},{"type":"null"}],"description":"Workflow progress stats"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["campaign_id","status","search_query","query","country_code","filter_config","progress","jobs","results","created_at","updated_at"],"title":"CampaignStatusResponse","description":"Full campaign status response."},"CampaignUpdate":{"properties":{"search_query":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Search Query","description":"Search query (e.g., 'restaurants', 'plumbers'). Use this instead of 'query'."},"query":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Query","description":"DEPRECATED: Use 'search_query' instead. Kept for backwards compatibility."},"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name","description":"Campaign display name"},"max_results":{"anyOf":[{"type":"integer","maximum":500.0,"minimum":1.0},{"type":"null"}],"title":"Max Results","description":"Maximum results per location"},"extract_reviews":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Extract Reviews","description":"Extract customer reviews from Google Maps"},"extract_photos":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Extract Photos","description":"Extract photo URLs from Google Maps"},"lang":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang","description":"Language code for Google Maps results (e.g., 'en', 'fr', 'de')"},"store_images":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Store Images","description":"Store Google Maps images in SeaweedFS"},"validate_phones":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Validate Phones","description":"Validate phone numbers using libphonenumber"},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"Enable FuzzIQ deduplication"},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return only unique records after deduplication"},"skip_proxy":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Skip Proxy","description":"Skip mobile proxy (use direct connection)"},"test":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Test","description":"Route jobs to test queue"},"workflow":{"anyOf":[{"$ref":"#/components/schemas/WorkflowConfig"},{"type":"null"}],"description":"Workflow configuration updates (merged with existing config)"}},"type":"object","title":"CampaignUpdate","description":"Schema for updating an existing campaign.\n\nv2.44.0: Added campaign update endpoint.\nAll fields are optional - only provided fields are updated.\nWorkflow config is merged with existing settings (not replaced)."},"CanonicalAddRequest":{"properties":{"record":{"allOf":[{"$ref":"#/components/schemas/FuzzIQRecordInput"}],"description":"Record to add"},"record_type":{"allOf":[{"$ref":"#/components/schemas/RecordType"}],"description":"Type of record"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Source of record (manual, import, etc.)","default":"manual"}},"type":"object","required":["record","record_type"],"title":"CanonicalAddRequest","description":"Request to manually add a record to canonical"},"CanonicalAddResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"canonical_id":{"type":"integer","title":"Canonical Id","description":"ID of added record"},"record_hash":{"type":"string","title":"Record Hash","description":"Hash of added record"},"was_duplicate":{"type":"boolean","title":"Was Duplicate","description":"Whether record was a duplicate (updated instead)","default":false}},"type":"object","required":["canonical_id","record_hash"],"title":"CanonicalAddResponse","description":"Response for adding canonical record"},"CanonicalImportRequest":{"properties":{"records":{"items":{"$ref":"#/components/schemas/FuzzIQRecordInput"},"type":"array","maxItems":1000,"minItems":1,"title":"Records","description":"Records to import (max 1000)"},"record_type":{"allOf":[{"$ref":"#/components/schemas/RecordType"}],"description":"Type of records"},"skip_duplicates":{"type":"boolean","title":"Skip Duplicates","description":"Skip duplicate records instead of erroring","default":true}},"type":"object","required":["records","record_type"],"title":"CanonicalImportRequest","description":"Request to bulk import records to canonical"},"CanonicalImportResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"imported_count":{"type":"integer","title":"Imported Count","description":"Number of records imported"},"duplicate_count":{"type":"integer","title":"Duplicate Count","description":"Number of duplicates skipped"},"error_count":{"type":"integer","title":"Error Count","description":"Number of errors","default":0},"errors":{"items":{"type":"string"},"type":"array","title":"Errors","description":"Error messages"}},"type":"object","required":["imported_count","duplicate_count"],"title":"CanonicalImportResponse","description":"Response for bulk import"},"CanonicalListResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"records":{"items":{"$ref":"#/components/schemas/CanonicalRecord"},"type":"array","title":"Records","description":"Canonical records"},"total":{"type":"integer","title":"Total","description":"Total records matching filter"},"limit":{"type":"integer","title":"Limit","description":"Limit used"},"offset":{"type":"integer","title":"Offset","description":"Offset used"}},"type":"object","required":["records","total","limit","offset"],"title":"CanonicalListResponse","description":"Response for canonical record list"},"CanonicalRecord":{"properties":{"id":{"type":"integer","title":"Id"},"record_hash":{"type":"string","title":"Record Hash"},"record_type":{"type":"string","title":"Record Type"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"company_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Domain"},"google_place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Place Id"},"linkedin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin Url"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"source_worker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Worker"},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","record_hash","record_type"],"title":"CanonicalRecord","description":"Canonical record in response"},"CardRelationshipCreate":{"properties":{"parent_id":{"type":"string","format":"uuid","title":"Parent Id"},"relationship_type":{"type":"string","pattern":"^(belongs_to|involves|associated_with)$","title":"Relationship Type"},"role":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Role"},"extra_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Extra Data"}},"type":"object","required":["parent_id","relationship_type"],"title":"CardRelationshipCreate","description":"Create a relationship. child_id is the ``many`` side; parent_id the ``one``."},"CardRelationshipResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"parent_id":{"type":"string","format":"uuid","title":"Parent Id"},"child_id":{"type":"string","format":"uuid","title":"Child Id"},"relationship_type":{"type":"string","title":"Relationship Type"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"extra_data":{"type":"object","title":"Extra Data"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By User Id"},"parent_task_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Task Title"},"parent_board_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Board Type"},"parent_board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Board Id"},"child_task_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Child Task Title"},"child_board_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Child Board Type"},"child_board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Child Board Id"}},"type":"object","required":["id","client_id","parent_id","child_id","relationship_type","created_at"],"title":"CardRelationshipResponse"},"CardRelationshipUpdate":{"properties":{"role":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Role"},"extra_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Extra Data"}},"type":"object","title":"CardRelationshipUpdate"},"CardSearchResult":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"board_type":{"type":"string","title":"Board Type"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"board_name":{"type":"string","title":"Board Name"}},"type":"object","required":["id","title","board_type","board_id","board_name"],"title":"CardSearchResult"},"CategoryCreate":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"slug":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Slug"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"sort_order":{"type":"integer","title":"Sort Order","default":0}},"type":"object","required":["name"],"title":"CategoryCreate","description":"Create a blog category."},"CategoryListResponse":{"properties":{"categories":{"items":{"$ref":"#/components/schemas/PostCategoryResponse"},"type":"array","title":"Categories"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["categories","total"],"title":"CategoryListResponse","description":"List of categories."},"CategoryUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"slug":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Slug"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"}},"type":"object","title":"CategoryUpdate","description":"Update a blog category."},"CdnAllowlistCreate":{"properties":{"key":{"type":"string","maxLength":128,"minLength":1,"title":"Key"},"name":{"type":"string","maxLength":256,"minLength":1,"title":"Name"},"url":{"type":"string","maxLength":2048,"minLength":1,"title":"Url"},"css_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Css Url"},"sri_hash":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Sri Hash"},"css_sri_hash":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Css Sri Hash"},"load_strategy":{"type":"string","pattern":"^(async|defer|blocking)$","title":"Load Strategy","default":"async"},"category":{"type":"string","maxLength":64,"title":"Category","default":"animation"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["key","name","url"],"title":"CdnAllowlistCreate","description":"Create a CDN allowlist entry."},"CdnAllowlistEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"url":{"type":"string","title":"Url"},"css_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css Url"},"sri_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sri Hash"},"css_sri_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css Sri Hash"},"load_strategy":{"type":"string","title":"Load Strategy","default":"async"},"category":{"type":"string","title":"Category","default":"animation"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_active":{"type":"boolean","title":"Is Active","default":true},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","key","name","url","created_at","updated_at"],"title":"CdnAllowlistEntry","description":"A single CDN library in the allowlist."},"CdnAllowlistListResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/CdnAllowlistEntry"},"type":"array","title":"Entries"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["entries","total"],"title":"CdnAllowlistListResponse","description":"List of CDN allowlist entries."},"CdnAllowlistUpdate":{"properties":{"key":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Key"},"name":{"anyOf":[{"type":"string","maxLength":256,"minLength":1},{"type":"null"}],"title":"Name"},"url":{"anyOf":[{"type":"string","maxLength":2048,"minLength":1},{"type":"null"}],"title":"Url"},"css_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Css Url"},"sri_hash":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Sri Hash"},"css_sri_hash":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Css Sri Hash"},"load_strategy":{"anyOf":[{"type":"string","pattern":"^(async|defer|blocking)$"},{"type":"null"}],"title":"Load Strategy"},"category":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Category"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"CdnAllowlistUpdate","description":"Update a CDN allowlist entry."},"ChampBreakdown":{"properties":{"challenges_score":{"type":"integer","title":"Challenges Score","default":0},"challenges_notes":{"type":"string","title":"Challenges Notes","default":""},"authority_score":{"type":"integer","title":"Authority Score","default":0},"authority_notes":{"type":"string","title":"Authority Notes","default":""},"money_score":{"type":"integer","title":"Money Score","default":0},"money_notes":{"type":"string","title":"Money Notes","default":""},"prioritization_score":{"type":"integer","title":"Prioritization Score","default":0},"prioritization_notes":{"type":"string","title":"Prioritization Notes","default":""}},"type":"object","title":"ChampBreakdown","description":"CHAMP framework scoring breakdown - always present even if empty"},"ChangePlanRequest":{"properties":{"from_tariff_version_id":{"type":"integer","minimum":1.0,"title":"From Tariff Version Id"},"to_tariff_version_id":{"type":"integer","minimum":1.0,"title":"To Tariff Version Id"},"billing_interval":{"type":"string","enum":["month","year"],"title":"Billing Interval","default":"month"}},"additionalProperties":false,"type":"object","required":["from_tariff_version_id","to_tariff_version_id"],"title":"ChangePlanRequest","description":"Switch an existing subscription item from one tariff_version to another\n(tier change, interval change, or both). The customer-facing dashboard\nknows the tariff_version it is moving *from*; the service resolves that to\nthe underlying ``client_subscription_items`` row. Mirrors\n``UpgradePreviewRequest`` so the preview and the commit take the same body."},"ChangePlanResponse":{"properties":{"tariff_version_id":{"type":"integer","minimum":1.0,"title":"Tariff Version Id"},"billing_interval":{"type":"string","enum":["month","year"],"title":"Billing Interval"},"status":{"type":"string","enum":["active","trialing","past_due","cancelled","paused"],"title":"Status"},"proration_behavior":{"type":"string","minLength":1,"title":"Proration Behavior"}},"type":"object","required":["tariff_version_id","billing_interval","status","proration_behavior"],"title":"ChangePlanResponse","description":"Confirmation of an applied plan change. The frontend re-fetches ``/me``\nfor the full refreshed card; this payload is the immediate ack."},"ChangelogCreate":{"properties":{"version":{"type":"string","maxLength":50,"minLength":1,"title":"Version"},"title":{"type":"string","maxLength":500,"minLength":1,"title":"Title"},"body":{"type":"object","title":"Body","description":"Tiptap JSON document"}},"type":"object","required":["version","title","body"],"title":"ChangelogCreate","description":"Create changelog entry."},"ChangelogListResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/ChangelogResponse"},"type":"array","title":"Entries"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["entries","total"],"title":"ChangelogListResponse","description":"List of changelog entries."},"ChangelogResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"version":{"type":"string","title":"Version"},"title":{"type":"string","title":"Title"},"body":{"type":"object","title":"Body"},"status":{"$ref":"#/components/schemas/ContentStatus"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","version","title","body","status","created_at","updated_at"],"title":"ChangelogResponse","description":"Changelog entry response."},"ChangelogUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title"},"body":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Body"}},"type":"object","title":"ChangelogUpdate","description":"Update changelog entry."},"CheckBatchRequest":{"properties":{"records":{"items":{"$ref":"#/components/schemas/FuzzIQRecordInput"},"type":"array","maxItems":100,"minItems":1,"title":"Records","description":"Records to check (max 100)"},"record_type":{"allOf":[{"$ref":"#/components/schemas/RecordType"}],"description":"Type of records"},"add_to_canonical":{"type":"boolean","title":"Add To Canonical","description":"Add unique records to canonical","default":true},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id","description":"Campaign ID for scoped dedup"},"threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Threshold","description":"Confidence threshold for ML matching","default":0.5},"idempotency_key":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Idempotency Key","description":"Idempotency key for retry safety"}},"type":"object","required":["records","record_type"],"title":"CheckBatchRequest","description":"Request to check multiple records for duplicates (external/client-facing)","example":{"add_to_canonical":true,"record_type":"business","records":[{"company_name":"McDonald's Paris","google_place_id":"ChIJ123456789","phone":"+33-1-23-45-67-89"},{"company_name":"McDonald's Lyon","google_place_id":"ChIJ987654321","phone":"+33-4-56-78-90-12"}],"source_worker":"spiderMaps"}},"CheckBatchResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"unique":{"items":{"type":"object"},"type":"array","title":"Unique","description":"Unique records"},"duplicates":{"items":{"$ref":"#/components/schemas/DuplicateMatch"},"type":"array","title":"Duplicates","description":"Duplicate records with match info"},"stats":{"allOf":[{"$ref":"#/components/schemas/CheckBatchStats"}],"description":"Check statistics"}},"type":"object","required":["unique","duplicates","stats"],"title":"CheckBatchResponse","description":"Response for batch record check","example":{"duplicates":[{"confidence":1.0,"match_type":"google_place_id","matched_canonical_id":12345,"record":{"company_name":"McDonald's Lyon","google_place_id":"ChIJ987654321"}}],"stats":{"added_to_canonical":1,"duplicate_count":1,"total_checked":2,"unique_count":1},"success":true,"unique":[{"company_name":"McDonald's Paris","google_place_id":"ChIJ123456789"}]}},"CheckBatchStats":{"properties":{"total_checked":{"type":"integer","title":"Total Checked","description":"Total records checked"},"unique_count":{"type":"integer","title":"Unique Count","description":"Number of unique records"},"duplicate_count":{"type":"integer","title":"Duplicate Count","description":"Number of duplicates found"},"added_to_canonical":{"type":"integer","title":"Added To Canonical","description":"Records added to canonical","default":0},"skipped":{"type":"boolean","title":"Skipped","description":"Whether dedup was skipped","default":false},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Reason if skipped"}},"type":"object","required":["total_checked","unique_count","duplicate_count"],"title":"CheckBatchStats","description":"Statistics for batch check"},"CheckSingleRequest":{"properties":{"record":{"allOf":[{"$ref":"#/components/schemas/FuzzIQRecordInput"}],"description":"Record to check"},"record_type":{"allOf":[{"$ref":"#/components/schemas/RecordType"}],"description":"Type of record"},"add_to_canonical":{"type":"boolean","title":"Add To Canonical","description":"Add to canonical if unique","default":true},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id","description":"Campaign ID for scoped dedup"},"threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Threshold","description":"Confidence threshold for ML matching","default":0.5}},"type":"object","required":["record","record_type"],"title":"CheckSingleRequest","description":"Request to check a single record for duplicates (external/client-facing)"},"CheckSingleResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"is_duplicate":{"type":"boolean","title":"Is Duplicate","description":"Whether record is a duplicate"},"confidence":{"type":"number","title":"Confidence","description":"Match confidence (0.0-1.0)","default":0.0},"matched_record_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Matched Record Id","description":"ID of matched canonical record"},"match_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Match Type","description":"Type of match"},"canonical_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Canonical Id","description":"ID if added to canonical"},"added_to_canonical":{"type":"boolean","title":"Added To Canonical","description":"Whether record was added to canonical","default":false},"skipped":{"type":"boolean","title":"Skipped","description":"Whether dedup was skipped","default":false},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Reason if skipped"}},"type":"object","required":["is_duplicate"],"title":"CheckSingleResponse","description":"Response for single record check"},"ChoiceLogprobs":{"properties":{"content":{"anyOf":[{"items":{"$ref":"#/components/schemas/LogprobContent"},"type":"array"},{"type":"null"}],"title":"Content"}},"type":"object","title":"ChoiceLogprobs","description":"Logprobs for a choice."},"ClientCredentialsResponse":{"properties":{"client_id":{"type":"string","title":"Client Id"},"api_key":{"type":"string","title":"Api Key"},"api_secret":{"type":"string","title":"Api Secret"},"token":{"type":"string","title":"Token"},"message":{"type":"string","title":"Message","default":"Credentials regenerated. Save these now - they cannot be retrieved again."}},"type":"object","required":["client_id","api_key","api_secret","token"],"title":"ClientCredentialsResponse","description":"Response containing API credentials. Credentials are shown only once."},"ClientInvoice":{"properties":{"id":{"type":"string","title":"Id"},"number":{"type":"string","title":"Number"},"date":{"type":"string","title":"Date"},"amount_usd":{"type":"number","minimum":0.0,"title":"Amount Usd"},"status":{"type":"string","enum":["paid","failed","open","void"],"title":"Status"},"hosted_invoice_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hosted Invoice Url"},"invoice_pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invoice Pdf Url"}},"type":"object","required":["id","number","date","amount_usd","status"],"title":"ClientInvoice"},"ColumnCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"slug":{"type":"string","maxLength":100,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-_]*$","title":"Slug"},"position":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Position"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"is_done_column":{"type":"boolean","title":"Is Done Column","default":false},"mapped_status":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Mapped Status"}},"type":"object","required":["name","slug"],"title":"ColumnCreate"},"ColumnReorder":{"properties":{"columns":{"items":{"$ref":"#/components/schemas/ColumnReorderItem"},"type":"array","minItems":1,"title":"Columns"}},"type":"object","required":["columns"],"title":"ColumnReorder"},"ColumnReorderItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"position":{"type":"integer","minimum":0.0,"title":"Position"}},"type":"object","required":["id","position"],"title":"ColumnReorderItem"},"ColumnResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"position":{"type":"integer","title":"Position"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"wip_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Wip Limit"},"is_done_column":{"type":"boolean","title":"Is Done Column"},"is_default":{"type":"boolean","title":"Is Default"},"mapped_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mapped Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"task_count":{"type":"integer","title":"Task Count","default":0}},"type":"object","required":["id","client_id","board_id","name","slug","position","is_done_column","is_default","created_at","updated_at"],"title":"ColumnResponse"},"ColumnUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"wip_limit":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Wip Limit"},"is_done_column":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Done Column"},"mapped_status":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Mapped Status"}},"type":"object","title":"ColumnUpdate"},"CommandComplete":{"properties":{"success":{"type":"boolean","title":"Success"},"result":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"}},"type":"object","required":["success"],"title":"CommandComplete","description":"Agent reporting command completion"},"CommandResponse":{"properties":{"command_id":{"type":"string","format":"uuid","title":"Command Id"},"location_id":{"type":"string","format":"uuid","title":"Location Id"},"modem_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Modem Id"},"command_type":{"$ref":"#/components/schemas/CommandType"},"command_payload":{"type":"object","title":"Command Payload"},"priority":{"type":"integer","title":"Priority"},"status":{"$ref":"#/components/schemas/CommandStatus"},"result":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["command_id","location_id","modem_id","command_type","command_payload","priority","status","result","error_message","created_at","started_at","completed_at"],"title":"CommandResponse","description":"Command response"},"CommandStatus":{"type":"string","enum":["pending","processing","completed","failed"],"title":"CommandStatus","description":"Command execution status"},"CommandType":{"type":"string","enum":["rotate_ip","reboot","ussd","sms_send","sms_delete","health_check","speed_test"],"title":"CommandType","description":"Agent command types"},"CompanyDataConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Look up company registry (Companies House, SEC, VIES)","default":true},"country":{"type":"string","title":"Country","description":"Country code or 'auto' to detect","default":"auto"},"include_financials":{"type":"boolean","title":"Include Financials","description":"Include financial data (UK only, ~$0.05)","default":false}},"type":"object","title":"CompanyDataConfig"},"CompanyDataMode":{"type":"string","enum":["search","lookup","vat"],"title":"CompanyDataMode","description":"Operation modes for SpiderCompanyData jobs."},"CompanyInfo-Input":{"properties":{"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry","description":"Business industry"},"key_services":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Key Services","description":"List of key services"},"target_audience":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Audience","description":"Target audience description"},"one_sentence_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"One Sentence Summary","description":"One-sentence business summary"}},"type":"object","title":"CompanyInfo","description":"Company information from SpiderSite AI extraction"},"CompanyInfo-Output":{"properties":{"industry":{"type":"string","title":"Industry","default":""},"key_services":{"items":{"type":"string"},"type":"array","title":"Key Services"},"target_audience":{"type":"string","title":"Target Audience","default":""},"one_sentence_summary":{"type":"string","title":"One Sentence Summary","default":""}},"type":"object","title":"CompanyInfo","description":"Company information - always present even if empty (for Xano field mapping)"},"CompanyIntelBatchRequest":{"properties":{"companies":{"items":{"$ref":"#/components/schemas/CompanyIntelRequest"},"type":"array","maxItems":50,"minItems":1,"title":"Companies","description":"List of companies to research (max 50)"},"profile_mode":{"anyOf":[{"type":"string","pattern":"^(short|full|full_email)$"},{"type":"null"}],"title":"Profile Mode","description":"Employee detail level for all companies: 'short', 'full', 'full_email'","default":"short"},"max_employees":{"anyOf":[{"type":"integer","maximum":2000.0,"minimum":1.0},{"type":"null"}],"title":"Max Employees","description":"Max employees per company (default 20)"},"config":{"allOf":[{"$ref":"#/components/schemas/CompanyIntelConfig"}],"description":"Shared pipeline config (applied to all)"},"test":{"type":"boolean","title":"Test","description":"Route to test queues","default":false}},"type":"object","required":["companies"],"title":"CompanyIntelBatchRequest","description":"Batch company intel request."},"CompanyIntelConfig":{"properties":{"discovery":{"$ref":"#/components/schemas/DiscoveryConfig"},"spidersite":{"$ref":"#/components/schemas/SiteConfig"},"spidercompanydata":{"$ref":"#/components/schemas/CompanyDataConfig"},"spiderverify":{"$ref":"#/components/schemas/VerifyConfig"},"spiderpeople":{"$ref":"#/components/schemas/PeopleConfig"}},"type":"object","title":"CompanyIntelConfig","description":"Pipeline step configuration — toggle steps on/off."},"CompanyIntelRequest":{"properties":{"company_name":{"type":"string","maxLength":255,"minLength":1,"title":"Company Name","description":"Company name to research"},"city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"City","description":"City/location hint"},"country_code":{"anyOf":[{"type":"string","maxLength":2,"minLength":2},{"type":"null"}],"title":"Country Code","description":"ISO 2-letter country code"},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain","description":"Known domain (skips Perplexity discovery)"},"linkedin_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin Url","description":"Known LinkedIn company URL"},"profile_mode":{"anyOf":[{"type":"string","pattern":"^(short|full|full_email)$"},{"type":"null"}],"title":"Profile Mode","description":"Employee detail level: 'short' (name+title), 'full' (+skills/education/experience), 'full_email' (+email discovery)","default":"short"},"max_employees":{"anyOf":[{"type":"integer","maximum":2000.0,"minimum":1.0},{"type":"null"}],"title":"Max Employees","description":"Max employees to extract (default 20)"},"config":{"allOf":[{"$ref":"#/components/schemas/CompanyIntelConfig"}],"description":"Pipeline step configuration"},"test":{"type":"boolean","title":"Test","description":"Route to test queues","default":false}},"type":"object","required":["company_name"],"title":"CompanyIntelRequest","description":"Single company intel request."},"CompanyIntelResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"job_id":{"type":"string","title":"Job Id","description":"Job ID — poll GET /api/v1/jobs/{job_id}/results"},"type":{"type":"string","title":"Type","description":"single or batch","default":"single"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name","description":"Company name (single mode)"},"companies_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Companies Count","description":"Number of companies (batch mode)"},"flow":{"type":"string","title":"Flow","description":"WindMill flow triggered"},"status":{"type":"string","title":"Status","description":"Initial status","default":"processing"}},"type":"object","required":["job_id","flow"],"title":"CompanyIntelResponse","description":"Response after submitting company intel."},"CompanyRegistryData":{"properties":{"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Registry source: us_sec_edgar, uk_companies_house, eu_vies"},"registration_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Registration Number"},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number"},"legal_form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Form"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"incorporation_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Incorporation Date"},"registered_address":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Registered Address"},"sic_codes":{"items":{"type":"string"},"type":"array","title":"Sic Codes"},"officers":{"items":{"type":"object"},"type":"array","title":"Officers"},"financials":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Financials"}},"type":"object","title":"CompanyRegistryData","description":"Registry data from SpiderCompanyData."},"CompanyResult":{"properties":{"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"original_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Url"},"source_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Type"},"source_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Location"},"emails_found":{"items":{"type":"string"},"type":"array","title":"Emails Found"},"emails_verified":{"items":{"type":"object"},"type":"array","title":"Emails Verified"},"phones":{"items":{"type":"string"},"type":"array","title":"Phones"},"addresses":{"items":{"type":"string"},"type":"array","title":"Addresses"},"registry_data":{"anyOf":[{"$ref":"#/components/schemas/CompanyRegistryData"},{"type":"null"}]},"detected_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detected Country"},"country_detection_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Detection Source"},"instagram_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Instagram Data"},"facebook_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Facebook Data"},"linkedin_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Linkedin Data"},"company_info":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Company Info"},"team_info":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Team Info"},"spidersite_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spidersite Status"},"spidercompanydata_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spidercompanydata Status"},"instagram_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram Status"},"facebook_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Facebook Status"},"linkedin_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin Status"},"spiderverify_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spiderverify Status"}},"type":"object","title":"CompanyResult","description":"Result for a single company in research job."},"CompendiumConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Generate markdown compendium (enabled by default). Provides full transparency of scraped content.","default":true},"max_chars":{"type":"integer","maximum":1000000.0,"minimum":1000.0,"title":"Max Chars","description":"Maximum compendium size in characters. Truncates if exceeded.","default":100000},"cleanup_level":{"type":"string","enum":["raw","fit","citations","minimal"],"title":"Cleanup Level","description":"Cleanup level: 'raw' (100% fidelity), 'fit' (remove nav/ads, ~60% size), 'citations' (academic format, ~70%), 'minimal' (main content only, ~30% size, 70% token savings for LLMs)","default":"fit"},"separator":{"type":"string","title":"Separator","description":"Page separator in compendium","default":"\n\n---\n\n"},"include_in_response":{"type":"boolean","title":"Include In Response","description":"Include markdown content in API response. Set false for large files (use download URL instead).","default":true},"remove_duplicates":{"type":"boolean","title":"Remove Duplicates","description":"Smart deduplication: removes repeated headers/footers across pages. Saves 20-40% size.","default":true},"priority_sections":{"items":{"type":"string"},"type":"array","title":"Priority Sections","description":"HTML tags to prioritize when extracting content (minimal mode)","default":["main","article","content"]}},"type":"object","title":"CompendiumConfig","description":"AI Context Engine - Compendium configuration (v2.7.0)\n\nIntelligently generates markdown compendiums optimized for LLM consumption.\nIncludes smart deduplication (20-40% size reduction) and 3-tier storage system.","examples":[{"cleanup_level":"fit","enabled":true,"max_chars":100000,"remove_duplicates":true},{"cleanup_level":"minimal","enabled":true,"include_in_response":true,"max_chars":50000,"remove_duplicates":true},{"cleanup_level":"citations","enabled":true,"max_chars":200000,"remove_duplicates":false}]},"ComponentAgentMeta":{"properties":{"interaction_pattern":{"anyOf":[{"type":"string","enum":["static","click","hover","scroll","timer","form","drag"]},{"type":"null"}],"title":"Interaction Pattern","description":"How the user interacts with this component (or doesn't, for static)."},"trigger_kind":{"anyOf":[{"type":"string","enum":["page-load","scroll-into-view","click","hover","exit-intent","timer-fixed-date","timer-elapsed","form-submit","geo-match","none"]},{"type":"null"}],"title":"Trigger Kind","description":"What triggers this component to fire (for kind ∈ interactive/dynamic). NULL for static."},"placement":{"anyOf":[{"type":"string","enum":["above-fold","below-fold","side-rail","modal","toast","footer","header","any"]},{"type":"null"}],"title":"Placement","description":"Where on a page this component is intended to live. 'any' = no constraint."},"motion_safety":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Motion Safety","description":"TRUE if component honours prefers-reduced-motion. NULL = unknown / not curated yet."},"accessibility_notes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Accessibility Notes","description":"A11y considerations: keyboard nav, screen-reader behaviour, contrast notes."},"conversion_strategy":{"anyOf":[{"type":"string","enum":["primary-cta","secondary-cta","trust","scarcity","social-proof","education","navigation","none"]},{"type":"null"}],"title":"Conversion Strategy","description":"What conversion role this component plays in the funnel."}},"additionalProperties":false,"type":"object","title":"ComponentAgentMeta","description":"Per-component discovery axes.\n\nDistinct from props_schema (which describes the inputs the component\naccepts). agent_meta describes the component's BEHAVIOUR + INTENT for\ndiscovery — what an agent needs to know to pick the right component\nfor a brief, before they look at the props."},"ComponentCategory":{"type":"string","enum":["hero","cta","faq","pricing","features","testimonials","contact_form","footer","header","gallery","stats","animation","booking","custom","content","social-proof","dynamic","extension","popup","urgency","scarcity","capture","cro","trust","conversion","engagement","logos","forms","team","authentication"],"title":"ComponentCategory"},"ComponentCreate":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Slug"},"name":{"type":"string","maxLength":256,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"version":{"type":"string","maxLength":32,"title":"Version","default":"1.0.0"},"category":{"allOf":[{"$ref":"#/components/schemas/ComponentCategory"}],"default":"custom"},"html_template":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Html Template","description":"LiquidJS template string (required for Tiers 1-3)"},"css":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css"},"js":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Js"},"dependencies":{"items":{"type":"string"},"type":"array","title":"Dependencies","description":"CDN allowlist keys (e.g. ['gsap', 'gsap/ScrollTrigger'])"},"props_schema":{"type":"object","title":"Props Schema","description":"JSON Schema for accepted props"},"default_props":{"type":"object","title":"Default Props","description":"Fallback prop values"},"thumbnail_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail Url"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"is_global":{"type":"boolean","title":"Is Global","default":false},"framework":{"anyOf":[{"type":"string","pattern":"^(react|vue|svelte)$"},{"type":"null"}],"title":"Framework","description":"Framework for Tier 4 app components"},"source_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Code","description":"Framework source code (JSX/Vue SFC/Svelte)"},"auto_extract_css":{"type":"boolean","title":"Auto Extract Css","description":"Extract inline <style> blocks from html_template into the css field before validation (for Tilda/Webflow imports).","default":false},"preview_thumbnail_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Thumbnail Url","description":"Marketplace gallery preview image URL (PNG/GIF/MP4 in R2)."},"replication_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Replication Prompt","description":"Optional text prompt clients can copy to feed their own LLM to recreate this component."},"marketplace_category":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Marketplace Category","description":"Marketplace category slug — drives the per-type sidebar grouping."},"marketplace_featured":{"type":"boolean","title":"Marketplace Featured","description":"Surface this component on the marketplace landing page.","default":false},"marketplace_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Marketplace Description","description":"Description shown on marketplace cards (defaults to `description` if unset)."},"kind":{"anyOf":[{"$ref":"#/components/schemas/ComponentKind"},{"type":"null"}],"description":"4-class behavioural taxonomy (Phase A). Drives editor + renderer + MCP surface."},"block_type":{"anyOf":[{"$ref":"#/components/schemas/DynamicBlockType"},{"type":"null"}],"description":"Generic block shape (only set when kind='dynamic')."},"js_runtime":{"anyOf":[{"$ref":"#/components/schemas/JsRuntime"},{"type":"null"}],"description":"JS runtime pattern; required for non-static kinds."},"layouts":{"anyOf":[{"items":{"$ref":"#/components/schemas/Layout"},"type":"array","maxItems":12},{"type":"null"}],"title":"Layouts","description":"Renderer layout variants (Phase A). Picks via block.layout."},"sources":{"anyOf":[{"items":{"$ref":"#/components/schemas/SourceBinding"},"type":"array","maxItems":8},{"type":"null"}],"title":"Sources","description":"Default source bindings (required non-empty when kind='dynamic')."},"extension_spec":{"anyOf":[{"$ref":"#/components/schemas/ExtensionSpec"},{"type":"null"}],"description":"Per-extension config (required when kind='extension', forbidden otherwise)."},"mood":{"anyOf":[{"items":{"$ref":"#/components/schemas/Mood"},"type":"array"},{"type":"null"}],"title":"Mood"},"palette":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":12},{"type":"null"}],"title":"Palette"},"brand_fit_tags":{"anyOf":[{"items":{"$ref":"#/components/schemas/BrandFit"},"type":"array"},{"type":"null"}],"title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"$ref":"#/components/schemas/SceneType"},{"type":"null"}]},"agent_meta":{"anyOf":[{"$ref":"#/components/schemas/ComponentAgentMeta"},{"type":"null"}]},"authoring_hints":{"anyOf":[{"$ref":"#/components/schemas/AuthoringHints"},{"type":"null"}],"description":"Component-author-written rules: preferred_path, common_mistakes, must_set, must_not_set."}},"type":"object","required":["slug","name"],"title":"ComponentCreate","description":"Create a new component."},"ComponentKind":{"type":"string","enum":["static","interactive","dynamic","extension"],"title":"ComponentKind","description":"Behavioural class of a marketplace component.\n\nstatic       — props in, HTML out. No JS, no data fetch, no secrets.\ninteractive  — props in + JS for client-side behaviour. No data fetch, no secrets.\ndynamic      — props in + data fetch (sources). May or may not have JS.\nextension    — renderer/worker hook with secrets. Per-tenant route registration may be required.\n\nDB column: content_components.kind (VARCHAR 16). Allowed values enforced by\nchk_components_kind constraint AND by this enum at the Pydantic layer."},"ComponentListResponse":{"properties":{"components":{"items":{"$ref":"#/components/schemas/ComponentResponse"},"type":"array","title":"Components"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["components","total"],"title":"ComponentListResponse","description":"Paginated component list."},"ComponentPreviewRequest":{"properties":{"props":{"type":"object","title":"Props","description":"Props to render with"},"viewport":{"type":"string","enum":["desktop","mobile","tablet"],"title":"Viewport","description":"Intended viewport size hint (informational only for v1)","default":"desktop"}},"type":"object","title":"ComponentPreviewRequest","description":"Body for POST /components/{id}/preview."},"ComponentPreviewResponse":{"properties":{"component_id":{"type":"string","title":"Component Id"},"slug":{"type":"string","title":"Slug"},"version":{"type":"string","title":"Version"},"html":{"type":"string","title":"Html","description":"Shadow-DOM-wrapped HTML ready to inject into an iframe body"},"css":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css"},"js":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Js"},"custom_element_tag":{"type":"string","title":"Custom Element Tag","description":"E.g. 'spideriq-cmp' for Tiers 1-3, 'spideriq-app-{slug}' for Tier 4"},"merged_props":{"type":"object","title":"Merged Props","description":"default_props merged under the provided props"},"framework":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Framework"},"bundle_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bundle Url"}},"type":"object","required":["component_id","slug","version","html","custom_element_tag","merged_props"],"title":"ComponentPreviewResponse","description":"Pieces required to render a single component in isolation.\n\nThe dashboard uses these to populate an iframe's srcdoc with a\nDeclarative Shadow DOM `<spideriq-cmp>` wrapper. No server-side edge\ndeploy is involved — this is the \"quick check before deploy\" path\nBrokerZ asked for."},"ComponentResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"slug":{"type":"string","title":"Slug"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"version":{"type":"string","title":"Version"},"category":{"$ref":"#/components/schemas/ComponentCategory"},"html_template":{"type":"string","title":"Html Template"},"css":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css"},"js":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Js"},"dependencies":{"items":{"type":"string"},"type":"array","title":"Dependencies"},"props_schema":{"type":"object","title":"Props Schema"},"default_props":{"type":"object","title":"Default Props"},"thumbnail_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail Url"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"is_global":{"type":"boolean","title":"Is Global"},"status":{"$ref":"#/components/schemas/ComponentStatus"},"framework":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Framework"},"source_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Code"},"bundle_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bundle Url"},"build_status":{"type":"string","title":"Build Status","default":"none"},"build_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Build Error"},"preview_thumbnail_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Thumbnail Url"},"replication_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replication Prompt"},"marketplace_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Marketplace Category"},"marketplace_featured":{"type":"boolean","title":"Marketplace Featured","default":false},"marketplace_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Marketplace Description"},"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"},"block_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Block Type"},"js_runtime":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Js Runtime"},"layouts":{"items":{"type":"object"},"type":"array","title":"Layouts"},"sources":{"items":{"type":"object"},"type":"array","title":"Sources"},"extension_spec":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Extension Spec"},"mood":{"items":{"type":"string"},"type":"array","title":"Mood"},"palette":{"items":{"type":"string"},"type":"array","title":"Palette"},"brand_fit_tags":{"items":{"type":"string"},"type":"array","title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scene Type"},"agent_meta":{"type":"object","title":"Agent Meta"},"authoring_hints":{"type":"object","title":"Authoring Hints"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"warnings":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Warnings"}},"additionalProperties":true,"type":"object","required":["id","slug","name","version","category","html_template","props_schema","default_props","tags","is_global","status","created_at","updated_at"],"title":"ComponentResponse","description":"Component response."},"ComponentRollbackRequest":{"properties":{"target_version":{"type":"string","title":"Target Version","description":"The version to restore (e.g. '1.0.3'). Must exist for this component."},"bump":{"type":"string","enum":["patch","minor","major"],"title":"Bump","description":"How to bump the NEW version (which gets the old content). Default patch.","default":"patch"},"pages":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Pages","description":"Limit repoint to these page slugs. Omit for all."},"dry_run":{"type":"boolean","title":"Dry Run","default":false},"confirm_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token"}},"type":"object","required":["target_version"],"title":"ComponentRollbackRequest","description":"Inputs for `POST /{slug}/rollback`."},"ComponentStatus":{"type":"string","enum":["draft","published","archived"],"title":"ComponentStatus"},"ComponentUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":256,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"category":{"anyOf":[{"$ref":"#/components/schemas/ComponentCategory"},{"type":"null"}]},"html_template":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Html Template"},"css":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css"},"js":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Js"},"dependencies":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Dependencies"},"props_schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Props Schema"},"default_props":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Default Props"},"thumbnail_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail Url"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"framework":{"anyOf":[{"type":"string","pattern":"^(react|vue|svelte)$"},{"type":"null"}],"title":"Framework"},"source_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Code"},"auto_extract_css":{"type":"boolean","title":"Auto Extract Css","description":"Extract inline <style> blocks from html_template into the css field before validation.","default":false},"preview_thumbnail_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Thumbnail Url"},"replication_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Replication Prompt"},"marketplace_category":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Marketplace Category"},"marketplace_featured":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketplace Featured"},"marketplace_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Marketplace Description"},"kind":{"anyOf":[{"$ref":"#/components/schemas/ComponentKind"},{"type":"null"}]},"block_type":{"anyOf":[{"$ref":"#/components/schemas/DynamicBlockType"},{"type":"null"}]},"js_runtime":{"anyOf":[{"$ref":"#/components/schemas/JsRuntime"},{"type":"null"}]},"layouts":{"anyOf":[{"items":{"$ref":"#/components/schemas/Layout"},"type":"array","maxItems":12},{"type":"null"}],"title":"Layouts"},"sources":{"anyOf":[{"items":{"$ref":"#/components/schemas/SourceBinding"},"type":"array","maxItems":8},{"type":"null"}],"title":"Sources"},"extension_spec":{"anyOf":[{"$ref":"#/components/schemas/ExtensionSpec"},{"type":"null"}]},"mood":{"anyOf":[{"items":{"$ref":"#/components/schemas/Mood"},"type":"array"},{"type":"null"}],"title":"Mood"},"palette":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":12},{"type":"null"}],"title":"Palette"},"brand_fit_tags":{"anyOf":[{"items":{"$ref":"#/components/schemas/BrandFit"},"type":"array"},{"type":"null"}],"title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"$ref":"#/components/schemas/SceneType"},{"type":"null"}]},"agent_meta":{"anyOf":[{"$ref":"#/components/schemas/ComponentAgentMeta"},{"type":"null"}]},"authoring_hints":{"anyOf":[{"$ref":"#/components/schemas/AuthoringHints"},{"type":"null"}],"description":"Update component-author-written rules surfaced via the _rules envelope."}},"type":"object","title":"ComponentUpdate","description":"Update a component."},"ComponentUpdateAndPropagateRequest":{"properties":{"html_template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Html Template","description":"New HTML template. Inline <style> tags are rejected — put CSS in the `css` field."},"css":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Css","description":"New CSS (injected via Declarative Shadow DOM at render time)."},"js":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Js","description":"New JS (Tier 2 scoped hydration)."},"props_schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Props Schema","description":"New JSON Schema for props validation."},"default_props":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Default Props","description":"New default prop values."},"dependencies":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Dependencies","description":"CDN library dependency keys (Tier 3). Validated against allowlist."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Update display name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Update description."},"bump":{"type":"string","enum":["patch","minor","major"],"title":"Bump","description":"How to bump semver from the current version (default patch).","default":"patch"},"pages":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Pages","description":"Limit propagation to these page slugs. Omit to propagate to every consuming page."},"dry_run":{"type":"boolean","title":"Dry Run","description":"Preview mode — returns affected_pages + confirm_token without mutating.","default":false},"confirm_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm Token","description":"Token from a prior dry_run. Consume to perform the mutation."}},"type":"object","title":"ComponentUpdateAndPropagateRequest","description":"Inputs for `POST /{slug}/update-and-propagate`.\n\nAt least one content field (html_template / css / js / props_schema / default_props /\ndependencies / name / description) must be provided."},"ConnectionResponse":{"properties":{"id":{"type":"integer","title":"Id"},"integration_id":{"type":"integer","title":"Integration Id"},"brand_id":{"type":"integer","title":"Brand Id"},"provider":{"type":"string","title":"Provider"},"workspace_id":{"type":"string","title":"Workspace Id"},"warmup_tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warmup Tag"},"lemwarm_domains":{"items":{},"type":"array","title":"Lemwarm Domains"},"is_active":{"type":"boolean","title":"Is Active"},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Sync At"},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error"},"webhook_secret":{"type":"string","title":"Webhook Secret"},"smartlead_client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Smartlead Client Id"},"max_active_leads":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Active Leads"},"provider_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider Label"},"key_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Label"}},"type":"object","required":["id","integration_id","brand_id","provider","workspace_id","warmup_tag","lemwarm_domains","is_active","last_sync_at","last_error","webhook_secret"],"title":"ConnectionResponse"},"ConnectionUpdate":{"properties":{"workspace_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Workspace Id"},"warmup_tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warmup Tag"},"lemwarm_domains":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Lemwarm Domains"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"smartlead_client_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Smartlead Client Id"},"max_active_leads":{"anyOf":[{"type":"integer","maximum":100000000.0,"minimum":0.0},{"type":"null"}],"title":"Max Active Leads"}},"type":"object","title":"ConnectionUpdate"},"ConnectorInfo":{"properties":{"provider":{"type":"string","title":"Provider"},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connected At"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["provider"],"title":"ConnectorInfo","description":"OAuth connector info."},"ContentBlock":{"properties":{"id":{"type":"string","title":"Id","description":"Unique block ID (UUID)"},"type":{"allOf":[{"$ref":"#/components/schemas/BlockType"}],"description":"Block type"},"data":{"type":"object","title":"Data","description":"Block-specific data"},"component_slug":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Component Slug","description":"Component slug to render this block with Shadow DOM isolation"},"component_version":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Component Version","description":"Pinned component version (omit for latest published)"},"props":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Props","description":"Props passed to the component template"},"layout":{"anyOf":[{"type":"string","maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},{"type":"null"}],"title":"Layout","description":"Layout id from component.layouts[].id. NULL = use component default."},"data_binding":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Data Binding","description":"Per-block source/filter/sort/limit override (DataBinding shape from app.schemas.content_components). Required for kind='dynamic' blocks unless component default_filter covers the use case."}},"type":"object","required":["id","type"],"title":"ContentBlock","description":"A single content block in a page."},"ContentPart":{"properties":{"type":{"type":"string","enum":["text","image_url"],"title":"Type"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"image_url":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Image Url"}},"type":"object","required":["type"],"title":"ContentPart","description":"Part of a multi-modal message content."},"ContentSettingsExtensions":{"properties":{"sitemap":{"anyOf":[{"$ref":"#/components/schemas/SitemapExtensionConfig"},{"type":"null"}]},"robots":{"anyOf":[{"$ref":"#/components/schemas/RobotsExtensionConfig"},{"type":"null"}]},"rss":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Rss"},"atom":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Atom"},"feed_json":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Feed Json"},"opensearch":{"anyOf":[{"$ref":"#/components/schemas/OpenSearchExtensionConfig"},{"type":"null"}]},"llms_txt":{"anyOf":[{"$ref":"#/components/schemas/LlmsTxtExtensionConfig"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"ContentSettingsExtensions","description":"Wave 6 per-tenant extension config bag.\n\nEach W6 PR appends its own optional field here. Every Optional field\ndefaults to None so partial configs round-trip cleanly."},"ContentSettingsResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"site_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Site Name"},"site_tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Site Tagline"},"favicon_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Favicon Url"},"primary_color":{"type":"string","title":"Primary Color","default":"#eebf01"},"surface_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface Color"},"surface_elevated_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface Elevated Color"},"subtle_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtle Color"},"body_text_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Text Color"},"heading_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Heading Color"},"logo_dark_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Dark Url"},"logo_light_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Light Url"},"copyright_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Copyright Text"},"social_links":{"additionalProperties":{"type":"string"},"type":"object","title":"Social Links","default":{}},"google_analytics_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Analytics Id"},"plausible_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plausible Domain"},"default_meta_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Meta Title"},"default_meta_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Meta Description"},"default_og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Og Image Url"},"default_seo_title_suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Seo Title Suffix"},"custom_head_scripts":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Head Scripts"},"custom_footer_scripts":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Footer Scripts"},"posthog_project_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Posthog Project Key"},"posthog_api_host":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Posthog Api Host"},"google_site_verification":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Site Verification"},"extensions":{"type":"object","title":"Extensions"},"geo_toggle_enabled":{"type":"boolean","title":"Geo Toggle Enabled","default":false},"map_providers":{"type":"object","title":"Map Providers"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"pending_deploy":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Pending Deploy"},"pending_deploy_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pending Deploy Message"},"last_deployed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Deployed At"}},"type":"object","required":["id","created_at","updated_at"],"title":"ContentSettingsResponse","description":"Content settings response."},"ContentSettingsUpdate":{"properties":{"site_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Site Name"},"site_tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Site Tagline"},"favicon_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Favicon Url"},"primary_color":{"anyOf":[{"type":"string","maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Primary Color"},"surface_color":{"anyOf":[{"type":"string","maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Surface Color"},"surface_elevated_color":{"anyOf":[{"type":"string","maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Surface Elevated Color"},"subtle_color":{"anyOf":[{"type":"string","maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Subtle Color"},"body_text_color":{"anyOf":[{"type":"string","maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Body Text Color"},"heading_color":{"anyOf":[{"type":"string","maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Heading Color"},"logo_dark_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Dark Url"},"logo_light_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Light Url"},"copyright_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Copyright Text"},"social_links":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Social Links"},"google_analytics_id":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Google Analytics Id"},"plausible_domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Plausible Domain"},"default_meta_title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Default Meta Title"},"default_meta_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Default Meta Description"},"default_og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Og Image Url"},"default_seo_title_suffix":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Default Seo Title Suffix"},"custom_head_scripts":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Head Scripts"},"custom_footer_scripts":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Footer Scripts"},"posthog_project_key":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Posthog Project Key"},"posthog_api_host":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Posthog Api Host"},"google_site_verification":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Google Site Verification"},"extensions":{"anyOf":[{"$ref":"#/components/schemas/ContentSettingsExtensions"},{"type":"null"}]},"geo_toggle_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Geo Toggle Enabled"},"map_providers":{"anyOf":[{"$ref":"#/components/schemas/MapProvidersConfig"},{"type":"null"}]}},"additionalProperties":false,"type":"object","title":"ContentSettingsUpdate","description":"Update content settings.\n\nBUG-2 (2026-05-22) — `model_config['extra'] = 'forbid'` so unknown field\nnames surface a 422 envelope at the API boundary instead of being silently\ndropped. Pre-fix behavior: a PATCH with `tagline` (vs the schema's\n`site_tagline`) returned 200 OK with nothing persisted. Now it returns\n422 \"Extra inputs are not permitted\" with the offending key, which the\nerror envelope helper at app/services/error_envelope.py converts into a\nstructured response with `suggested_action`. Adding a new tenant-facing\nsetting now requires three coordinated changes — schema field +\nservice-layer allowlist + DB column — and the API surfaces the gap loudly\nif any layer is missing."},"ContentStatus":{"type":"string","enum":["draft","pending_review","published","scheduled","archived"],"title":"ContentStatus"},"ConversionFunnelResult":{"properties":{"flow_id":{"type":"string","maxLength":64,"minLength":1,"title":"Flow Id"},"since":{"type":"string","format":"date-time","title":"Since"},"renders":{"type":"integer","minimum":0.0,"title":"Renders","description":"Count of render events."},"submits":{"type":"integer","minimum":0.0,"title":"Submits","description":"Count of bookings created in window."},"confirmed":{"type":"integer","minimum":0.0,"title":"Confirmed","description":"Count of confirmed bookings."},"conversion_rate":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Conversion Rate","description":"confirmed / renders, rounded to 4 decimals. 0 if renders==0."}},"additionalProperties":false,"type":"object","required":["flow_id","since","renders","submits","confirmed","conversion_rate"],"title":"ConversionFunnelResult","description":"conversion_funnel(flow_id, since) response shape.\n\n``conversion_rate = confirmed / renders`` (0 if renders == 0)."},"CountryListItem":{"properties":{"country_code":{"type":"string","title":"Country Code"},"country_name":{"type":"string","title":"Country Name"},"location_count":{"type":"integer","title":"Location Count"}},"type":"object","required":["country_code","country_name","location_count"],"title":"CountryListItem","description":"Country item for listing available countries."},"CountryListResponse":{"properties":{"countries":{"items":{"$ref":"#/components/schemas/CountryListItem"},"type":"array","title":"Countries"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["countries","total"],"title":"CountryListResponse","description":"Response for listing available countries."},"CountryStats":{"properties":{"country_code":{"type":"string","title":"Country Code"},"country_name":{"type":"string","title":"Country Name"},"total_locations":{"type":"integer","title":"Total Locations"},"cities":{"type":"integer","title":"Cities"},"postcodes":{"type":"integer","title":"Postcodes"},"needs_postcodes":{"type":"integer","title":"Needs Postcodes"}},"type":"object","required":["country_code","country_name","total_locations","cities","postcodes","needs_postcodes"],"title":"CountryStats","description":"Statistics for a single country."},"CoverageGapAsset":{"properties":{"asset_type":{"type":"string","maxLength":32,"minLength":1,"title":"Asset Type"},"slug":{"type":"string","maxLength":128,"minLength":1,"title":"Slug"},"name":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Name"},"missing":{"items":{"type":"string"},"type":"array","maxItems":6,"title":"Missing"}},"type":"object","required":["asset_type","slug"],"title":"CoverageGapAsset","description":"A single asset row missing one or more axes — used by the\n`low_coverage_assets` array on `CoverageResponse`."},"CoverageResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/CoverageRow"},"type":"array","maxItems":8,"title":"Rows"},"low_coverage_assets":{"items":{"$ref":"#/components/schemas/CoverageGapAsset"},"type":"array","maxItems":200,"title":"Low Coverage Assets"},"generated_at":{"type":"string","maxLength":64,"minLength":10,"title":"Generated At"}},"type":"object","required":["rows","generated_at"],"title":"CoverageResponse","description":"`GET /api/v1/dashboard/content/marketplace/coverage` envelope.\n\nIncludes per-table summary rows + a sample of low-coverage asset slugs\nso the orchestrator can target slice 4's bulk run, and the dashboard\ncan drive a \"fill the bank\" badge."},"CoverageRow":{"properties":{"asset_type":{"type":"string","maxLength":32,"minLength":1,"title":"Asset Type"},"total":{"type":"integer","minimum":0.0,"title":"Total"},"with_mood":{"type":"integer","minimum":0.0,"title":"With Mood"},"with_palette":{"type":"integer","minimum":0.0,"title":"With Palette"},"with_brand_fit":{"type":"integer","minimum":0.0,"title":"With Brand Fit"},"with_scene_type":{"type":"integer","minimum":0.0,"title":"With Scene Type"},"with_agent_meta":{"type":"integer","minimum":0.0,"title":"With Agent Meta"},"pct_mood":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Pct Mood"},"pct_palette":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Pct Palette"},"pct_brand_fit":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Pct Brand Fit"},"pct_scene_type":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Pct Scene Type"},"pct_agent_meta":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Pct Agent Meta"}},"type":"object","required":["asset_type","total","with_mood","with_palette","with_brand_fit","with_scene_type","with_agent_meta"],"title":"CoverageRow","description":"Per-table coverage snapshot from `vw_marketplace_v2_coverage`.\n\nCounts use `array_length(col, 1) > 0` to distinguish empty `text[]`\nfrom genuine values — Postgres `'{}'` is NOT NULL but doesn't carry\nany vocabulary. Percentages are 0.0–100.0 floats rounded to 1 dp."},"CreateAgentKeyRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Display name for the agent"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"photo":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Photo","description":"Avatar URL (from /agent-keys/avatar)"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models","description":"Provider names and/or task aliases the key may invoke (null = all)"},"free_models_only":{"type":"boolean","title":"Free Models Only","description":"If true, only free-tier providers may be used","default":false},"ttl_hours":{"type":"integer","maximum":2160.0,"minimum":1.0,"title":"Ttl Hours","description":"Token lifetime (1h–90d, default 30d)","default":720},"monthly_budget_usd":{"anyOf":[{"type":"number","maximum":100000.0,"minimum":0.0},{"type":"null"}],"title":"Monthly Budget Usd"},"soft_budget_usd":{"anyOf":[{"type":"number","maximum":100000.0,"minimum":0.0},{"type":"null"}],"title":"Soft Budget Usd"},"rate_limit_rpm":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Rate Limit Rpm"},"rate_limit_rpd":{"anyOf":[{"type":"integer","maximum":1000000.0,"minimum":0.0},{"type":"null"}],"title":"Rate Limit Rpd"},"owner_email":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"title":"Owner Email"}},"type":"object","required":["name"],"title":"CreateAgentKeyRequest"},"CreateAgentKeyResponse":{"properties":{"id":{"type":"string","title":"Id"},"token":{"type":"string","title":"Token","description":"The plaintext PAT — shown ONCE. Caller MUST persist it."},"token_prefix":{"type":"string","title":"Token Prefix"},"name":{"type":"string","title":"Name"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"free_models_only":{"type":"boolean","title":"Free Models Only"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"client_id":{"type":"string","title":"Client Id"}},"type":"object","required":["id","token","token_prefix","name","scopes","allowed_models","free_models_only","expires_at","created_at","client_id"],"title":"CreateAgentKeyResponse","description":"Returns the full token exactly once."},"CrmHealthResponse":{"properties":{"status":{"type":"string","title":"Status","default":"ok"},"rls_enabled":{"type":"boolean","title":"Rls Enabled"},"roles":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Roles"},"tables":{"items":{"type":"string"},"type":"array","title":"Tables"}},"type":"object","required":["rls_enabled","roles"],"title":"CrmHealthResponse"},"CrmReplayResponse":{"properties":{"submission_id":{"type":"string","format":"uuid","title":"Submission Id"},"crm_status":{"type":"string","maxLength":32,"title":"Crm Status"},"resources_written":{"items":{"type":"string"},"type":"array","title":"Resources Written"},"errors":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Errors"}},"type":"object","required":["submission_id","crm_status"],"title":"CrmReplayResponse","description":"Result of a per-row CRM dual-write replay (SpiderFlow P1.W10.3).\n\nMirrors :class:`services.crm_write_service.CrmWriteOutcome` — ``crm_status``\nis the freshly re-stamped ``public.results.crm_status`` value."},"CurrentTaskInfo":{"properties":{"job_id":{"type":"string","format":"uuid","title":"Job Id"},"location_id":{"type":"integer","title":"Location Id"},"search_string":{"type":"string","title":"Search String"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"location_type":{"type":"string","title":"Location Type"},"status":{"type":"string","title":"Status"}},"type":"object","required":["job_id","location_id","search_string","location_type","status"],"title":"CurrentTaskInfo","description":"Information about the current/just-submitted task."},"CustomAIPromptConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable custom AI analysis. Requires compendium.enabled=true.","default":false},"system_prompt":{"type":"string","title":"System Prompt","description":"System message for AI (e.g., 'You are a cybersecurity analyst')","default":""},"user_prompt":{"type":"string","title":"User Prompt","description":"Analysis task for AI (e.g., 'Extract security certifications mentioned')","default":""},"json_schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Schema","description":"Expected JSON output schema. AI will try to match this structure."},"output_field_name":{"type":"string","title":"Output Field Name","description":"Field name in response where custom analysis will be stored","default":"custom_analysis"},"model":{"type":"string","title":"Model","description":"Model to use for AI analysis. Pass a SpiderGate task alias (spideriq/research, spideriq/lead-analysis, spideriq/extraction, spideriq/fast, etc.) — the gateway resolves the actual provider and tracks per-brand usage. Direct OpenRouter model names work only when the worker is rolled back to USE_SPIDERGATE=false.","default":"spideriq/research"},"temperature":{"type":"number","maximum":2.0,"minimum":0.0,"title":"Temperature","description":"Sampling temperature (0.0-2.0). Lower = more deterministic, higher = more creative. Default: 0.1","default":0.1},"max_tokens":{"type":"integer","maximum":16000.0,"minimum":100.0,"title":"Max Tokens","description":"Maximum tokens in AI response (100-16000). Default: 4000","default":4000}},"type":"object","title":"CustomAIPromptConfig","description":"Custom AI Prompt Configuration (v2.10.0)\n\nAllows clients to send custom system/user prompts that are applied\nto the compendium (markdown) to extract custom data as JSON.\n\nAlways used in combination with compendium - the AI analyzes the\nscraped website content and returns structured JSON based on your prompt.\n\nv2.10.0: Added model selection, temperature, and max_tokens configuration.\nWhen AI features (extract_team, extract_company_info, extract_pain_points)\nare also enabled, all analyses are combined into a SINGLE efficient API call.","examples":[{"enabled":true,"json_schema":{"compliance_frameworks":["GDPR","HIPAA"],"data_privacy_summary":"string","security_certifications":["SOC 2","ISO 27001"]},"max_tokens":4000,"model":"google/gemini-2.0-flash-exp:free","system_prompt":"You are a cybersecurity analyst specializing in SaaS platforms.","temperature":0.1,"user_prompt":"Analyze the website and extract: 1) Security certifications, 2) Compliance frameworks, 3) Data privacy practices. Return as JSON."},{"enabled":true,"max_tokens":8000,"model":"anthropic/claude-3.5-sonnet","output_field_name":"competitive_intel","system_prompt":"You are a competitive intelligence analyst.","temperature":0.3,"user_prompt":"Extract pricing tiers, key features, and target customer segments from this website."}]},"CustomerBookingHistoryEntry":{"properties":{"booking_id":{"type":"string","format":"uuid","title":"Booking Id"},"service_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Service Name"},"slot_start":{"type":"string","format":"date-time","title":"Slot Start"},"slot_end":{"type":"string","format":"date-time","title":"Slot End"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"}},"additionalProperties":false,"type":"object","required":["booking_id","slot_start","slot_end","status"],"title":"CustomerBookingHistoryEntry","description":"Per-booking row in the customer profile page."},"CustomerList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CustomerSummary"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Next Cursor"}},"additionalProperties":false,"type":"object","required":["items"],"title":"CustomerList"},"CustomerProfile":{"properties":{"id":{"type":"string","maxLength":128,"minLength":1,"title":"Id"},"name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string","maxLength":320,"format":"email"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Phone"},"booking_count":{"type":"integer","maximum":1000000.0,"minimum":0.0,"title":"Booking Count"},"total_spent_cents":{"type":"integer","maximum":100000000000.0,"minimum":0.0,"title":"Total Spent Cents","default":0},"first_booking_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Booking At"},"last_booking_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Booking At"},"history":{"items":{"$ref":"#/components/schemas/CustomerBookingHistoryEntry"},"type":"array","maxItems":500,"title":"History"}},"additionalProperties":false,"type":"object","required":["id","booking_count"],"title":"CustomerProfile","description":"Response for ``GET /booking/customers/{customer_id}``."},"CustomerSummary":{"properties":{"id":{"type":"string","maxLength":128,"minLength":1,"title":"Id"},"name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string","maxLength":320,"format":"email"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Phone"},"booking_count":{"type":"integer","maximum":1000000.0,"minimum":0.0,"title":"Booking Count"},"total_spent_cents":{"type":"integer","maximum":100000000000.0,"minimum":0.0,"title":"Total Spent Cents","default":0},"last_booking_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Booking At"}},"additionalProperties":false,"type":"object","required":["id","booking_count"],"title":"CustomerSummary","description":"One row in ``GET /booking/customers``.\n\n``id`` is a deterministic hash of (email || phone) — stable across\nbookings for the same contact without materializing a customers table."},"DNSBLResult":{"properties":{"spam_trap_risk":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Spam Trap Risk","description":"Spam trap risk score 0-100","default":0},"blacklists_hit":{"items":{"type":"string"},"type":"array","title":"Blacklists Hit","description":"List of blacklists the domain appears on"},"checked_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checked At","description":"Timestamp of DNSBL check (ISO 8601)"}},"type":"object","title":"DNSBLResult","description":"DNSBL spam trap check result"},"DashboardActionResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"request_id":{"type":"string","title":"Request Id"},"new_status":{"type":"string","title":"New Status"}},"type":"object","required":["success","message","request_id","new_status"],"title":"DashboardActionResponse","description":"Result of an approve/deny action via dashboard session auth."},"DashboardFlowsResponse":{"properties":{"flows":{"items":{"$ref":"#/components/schemas/FlowSummary"},"type":"array","title":"Flows"}},"type":"object","required":["flows"],"title":"DashboardFlowsResponse"},"DashboardRunsResponse":{"properties":{"runs":{"items":{"$ref":"#/components/schemas/RunRecord"},"type":"array","title":"Runs"}},"type":"object","required":["runs"],"title":"DashboardRunsResponse"},"DashboardUserCreate":{"properties":{"user_id":{"type":"string","title":"User Id","description":"Better Auth user ID"},"role":{"type":"string","title":"Role","description":"Role: super_admin, brand_admin, or client_user","default":"client_user"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id","description":"Required for brand_admin and client_user"},"permissions":{"items":{"type":"string"},"type":"array","title":"Permissions","description":"Granular permission overrides"}},"type":"object","required":["user_id"],"title":"DashboardUserCreate","description":"Create/invite a dashboard user."},"DashboardUserListItem":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"role":{"type":"string","title":"Role"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"brands":{"items":{"$ref":"#/components/schemas/schemas__dashboard__UserBrandMembership"},"type":"array","title":"Brands","description":"Brands the user belongs to"}},"type":"object","required":["id","user_id","role","is_active"],"title":"DashboardUserListItem","description":"Dashboard user in a list response."},"DashboardUserListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/DashboardUserListItem"},"type":"array","title":"Users"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["users","total"],"title":"DashboardUserListResponse","description":"List of dashboard users."},"DashboardUserResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"type":"string","title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"role":{"type":"string","title":"Role","description":"User role: super_admin, brand_admin, or client_user"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id","description":"Client ID (required for brand_admin and client_user)"},"permissions":{"items":{"type":"string"},"type":"array","title":"Permissions","default":[]},"event_sounds_enabled":{"type":"boolean","title":"Event Sounds Enabled","description":"Per-user opt-in for /dashboard/live audio cues (Live Theater Stage D).","default":false}},"type":"object","required":["user_id","email","role"],"title":"DashboardUserResponse","description":"Current user info returned after session validation."},"DashboardUserUpdate":{"properties":{"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role","description":"Role: super_admin, brand_admin, or client_user"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id","description":"Client ID (required for brand_admin and client_user)"},"permissions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permissions"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"DashboardUserUpdate","description":"Update a dashboard user's role or permissions."},"DataRestrictionCreateRequest":{"properties":{"source_id":{"type":"string","maxLength":128,"minLength":1,"title":"Source Id"},"group_slug":{"type":"string","maxLength":64,"minLength":1,"title":"Group Slug"},"field":{"type":"string","maxLength":64,"minLength":1,"title":"Field"},"op":{"type":"string","maxLength":16,"title":"Op","default":"in"},"allowed_values":{"items":{"type":"string"},"type":"array","maxItems":500,"title":"Allowed Values"}},"type":"object","required":["source_id","group_slug","field"],"title":"DataRestrictionCreateRequest"},"DataRestrictionUpdateRequest":{"properties":{"op":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Op"},"allowed_values":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":500},{"type":"null"}],"title":"Allowed Values"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"DataRestrictionUpdateRequest"},"DataSourceAggregateBucket":{"properties":{"label":{"type":"string","title":"Label","description":"Human-readable bucket label.  For time-bucketed group_by fields this is 'YYYY-MM' (post date_trunc('month', ...) + to_char).  For categorical group_by it's the column value (or the joined-out label, e.g. content_authors.full_name instead of an author UUID).","default":""},"value":{"type":"number","title":"Value","description":"Aggregate value (count / sum / avg over value_field).  Always float to keep the chart shape uniform between count (integer) and avg (decimal).","default":0.0}},"type":"object","title":"DataSourceAggregateBucket","description":"One row in a chart aggregation result.\n\nMarketplace V2 W5.3 — `chart` block_type.  The renderer fetches an\narray of these from `GET /content/data-sources/{id}/aggregate`,\nstamps it onto the block as `aggregation_data`, and the renderer-side\n`apps/liquid-renderer/src/blocks/chart.ts` builds a Chart.js config."},"DataSourceAggregateResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DataSourceAggregateBucket"},"type":"array","title":"Items"},"total":{"type":"integer","minimum":0.0,"title":"Total","default":0},"source_id":{"type":"string","title":"Source Id"},"group_by_field":{"type":"string","title":"Group By Field"},"aggregation":{"type":"string","title":"Aggregation"},"value_field":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Value Field"}},"type":"object","required":["source_id","group_by_field","aggregation"],"title":"DataSourceAggregateResponse","description":"Response shape for GET /content/data-sources/{id}/aggregate.\n\nReturns up to `min(max_items, 500)` buckets ordered by:\n- label ASC for time-bucketed group_by (line/area chart natural order)\n- value DESC for categorical group_by (pie/bar surfaces biggest first)"},"DataSourceConfig":{"properties":{"name":{"type":"string","minLength":1,"title":"Name"},"type":{"type":"string","minLength":1,"title":"Type","description":"SpiderIQ job type (spiderMaps, spiderSite, etc.)"},"job_id":{"type":"string","minLength":1,"title":"Job Id"},"variable_name":{"type":"string","minLength":1,"title":"Variable Name","description":"Template variable name for this data"},"refresh_interval":{"type":"integer","title":"Refresh Interval","description":"Cache TTL in seconds","default":3600}},"type":"object","required":["name","type","job_id","variable_name"],"title":"DataSourceConfig"},"DataSourceItemsResponse":{"properties":{"items":{"items":{"type":"object"},"type":"array","title":"Items","description":"Records from the data source, filtered/sorted/paginated per the query params. Empty list when nothing matches."},"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Total matching records on the server (drives pagination; may exceed len(items) when a limit applies).","default":0},"source_id":{"type":"string","title":"Source Id","description":"The source id, echoed back for renderer verification."}},"type":"object","required":["source_id"],"title":"DataSourceItemsResponse","description":"Response shape for GET /content/data-sources/{id}/items.\n\nBacks the dynamic `list` / `item` blocks and any component that binds a\ncollection via ``kind='dynamic'`` + ``data_binding`` — the renderer\n(``api-client.ts fetchDataSource``) calls this once per data-driven block\nand stamps ``items`` onto the block. Flat envelope, source-shape-independent:\neach record's keys are the source's ``schema_json.fields[].id`` (trimmed to\nthe requested ``?fields=`` when supplied)."},"DataSourceListResponse":{"properties":{"sources":{"items":{"$ref":"#/components/schemas/DataSourceResponse"},"type":"array","title":"Sources"},"total":{"type":"integer","minimum":0.0,"title":"Total"}},"type":"object","required":["sources","total"],"title":"DataSourceListResponse","description":"Response shape for GET /content/data-sources."},"DataSourceResponse":{"properties":{"id":{"type":"string","title":"Id"},"label":{"type":"string","title":"Label"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"is_collection":{"type":"boolean","title":"Is Collection"},"is_public":{"type":"boolean","title":"Is Public"},"sort_order":{"type":"integer","title":"Sort Order"},"fields":{"items":{"type":"object"},"type":"array","title":"Fields"},"filters":{"items":{"type":"object"},"type":"array","title":"Filters"}},"type":"object","required":["id","label","is_collection","is_public","sort_order"],"title":"DataSourceResponse","description":"Read-side projection of a content_data_sources row.\n\nMirrors ``services.data_source_registry.DataSource`` — no extra fields."},"DeleteCampaignResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"campaign_id":{"type":"string","title":"Campaign Id"},"deleted_locations":{"type":"integer","title":"Deleted Locations"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","campaign_id","deleted_locations","message"],"title":"DeleteCampaignResponse","description":"Response for campaign deletion."},"DeployDetailResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"script_name":{"type":"string","title":"Script Name"},"status":{"type":"string","title":"Status"},"version_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Id"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"triggered_by":{"type":"string","title":"Triggered By","default":"manual"},"kv_namespace_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kv Namespace Id"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"deployed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deployed At"},"snapshot":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Snapshot"},"build_log":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Build Log"},"screenshot_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Screenshot Url"},"screenshot_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Screenshot Status"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"}},"additionalProperties":true,"type":"object","required":["id","client_id","script_name","status"],"title":"DeployDetailResponse","description":"Full per-deploy detail — backs /dashboard/content/deployments/:id.\n\nSuperset of DeployStatusResponse with the migration-267 detail fields:\nthe content `snapshot` (our analog of a git commit), the `build_log`, the\nasync `screenshot_url`/`screenshot_status`, and a computed `duration_ms`.\nPre-267 deploys return NULL for the new fields (UI renders \"no detail\")."},"DeployListResponse":{"properties":{"deploys":{"items":{"$ref":"#/components/schemas/DeployStatusResponse"},"type":"array","title":"Deploys"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["deploys","total"],"title":"DeployListResponse","description":"List of recent deploys."},"DeployReadinessResponse":{"properties":{"ready":{"type":"boolean","title":"Ready"},"checklist":{"items":{"$ref":"#/components/schemas/ReadinessCheckItem"},"type":"array","title":"Checklist"},"blocking":{"items":{"type":"string"},"type":"array","title":"Blocking","default":[]},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings","default":[]}},"type":"object","required":["ready","checklist"],"title":"DeployReadinessResponse","description":"Pre-deploy readiness checklist."},"DeployResponse":{"properties":{"deploy_id":{"type":"string","title":"Deploy Id"},"status":{"type":"string","title":"Status"},"script_name":{"type":"string","title":"Script Name"},"version_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Id"},"domains":{"items":{"type":"string"},"type":"array","title":"Domains","default":[]},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["deploy_id","status","script_name"],"title":"DeployResponse","description":"Deploy result."},"DeployStatusResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"script_name":{"type":"string","title":"Script Name"},"status":{"type":"string","title":"Status"},"version_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Id"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"triggered_by":{"type":"string","title":"Triggered By","default":"manual"},"kv_namespace_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kv Namespace Id"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"deployed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deployed At"}},"type":"object","required":["id","client_id","script_name","status"],"title":"DeployStatusResponse","description":"Current deploy status for a client."},"DeviceCodePollResponse":{"properties":{"status":{"type":"string","title":"Status"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["status"],"title":"DeviceCodePollResponse"},"DeviceCodeStartResponse":{"properties":{"user_code":{"type":"string","title":"User Code"},"verification_uri":{"type":"string","title":"Verification Uri"},"expires_in":{"type":"integer","title":"Expires In"},"interval":{"type":"integer","title":"Interval"},"poll_id":{"type":"string","title":"Poll Id"}},"type":"object","required":["user_code","verification_uri","expires_in","interval","poll_id"],"title":"DeviceCodeStartResponse"},"DigestSettingsResponse":{"properties":{"cadence":{"type":"string","title":"Cadence"},"last_sent_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Sent At"}},"type":"object","required":["cadence"],"title":"DigestSettingsResponse"},"DigestSettingsUpdate":{"properties":{"cadence":{"type":"string","pattern":"^(off|daily|weekly)$","title":"Cadence"}},"type":"object","required":["cadence"],"title":"DigestSettingsUpdate"},"DiscoveryConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Use Perplexity to discover domain + LinkedIn URL","default":true}},"type":"object","title":"DiscoveryConfig"},"DocAskRequest":{"properties":{"query":{"type":"string","maxLength":500,"minLength":3,"title":"Query","description":"The question to ask the docs."},"top_k":{"type":"integer","maximum":12.0,"minimum":1.0,"title":"Top K","description":"How many source passages to ground the answer in.","default":6}},"type":"object","required":["query"],"title":"DocAskRequest","description":"Body for POST /content/docs/ask (Docs Platform v2 · 3.2)."},"DocCreate":{"properties":{"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string","pattern":"^(summary|summary_large_image)$"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string","pattern":"^(index|noindex),(follow|nofollow)$"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"slug":{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Slug"},"title":{"type":"string","maxLength":500,"minLength":1,"title":"Title"},"body":{"type":"object","title":"Body","description":"Tiptap JSON document"},"is_section":{"type":"boolean","title":"Is Section","default":false},"sort_order":{"type":"integer","title":"Sort Order","default":0},"version":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Version","default":"default"}},"type":"object","required":["slug","title","body"],"title":"DocCreate","description":"Create a documentation page."},"DocEventRequest":{"properties":{"event_type":{"type":"string","pattern":"^(view|search|ask)$","title":"Event Type"},"doc_path":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Doc Path"},"version":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Version","default":"default"},"query_text":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Query Text"}},"type":"object","required":["event_type"],"title":"DocEventRequest","description":"Body for POST /content/docs/event — a docs analytics beacon (Docs v2 · 3.4).\n\nFire-and-forget capture of a docs page view (or, if a client wants, a\nsearch/ask). doc.liquid only ever beacons ``view`` — search/ask are captured\nserver-side in their own handlers to avoid double counting."},"DocFeedbackRequest":{"properties":{"doc_path":{"type":"string","maxLength":512,"minLength":1,"title":"Doc Path"},"helpful":{"type":"boolean","title":"Helpful"},"comment":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Comment"}},"type":"object","required":["doc_path","helpful"],"title":"DocFeedbackRequest","description":"One 'was this helpful?' vote from a docs page."},"DocListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"slug":{"type":"string","title":"Slug"},"full_path":{"type":"string","title":"Full Path"},"title":{"type":"string","title":"Title"},"status":{"$ref":"#/components/schemas/ContentStatus"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"is_section":{"type":"boolean","title":"Is Section"},"sort_order":{"type":"integer","title":"Sort Order"},"seo_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Description"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","slug","full_path","title","status","is_section","sort_order","created_at","updated_at"],"title":"DocListItem","description":"Flat documentation list item (metadata only — no body).\n\nComplements ``DocTreeItem`` (hierarchy). Agents use the flat list to find a\ndoc's UUID by title/status before fetching/mutating it. Body is omitted to\nkeep the payload small; fetch it via ``GET /docs/{id}``."},"DocListResponse":{"properties":{"docs":{"items":{"$ref":"#/components/schemas/DocListItem"},"type":"array","title":"Docs"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["docs","total"],"title":"DocListResponse","description":"Flat list of docs."},"DocMarkdownImport":{"properties":{"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string","pattern":"^(summary|summary_large_image)$"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string","pattern":"^(index|noindex),(follow|nofollow)$"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"markdown":{"type":"string","maxLength":262144,"minLength":1,"title":"Markdown","description":"Markdown source (≤256 KB). Parsed to a Tiptap body; `:::component{...}` → component node."},"doc_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Doc Id","description":"Existing doc to replace; omit to create a new doc."},"slug":{"anyOf":[{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$"},{"type":"null"}],"title":"Slug","description":"Flat slug — required when creating, ignored when updating (slug is immutable)."},"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title","description":"Required when creating; updates the title when set on an existing doc."},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"is_section":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Section"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"}},"additionalProperties":false,"type":"object","required":["markdown"],"title":"DocMarkdownImport","description":"Import a doc from a Markdown body (Docs Platform v2 · 2.2).\n\nCreate a NEW doc (omit ``doc_id``, provide ``slug`` + ``title``) or REPLACE an\nexisting doc's body (set ``doc_id``). The Markdown is parsed to a Tiptap body\nvia ``content_markdown.markdown_to_tiptap``; ``:::component{...}`` directives\nbecome embedded component nodes (the 2.1 MDX unlock). SEO fields (from\nSeoMixin) are optional and applied on create/update just like ``DocCreate``."},"DocReorderRequest":{"properties":{"items":{"items":{"type":"object"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"DocReorderRequest","description":"Reorder documentation pages."},"DocResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"slug":{"type":"string","title":"Slug"},"full_path":{"type":"string","title":"Full Path"},"title":{"type":"string","title":"Title"},"body":{"type":"object","title":"Body"},"body_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Text"},"status":{"$ref":"#/components/schemas/ContentStatus"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"sort_order":{"type":"integer","title":"Sort Order"},"is_section":{"type":"boolean","title":"Is Section"},"seo_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"api_base_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base Url"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version","default":"default"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Url"},"warnings":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Warnings"}},"type":"object","required":["id","slug","full_path","title","body","status","sort_order","is_section","created_at","updated_at"],"title":"DocResponse","description":"Documentation page response."},"DocTreeItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"full_path":{"type":"string","title":"Full Path"},"title":{"type":"string","title":"Title"},"is_section":{"type":"boolean","title":"Is Section"},"sort_order":{"type":"integer","title":"Sort Order"},"status":{"anyOf":[{"$ref":"#/components/schemas/ContentStatus"},{"type":"null"}]},"children":{"items":{"$ref":"#/components/schemas/DocTreeItem"},"type":"array","title":"Children","default":[]}},"type":"object","required":["id","slug","full_path","title","is_section","sort_order"],"title":"DocTreeItem","description":"Documentation tree item (recursive)."},"DocTreeResponse":{"properties":{"tree":{"items":{"$ref":"#/components/schemas/DocTreeItem"},"type":"array","title":"Tree"},"versions":{"items":{"$ref":"#/components/schemas/DocVersionItem"},"type":"array","title":"Versions","default":[]},"current_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Version"}},"type":"object","required":["tree"],"title":"DocTreeResponse","description":"Full documentation tree.\n\nDocs Platform v2 · 3.4 — carries the tenant's published docs ``versions``\nand the ``current_version`` the tree was rendered for, so the docs chrome\ncan build the version switcher from the one call it always makes."},"DocUpdate":{"properties":{"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string","pattern":"^(summary|summary_large_image)$"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string","pattern":"^(index|noindex),(follow|nofollow)$"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title"},"body":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Body"},"is_section":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Section"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"}},"type":"object","title":"DocUpdate","description":"Update documentation page."},"DocVersionCreate":{"properties":{"label":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[a-z0-9][a-z0-9._-]*$","title":"Label"},"title":{"type":"string","maxLength":120,"minLength":1,"title":"Title"},"is_default":{"type":"boolean","title":"Is Default","default":false},"sort_order":{"type":"integer","title":"Sort Order","default":0},"status":{"type":"string","pattern":"^(draft|published|archived)$","title":"Status","default":"published"}},"type":"object","required":["label","title"],"title":"DocVersionCreate","description":"Body for POST /docs/versions — register a docs version label."},"DocVersionItem":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"label":{"type":"string","title":"Label"},"title":{"type":"string","title":"Title"},"is_default":{"type":"boolean","title":"Is Default","default":false},"sort_order":{"type":"integer","title":"Sort Order","default":0},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},"type":"object","required":["label","title"],"title":"DocVersionItem","description":"One docs version label exposed in the chrome switcher (Docs v2 · 3.4)."},"DocVersionUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":120,"minLength":1},{"type":"null"}],"title":"Title"},"is_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Default"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"},"status":{"anyOf":[{"type":"string","pattern":"^(draft|published|archived)$"},{"type":"null"}],"title":"Status"}},"type":"object","title":"DocVersionUpdate","description":"Body for PATCH /docs/versions/{id} — edit a docs version."},"DocVersionsResponse":{"properties":{"versions":{"items":{"$ref":"#/components/schemas/DocVersionItem"},"type":"array","title":"Versions","default":[]},"current_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Version"}},"type":"object","title":"DocVersionsResponse","description":"Published docs versions for a tenant (Docs v2 · 3.4 switcher)."},"DomainCreate":{"properties":{"domain":{"type":"string","title":"Domain"},"homepage_slug":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Homepage Slug"}},"type":"object","required":["domain"],"title":"DomainCreate","description":"Create a domain mapping.","example":{"domain":"acme.com"}},"DomainFilterConfig":{"properties":{"filter_social_media":{"type":"boolean","title":"Filter Social Media","description":"Filter out social media URLs (facebook, instagram, linkedin)","default":true},"filter_review_sites":{"type":"boolean","title":"Filter Review Sites","description":"Filter out review site URLs (yelp, tripadvisor, google reviews)","default":true},"filter_directories":{"type":"boolean","title":"Filter Directories","description":"Filter out directory URLs (yellowpages, foursquare)","default":true},"custom_blacklist":{"items":{"type":"string"},"type":"array","title":"Custom Blacklist","description":"Additional domains to filter"}},"type":"object","title":"DomainFilterConfig","description":"Domain filtering configuration."},"DomainListResponse":{"properties":{"domains":{"items":{"$ref":"#/components/schemas/DomainResponse"},"type":"array","title":"Domains"},"total":{"type":"integer","title":"Total"},"platform_preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform Preview Url"},"can_add_custom_domain":{"type":"boolean","title":"Can Add Custom Domain","default":true},"default_subdomain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Subdomain"}},"type":"object","required":["domains","total"],"title":"DomainListResponse","description":"List of domains."},"DomainResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"domain":{"type":"string","title":"Domain"},"is_primary":{"type":"boolean","title":"Is Primary","default":false},"is_verified":{"type":"boolean","title":"Is Verified","default":false},"verification_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verification Token"},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At"},"ssl_status":{"type":"string","title":"Ssl Status","default":"pending"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"worker_route_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Worker Route Status"},"custom_hostname_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Hostname Status"},"needs_deploy":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Needs Deploy"},"homepage_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Homepage Slug"}},"type":"object","required":["id","domain"],"title":"DomainResponse","description":"Domain mapping response.\n\nThe two ``*_status`` fields are populated by ``POST /domains`` (Cloudflare\nonboarding outcome) and are ``None`` on every other endpoint (list / verify\n/ set-primary). Lets the dashboard render an actionable banner — \"Worker\nRoute created\" / \"skipped: zone not in our CF account\" / \"error: …\" —\nwithout needing a separate envelope schema."},"DomainUpdate":{"properties":{"new_domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"New Domain"},"homepage_slug":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Homepage Slug"}},"type":"object","title":"DomainUpdate","description":"Update a domain (SD1): rename the host and/or set its start page.\n\n`homepage_slug` accepts null to clear back to the renderer default; the\nroute uses `model_fields_set` to tell \"set to null\" from \"omitted\".","example":{"homepage_slug":"promo","new_domain":"go.acme.com"}},"DuplicateBlockRequest":{"properties":{"position":{"title":"Position","description":"'before', 'after' (default), or an int index to insert at.","default":"after"}},"type":"object","title":"DuplicateBlockRequest","description":"Body for duplicate-block endpoint."},"DuplicateMatch":{"properties":{"record":{"type":"object","title":"Record","description":"The checked record"},"matched_canonical_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Matched Canonical Id","description":"ID of matched canonical record"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Match confidence (0.0-1.0)"},"match_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Match Type","description":"Type of match (exact_hash, email, google_place_id, ml_fuzzy)"}},"type":"object","required":["record","confidence"],"title":"DuplicateMatch","description":"Information about a duplicate match"},"DuplicateRequest":{"properties":{"new_slug":{"anyOf":[{"type":"string","maxLength":255,"pattern":"^[a-z0-9][a-z0-9-]*$"},{"type":"null"}],"title":"New Slug","description":"Optional flat slug for the duplicate. Auto-generated if omitted."}},"type":"object","title":"DuplicateRequest","description":"Body for duplicate-page / duplicate-post / duplicate-doc endpoints.\n\nBoth fields are optional — if `new_slug` is omitted, the server auto-generates\n`{original-slug}-copy`, `-copy-2`, etc. picking the lowest unused suffix."},"DynamicBlockType":{"type":"string","enum":["list","item_details","form","calendar","kanban","chart","map","table"],"title":"DynamicBlockType","description":"Generic block shape for kind='dynamic' components.\n\nSet on content_components.block_type when kind='dynamic'; NULL otherwise.\nDrives the editor's block-shape picker and the renderer's data-fetch\npattern (a list block fetches multiple rows + iterates; an item_details\nblock fetches one row).\n\nNaming note: distinct from app.schemas.content.BlockType which is the\npage-editor block taxonomy (hero / features_grid / etc.). That enum\ndescribes content types of static blocks; this enum describes the data\nbinding pattern for dynamic blocks."},"EmbeddingRequest":{"properties":{"model":{"type":"string","maxLength":128,"minLength":1,"title":"Model","description":"OpenAI embedding model. Examples: 'text-embedding-3-large', 'text-embedding-3-small', 'text-embedding-ada-002'."},"input":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}],"title":"Input","description":"Single text or list of texts to embed. OpenAI accepts up to 2048 inputs per request."},"dimensions":{"anyOf":[{"type":"integer","maximum":3072.0,"minimum":1.0},{"type":"null"}],"title":"Dimensions","description":"Output vector dimensions. text-embedding-3-* support truncation; ada-002 ignores this."},"encoding_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Encoding Format","description":"'float' (default — list of floats) or 'base64' (compact binary). Affects wire size only."},"user":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"User","description":"Optional end-user identifier for abuse monitoring (passed through to OpenAI)."}},"type":"object","required":["model","input"],"title":"EmbeddingRequest","description":"Mirror of OpenAI's embeddings request body.\n\n``input`` is the load-bearing union type — OpenAI accepts either a single\nstring or a list of strings (or even token arrays for the legacy\n``text-embedding-ada-002``). We pass it through verbatim and let\nlitellm + OpenAI surface validation errors for malformed shapes."},"ErrorDetail":{"properties":{"message":{"type":"string","title":"Message"},"type":{"type":"string","title":"Type"},"param":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Param"},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},"type":"object","required":["message","type"],"title":"ErrorDetail","description":"Error detail following OpenAI format."},"EventEnvelope":{"properties":{"type":{"type":"string","title":"Type"},"ts":{"type":"string","title":"Ts"},"data":{"type":"object","title":"Data"}},"type":"object","required":["type","ts","data"],"title":"EventEnvelope"},"ExtensionSpec":{"properties":{"pattern":{"type":"string","enum":["renderer-hook","worker-route","tracking-relay","mcp-server"],"title":"Pattern","description":"Which extension pattern this implements."},"params":{"type":"object","title":"Params","description":"Pattern-specific configuration. e.g. for renderer-hook: {output_path, content_type}; for worker-route: {route_path, http_methods}; for tracking-relay: {provider, event_map}."},"secret_keys":{"items":{"type":"string"},"type":"array","maxItems":32,"title":"Secret Keys","description":"Keys this extension reads from the per-tenant secrets vault. Used by the editor's 'configure secrets' flow + the publish-time validation that all required secrets are populated."},"requires_tenant_route_registration":{"type":"boolean","title":"Requires Tenant Route Registration","description":"TRUE if installation requires registering a per-tenant route in the gateway. Phase F deferred — only valid for pattern='worker-route' or 'mcp-server'.","default":false}},"additionalProperties":false,"type":"object","required":["pattern"],"title":"ExtensionSpec","description":"Per-extension configuration.\n\nStored on content_components.extension_spec JSONB column when kind='extension'.\nShape varies by pattern; common fields are required, pattern-specific fields\nlive in `params`."},"ExtractFramesPayload":{"properties":{"action":{"const":"extract_frames","title":"Action","description":"Action discriminator","default":"extract_frames"},"video_url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Video Url","description":"Source video URL (SpiderMedia origin or guarded third-party)"},"strategy":{"allOf":[{"$ref":"#/components/schemas/FrameCountStrategy"}],"description":"How to compute frame count from source duration"},"target_frames":{"anyOf":[{"type":"integer","maximum":600.0,"minimum":10.0},{"type":"null"}],"title":"Target Frames","description":"strategy=target_frames: exact N frames"},"fps":{"anyOf":[{"type":"integer","maximum":60.0,"minimum":1.0},{"type":"null"}],"title":"Fps","description":"strategy=fps or duration_fps: frames per second"},"duration_seconds":{"anyOf":[{"type":"integer","maximum":120.0,"minimum":1.0},{"type":"null"}],"title":"Duration Seconds","description":"strategy=duration_fps: seconds to clip from start"},"output_format":{"type":"string","enum":["webp","jpeg"],"title":"Output Format","description":"WebP default — half the bytes of JPEG at same quality","default":"webp"},"output_width":{"type":"integer","maximum":1920.0,"minimum":320.0,"title":"Output Width","description":"Output frame width in pixels (height auto from aspect)","default":1280},"output_quality":{"type":"integer","maximum":95.0,"minimum":50.0,"title":"Output Quality","description":"Encoder quality 50-95","default":80},"test":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Test","description":"Route to test queue","default":false}},"type":"object","required":["video_url","strategy"],"title":"ExtractFramesPayload","description":"Payload for SpiderVideo `extract_frames` action.\n\nProduces a numbered sequence of web-optimized images suitable for\ncanvas + GSAP ScrollTrigger scroll-linked image sequences. Output\nmanifest plugs directly into the `sys-scroll-sequence` system component."},"ExtractFramesSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/ExtractFramesPayload"}],"description":"Frame-extraction configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":5}},"type":"object","required":["payload"],"title":"ExtractFramesSubmit","description":"Submit a SpiderVideo `extract_frames` job.","example":{"payload":{"aspectRatio":"9:16","musicUrl":"https://example.com/music.mp3","musicVolume":0.3,"projectName":"my-video","scenes":[{"durationInSeconds":5,"videoUrl":"https://example.com/scene1.mp4"},{"durationInSeconds":3,"videoUrl":"https://example.com/scene2.mp4"}],"transitionDurationInFrames":15,"upload":{"enabled":true}},"priority":5}},"FacetBucket":{"properties":{"name":{"type":"string","title":"Name"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["name","count"],"title":"FacetBucket","description":"Facet bucket with name and count."},"FeedCursorOut":{"properties":{"created_at":{"type":"string","format":"date-time","title":"Created At"},"id":{"type":"integer","title":"Id"}},"type":"object","required":["created_at","id"],"title":"FeedCursorOut","description":"Keyset cursor returned to the client for the read branch."},"FeedItem":{"properties":{"id":{"type":"integer","title":"Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"event_key":{"type":"string","maxLength":80,"title":"Event Key"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Read At"},"one_liner":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"One Liner"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"},"payload":{"type":"object","title":"Payload"}},"type":"object","required":["id","created_at","event_key"],"title":"FeedItem","description":"One row in the bell dropdown."},"FeedResponse":{"properties":{"unread":{"items":{"$ref":"#/components/schemas/FeedItem"},"type":"array","title":"Unread"},"read":{"items":{"$ref":"#/components/schemas/FeedItem"},"type":"array","title":"Read"},"next_cursor":{"anyOf":[{"$ref":"#/components/schemas/FeedCursorOut"},{"type":"null"}]}},"type":"object","required":["unread","read"],"title":"FeedResponse"},"FidelitySeverity":{"type":"string","enum":["blocking","lossy","info"],"title":"FidelitySeverity"},"FidelityWarning":{"properties":{"severity":{"$ref":"#/components/schemas/FidelitySeverity"},"component":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Component","description":"Component slug or page slug the warning attaches to. None = whole-import-level."},"message":{"type":"string","maxLength":1024,"minLength":1,"title":"Message"},"suggested_fix":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Suggested Fix"}},"type":"object","required":["severity","message"],"title":"FidelityWarning","description":"One row in the fidelity report. Persisted in content_imports.fidelity_warnings."},"FieldConfigItem":{"properties":{"key":{"type":"string","maxLength":100,"minLength":1,"pattern":"^[a-z][a-z0-9_]*$","title":"Key"},"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label"},"type":{"type":"string","maxLength":50,"title":"Type"},"visible":{"type":"boolean","title":"Visible","default":true},"position":{"type":"integer","minimum":0.0,"title":"Position","default":0},"group":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Group"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"}},"type":"object","required":["key","label","type"],"title":"FieldConfigItem","description":"One field in a board's field_config — drives the table view."},"FinishReason":{"type":"string","enum":["stop","length","tool_calls","content_filter","function_call"],"title":"FinishReason","description":"Reasons why the model stopped generating."},"FlowAnalyticsFailure":{"properties":{"run_id":{"type":"string","title":"Run Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["run_id","created_at","error_message"],"title":"FlowAnalyticsFailure"},"FlowAnalyticsKpis":{"properties":{"total_runs":{"type":"integer","title":"Total Runs"},"success_rate":{"type":"number","title":"Success Rate"},"avg_duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Duration Seconds"},"total_cost_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Cost Cents"}},"type":"object","required":["total_runs","success_rate","avg_duration_seconds","total_cost_cents"],"title":"FlowAnalyticsKpis"},"FlowAnalyticsResponse":{"properties":{"range":{"type":"string","enum":["7d","30d","90d"],"title":"Range"},"kpis":{"$ref":"#/components/schemas/FlowAnalyticsKpis"},"time_series":{"items":{"$ref":"#/components/schemas/FlowAnalyticsTimePoint"},"type":"array","title":"Time Series"},"status_breakdown":{"additionalProperties":{"type":"integer"},"type":"object","title":"Status Breakdown"},"scenario_field":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scenario Field"},"top_scenarios":{"items":{"$ref":"#/components/schemas/FlowAnalyticsScenario"},"type":"array","title":"Top Scenarios"},"recent_failures":{"items":{"$ref":"#/components/schemas/FlowAnalyticsFailure"},"type":"array","title":"Recent Failures"}},"type":"object","required":["range","kpis","time_series","status_breakdown","scenario_field","top_scenarios","recent_failures"],"title":"FlowAnalyticsResponse"},"FlowAnalyticsScenario":{"properties":{"label":{"type":"string","title":"Label"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["label","count"],"title":"FlowAnalyticsScenario"},"FlowAnalyticsTimePoint":{"properties":{"date":{"type":"string","title":"Date"},"succeeded":{"type":"integer","title":"Succeeded"},"failed":{"type":"integer","title":"Failed"},"running":{"type":"integer","title":"Running"}},"type":"object","required":["date","succeeded","failed","running"],"title":"FlowAnalyticsTimePoint"},"FlowDescriptorResponse":{"properties":{"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"kind":{"type":"string","maxLength":16,"minLength":1,"title":"Kind"},"name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Name"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"}},"additionalProperties":true,"type":"object","required":["flow_id","kind","status"],"title":"FlowDescriptorResponse","description":"Minimal flow identity returned by GET /booking/{id} and GET /forms/{id}.\n\n``extra='allow'`` so the Agent Trust Hardening Wave 3.4.2 ``guidance:`` block\nspliced by :func:`decorate_response_with_guidance` survives\n``response_model`` validation when the caller opts into ``?format=llm``."},"FlowDetail":{"properties":{"slug":{"type":"string","title":"Slug"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"category":{"type":"string","title":"Category"},"verified":{"type":"boolean","title":"Verified"},"supported_modes":{"items":{"type":"string"},"type":"array","title":"Supported Modes"},"icon_keys":{"items":{"type":"string"},"type":"array","title":"Icon Keys"},"cost_estimate_per_run":{"type":"integer","title":"Cost Estimate Per Run"},"dispatch_type":{"type":"string","title":"Dispatch Type"},"steps":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Steps"},"campaign_enumerator":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Enumerator"},"run_count":{"type":"integer","title":"Run Count","default":0},"input_schema":{"type":"object","title":"Input Schema"},"last_run_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Run At"},"campaign_scope_schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Campaign Scope Schema"},"marketing":{"anyOf":[{"$ref":"#/components/schemas/FlowMarketingResponse"},{"type":"null"}]}},"type":"object","required":["slug","name","description","category","verified","supported_modes","icon_keys","cost_estimate_per_run","dispatch_type","input_schema"],"title":"FlowDetail"},"FlowLockRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason","description":"Free-form note shown alongside the lock badge in the UI."}},"type":"object","title":"FlowLockRequest","description":"Body for ``POST /flows/{flow_id}/lock``. Empty body is acceptable —\nthe request is the action."},"FlowLockedErrorDetail":{"properties":{"error":{"type":"string","pattern":"^flow_locked$","title":"Error","default":"flow_locked"},"error_code":{"type":"string","pattern":"^flow_locked$","title":"Error Code","default":"flow_locked"},"message":{"type":"string","maxLength":500,"minLength":1,"title":"Message"},"locked_by_actor_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Locked By Actor Id"},"locked_by":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Locked By"},"locked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Locked At"},"locked_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked Reason"},"unlock_endpoint":{"type":"string","maxLength":512,"title":"Unlock Endpoint","description":"Where to POST to release the lock (path matches the dual-mounted Phase 11+12 prefix when the caller hit the project-scoped mount)."}},"type":"object","required":["message","unlock_endpoint"],"title":"FlowLockedErrorDetail","description":"423 Locked envelope. Mirrors :class:`schemas.content.PageLockedErrorDetail`\nso agents that already understand the page-locked shape can read this\nwithout retraining.\n\nSpiderFlow P1.W (2026-05-13) — surface dual key names for cross-surface\nconsistency with the agent-facing contract: ``error_code`` and ``locked_by``\nare the names the P1.T harness + future tooling expect; ``error`` and\n``locked_by_actor_id`` are kept for backward compatibility with the\nSpiderBook P4 page-locked envelope and existing MCP clients. New callers\nSHOULD read the underscored-prefix legacy names; the unsuffixed names are\naliases populated to the same values on every response."},"FlowMarketingResponse":{"properties":{"tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tagline"},"audience":{"items":{"type":"string"},"type":"array","title":"Audience","default":[]},"use_cases":{"items":{"type":"string"},"type":"array","title":"Use Cases","default":[]},"result_highlights":{"items":{"type":"string"},"type":"array","title":"Result Highlights","default":[]},"docs_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docs Url"}},"type":"object","title":"FlowMarketingResponse","description":"§C5-IMPLEMENT — marketing/docs payload surfaced on the Info tab."},"FlowModel":{"type":"string","enum":["one_to_one_meeting","pooled_team_meeting","service_staff_resource","package_membership","table_reservation","class_capacity","equipment_rental","home_service_dispatch"],"title":"FlowModel","description":"Top-level industry discriminator for every booking flow.\n\nStored in ``norm_cli_*.booking_flows.model`` (CHECK constraint mirrors\nthese values — see migration 141). Drives renderer dispatch; v1 fully\nrenders the first four, remaining four return 501 until their dedicated\nrenderer ships."},"FlowRestoreRequest":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run","default":false},"confirm_token":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirm Token"}},"type":"object","title":"FlowRestoreRequest","description":"Body for ``POST /flows/{flow_id}/restore``. dry_run/confirm_token live\nin the body to match the existing publish/unpublish gate shape."},"FlowRunRequest":{"properties":{"input":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Input"},"inputs":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Inputs"},"scope":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Scope"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","default":0}},"additionalProperties":false,"type":"object","title":"FlowRunRequest","description":"Body of `POST /flows/{slug}/run`. Exactly one of input/inputs/scope."},"FlowSummary":{"properties":{"slug":{"type":"string","title":"Slug"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"category":{"type":"string","title":"Category"},"verified":{"type":"boolean","title":"Verified"},"supported_modes":{"items":{"type":"string"},"type":"array","title":"Supported Modes"},"icon_keys":{"items":{"type":"string"},"type":"array","title":"Icon Keys"},"cost_estimate_per_run":{"type":"integer","title":"Cost Estimate Per Run"},"dispatch_type":{"type":"string","title":"Dispatch Type"},"steps":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Steps"},"campaign_enumerator":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Enumerator"},"run_count":{"type":"integer","title":"Run Count","default":0}},"type":"object","required":["slug","name","description","category","verified","supported_modes","icon_keys","cost_estimate_per_run","dispatch_type"],"title":"FlowSummary"},"FlowValidateRequest":{"properties":{"model":{"allOf":[{"$ref":"#/components/schemas/FlowModel"}],"description":"Industry discriminator — determines model-specific invariants.","default":"one_to_one_meeting"},"flow":{"type":"object","title":"Flow","description":"Flow definition ({steps: [...]})."},"schema":{"type":"object","title":"Schema","description":"Per-step schema ({step_id: {...}})."}},"additionalProperties":false,"type":"object","title":"FlowValidateRequest","description":"Body for ``POST /flows/validate``.\n\nSeparate from :class:`BookingFlowCreate` because validation is available\nwithout a ``business_id`` — the Flow Editor \"Validate\" button runs against\na draft the user is still composing."},"FlowValidateResponse":{"properties":{"valid":{"type":"boolean","title":"Valid"},"spec_version":{"type":"string","title":"Spec Version","default":"spiderbook.flow.v1"},"errors":{"items":{"type":"object"},"type":"array","title":"Errors"}},"additionalProperties":false,"type":"object","required":["valid"],"title":"FlowValidateResponse","description":"Response for ``POST /flows/validate``."},"FlowVersionDetailResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"version_number":{"type":"integer","minimum":1.0,"title":"Version Number"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"flow":{"type":"object","title":"Flow"},"schema":{"type":"object","title":"Schema"},"translations":{"anyOf":[{"additionalProperties":{"additionalProperties":{"type":"string"},"type":"object"},"type":"object"},{"type":"null"}],"title":"Translations"},"changed_by":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Changed By"},"changed_by_role":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Changed By Role"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","version_number","name","created_at"],"title":"FlowVersionDetailResponse","description":"Full snapshot with the JSONB payloads inline (read for diff/restore preview)."},"FlowVersionEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"version_number":{"type":"integer","minimum":1.0,"title":"Version Number"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"changed_by":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Changed By"},"changed_by_role":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Changed By Role"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"flow_size":{"type":"integer","minimum":0.0,"title":"Flow Size"},"schema_size":{"type":"integer","minimum":0.0,"title":"Schema Size"}},"type":"object","required":["id","version_number","name","created_at","flow_size","schema_size"],"title":"FlowVersionEntry","description":"One entry in the version snapshot list (newest first)."},"FlowVersionListResponse":{"properties":{"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"versions":{"items":{"$ref":"#/components/schemas/FlowVersionEntry"},"type":"array","title":"Versions"},"total":{"type":"integer","minimum":0.0,"title":"Total"}},"type":"object","required":["flow_id","versions","total"],"title":"FlowVersionListResponse"},"FormFlowDescriptorResponse":{"properties":{"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"kind":{"type":"string","maxLength":16,"minLength":1,"title":"Kind"},"name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Name"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"}},"additionalProperties":true,"type":"object","required":["flow_id","kind","status"],"title":"FormFlowDescriptorResponse","description":"Minimal flow identity returned by GET /api/v1/forms/{flow_id}.\n\n``extra='allow'`` so the runtime-injected ``guidance:`` key (spliced by\n:func:`middleware.format_llm.decorate_response_with_guidance` when the\nrequest opts in) survives ``response_model`` validation. Precedent:\nP5's ``PageResponse`` / ``ComponentResponse``."},"FormSubmissionItem":{"properties":{"submission_id":{"type":"string","format":"uuid","title":"Submission Id"},"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"version":{"type":"integer","minimum":1.0,"title":"Version","default":1},"answers":{"type":"object","title":"Answers"},"hidden":{"type":"object","title":"Hidden"},"variables":{"type":"object","title":"Variables"},"thankyou_screen_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Thankyou Screen Id"},"ip_hash":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Ip Hash"},"user_agent":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"User Agent"},"duration_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Duration Ms"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"crm_status":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Crm Status"}},"type":"object","required":["submission_id","flow_id","created_at"],"title":"FormSubmissionItem","description":"One row in the submissions list. Mirrors @spideriq/core's\n``BookingFlowSubmission`` so the SDK can deserialize directly."},"FormSubmissionListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/FormSubmissionItem"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","title":"FormSubmissionListResponse"},"FormSubmitRequest":{"properties":{"submission":{"type":"object","title":"Submission","description":"Field name → value map. Validated against the resolved form block's fields."},"request_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Request Id","description":"Optional client-supplied retry-idempotency token (also accepted via X-Request-Id header). Combined with client_id, dedups duplicate submissions within a 24h window."}},"type":"object","required":["submission"],"title":"FormSubmitRequest","description":"Payload accepted by POST /api/v1/content/forms/{form_id}/submit.\n\nPublic endpoint — no auth required. The submission is opaque (Dict)\nat request validation time; the service layer validates each value\nagainst the form block's props.fields configuration after resolving\nform_id → page → block."},"FormSubmitResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"},"submission_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Submission Id"},"success_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Success Action","description":"redirect | message | modal — passed through from the form's props so the client can route UX."},"success_redirect_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Success Redirect Url"},"success_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Success Message"},"webhook_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Status","description":"pending | success | failed | skipped — null when no webhook configured."}},"type":"object","required":["ok"],"title":"FormSubmitResponse","description":"Response body for POST /api/v1/content/forms/{form_id}/submit."},"FormTemplateCategoryItem":{"properties":{"slug":{"type":"string","maxLength":64,"minLength":1,"title":"Slug"},"label":{"type":"string","maxLength":256,"minLength":1,"title":"Label"},"sort_order":{"type":"integer","title":"Sort Order"}},"type":"object","required":["slug","label","sort_order"],"title":"FormTemplateCategoryItem"},"FormTemplateCategoryItemList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/FormTemplateCategoryItem"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"FormTemplateCategoryItemList"},"FrameCountStrategy":{"type":"string","enum":["target_frames","fps","duration_fps"],"title":"FrameCountStrategy","description":"How to compute frame count + ffmpeg fps from the source video."},"FunctionCall":{"properties":{"name":{"type":"string","title":"Name"},"arguments":{"type":"string","title":"Arguments"}},"type":"object","required":["name","arguments"],"title":"FunctionCall","description":"Function call in assistant message (deprecated, use tool_calls)."},"FunctionDefinition":{"properties":{"name":{"type":"string","title":"Name","description":"The name of the function"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Description of the function"},"parameters":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Parameters","description":"JSON Schema for function parameters"},"strict":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Strict","description":"Enable strict mode for structured outputs"}},"type":"object","required":["name"],"title":"FunctionDefinition","description":"Function definition for tool calling."},"FuzzIQRecordInput":{"properties":{"email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Email","description":"Email address"},"full_name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Full Name","description":"Full name"},"first_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"First Name","description":"First name"},"last_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Last Name","description":"Last name"},"linkedin_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin Url","description":"LinkedIn profile URL"},"position":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Position","description":"Job title/position"},"company_name":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Company Name","description":"Company/business name"},"company_domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Company Domain","description":"Company website domain"},"google_place_id":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Google Place Id","description":"Google Place ID"},"website":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Website","description":"Website URL"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone","description":"Phone number"},"city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"City","description":"City"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country","description":"Country"},"extra_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Extra Fields","description":"Additional custom fields"}},"type":"object","title":"FuzzIQRecordInput","description":"Input record for deduplication check","example":{"city":"Chicago","company_name":"McDonald's","country":"United States","google_place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4","phone":"+1-555-123-4567"}},"FuzzIQStatsResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"enabled":{"type":"boolean","title":"Enabled","description":"Whether FuzzIQ is enabled for client"},"schema_exists":{"type":"boolean","title":"Schema Exists","description":"Whether client schema exists","default":true},"schema_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Schema Name","description":"PostgreSQL schema name"},"record_count":{"type":"integer","title":"Record Count","description":"Total canonical records","default":0},"is_active":{"type":"boolean","title":"Is Active","description":"Whether schema is active","default":true},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Schema creation time"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"Last update time"}},"type":"object","required":["enabled"],"title":"FuzzIQStatsResponse","description":"Response for FuzzIQ statistics","example":{"created_at":"2024-01-15T10:30:00Z","enabled":true,"is_active":true,"record_count":15420,"schema_exists":true,"schema_name":"client_abc123","success":true,"updated_at":"2024-01-20T14:22:00Z"}},"GeneralSettings":{"properties":{"business_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Business Name"},"timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Timezone"},"currency":{"anyOf":[{"type":"string","maxLength":3,"minLength":3,"pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Currency"},"date_format":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Date Format"},"time_format":{"anyOf":[{"type":"string","pattern":"^(12|24)$"},{"type":"null"}],"title":"Time Format"},"locations":{"items":{"type":"object"},"type":"array","maxItems":50,"title":"Locations"}},"additionalProperties":true,"type":"object","title":"GeneralSettings"},"GeneratePasswordResponse":{"properties":{"password":{"type":"string","title":"Password"}},"type":"object","required":["password"],"title":"GeneratePasswordResponse","description":"Response containing a generated password."},"GeoFilter":{"properties":{"lat":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Lat","description":"Latitude"},"lon":{"type":"number","maximum":180.0,"minimum":-180.0,"title":"Lon","description":"Longitude"},"radius_km":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Radius Km","description":"Search radius in kilometers","default":50}},"type":"object","required":["lat","lon"],"title":"GeoFilter","description":"Geo-distance filter for location-based search."},"GeoUnit":{"properties":{"unit_id":{"type":"string","title":"Unit Id"},"label":{"type":"string","title":"Label"},"kind":{"type":"string","title":"Kind"},"country_code":{"type":"string","title":"Country Code"},"admin_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Region"},"location_count":{"type":"integer","title":"Location Count"}},"type":"object","required":["unit_id","label","kind","country_code","location_count"],"title":"GeoUnit","description":"A selectable geo unit for the \"smart box\" typeahead picker — either a whole\ncountry (``kind=\"country\"``) or a US state (``kind=\"state\"``). Mirrors the\ndashboard ``/dashboard/client/locations/selectable-units`` shape so agent\nsurfaces and the UI pick from the identical contract."},"GlobalBookingTemplate":{"properties":{"template_id":{"type":"string","format":"uuid","title":"Template Id"},"slug":{"type":"string","maxLength":96,"minLength":1,"title":"Slug"},"name":{"type":"string","maxLength":512,"minLength":1,"title":"Name"},"category":{"type":"string","maxLength":64,"minLength":1,"title":"Category"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"flow":{"type":"object","title":"Flow"},"schema":{"type":"object","title":"Schema"},"usage_count":{"type":"integer","minimum":0.0,"title":"Usage Count"},"is_official":{"type":"boolean","title":"Is Official"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["template_id","slug","name","category","usage_count","is_official","created_at"],"title":"GlobalBookingTemplate","description":"Global library shape (``public.booking_templates_global``).\n\nDiffers from the per-client shape in two ways: carries ``is_official`` and\nhas no ``updated_at`` (global rows are append-only once seeded).\n\nForm Templates v2 (migration 243, 2026-05-20) added ``slug`` — the\nURL/agent-safe identifier the ``form_create_from_template { slug }`` MCP\ntool keys on. Kept separate from ``name`` so authors can rename a template\nwithout breaking every agent that already references it."},"GlobalBookingTemplateList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/GlobalBookingTemplate"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"GlobalBookingTemplateList"},"GmapsCoordinates":{"properties":{"latitude":{"type":"number","title":"Latitude","description":"Latitude coordinate"},"longitude":{"type":"number","title":"Longitude","description":"Longitude coordinate"}},"type":"object","required":["latitude","longitude"],"title":"GmapsCoordinates","description":"Geographic coordinates from Google Maps"},"GroupCreateRequest":{"properties":{"slug":{"type":"string","maxLength":64,"minLength":1,"title":"Slug"},"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"membership_type":{"type":"string","maxLength":16,"title":"Membership Type","default":"manual"},"condition_predicate":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Condition Predicate"}},"type":"object","required":["slug","name"],"title":"GroupCreateRequest"},"GroupUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"condition_predicate":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Condition Predicate"},"clear_predicate":{"type":"boolean","title":"Clear Predicate","default":false}},"type":"object","title":"GroupUpdateRequest"},"GscConnectCallbackRequest":{"properties":{"code":{"type":"string","maxLength":2048,"minLength":1,"title":"Code"},"state":{"type":"string","maxLength":512,"minLength":1,"title":"State"}},"type":"object","required":["code","state"],"title":"GscConnectCallbackRequest"},"GscConnectStartResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"},"state":{"type":"string","title":"State"},"configured":{"type":"boolean","title":"Configured","description":"False when GSC_GOOGLE_CLIENT_ID is not set on the server (the OAuth client hasn't been provisioned yet).","default":true}},"type":"object","required":["auth_url","state"],"title":"GscConnectStartResponse"},"GscStatusResponse":{"properties":{"connected":{"type":"boolean","title":"Connected"},"account_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Email"},"connected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Connected At"},"token_healthy":{"type":"boolean","title":"Token Healthy","default":false},"properties":{"items":{"type":"string"},"type":"array","title":"Properties"},"configured":{"type":"boolean","title":"Configured","description":"False when the server has no GSC OAuth client configured.","default":true}},"type":"object","required":["connected"],"title":"GscStatusResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthOverviewRow":{"properties":{"sender_id":{"type":"integer","title":"Sender Id"},"email_address":{"type":"string","title":"Email Address"},"provider":{"type":"string","title":"Provider"},"connection_id":{"type":"integer","title":"Connection Id"},"mailbox_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mailbox Id"},"snapshot":{"anyOf":[{"$ref":"#/components/schemas/HealthSnapshotResponse"},{"type":"null"}]}},"type":"object","required":["sender_id","email_address","provider","connection_id","mailbox_id","snapshot"],"title":"HealthOverviewRow"},"HealthSnapshotResponse":{"properties":{"polled_at":{"type":"string","title":"Polled At"},"health_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Health Score"},"health_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Health Label"},"score_recomputed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Score Recomputed At"},"sent_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sent 24H"},"received_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Received 24H"},"inbox_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Inbox 24H"},"spam_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Spam 24H"},"bounce_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bounce 24H"},"reply_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reply 24H"},"sent_7d":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sent 7D"},"inbox_7d":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Inbox 7D"},"spam_7d":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Spam 7D"},"warmup_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Warmup Enabled"},"warmup_ramp_up":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Warmup Ramp Up"},"warmup_max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Warmup Max"},"fetch_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fetch Error"}},"type":"object","required":["polled_at","health_score","health_label","score_recomputed_at","sent_24h","received_24h","inbox_24h","spam_24h","bounce_24h","reply_24h","sent_7d","inbox_7d","spam_7d","warmup_enabled","warmup_ramp_up","warmup_max","fetch_error"],"title":"HealthSnapshotResponse"},"HoldSlotRequest":{"properties":{"slot_start":{"type":"string","format":"date-time","title":"Slot Start","description":"Slot start in ISO 8601 with timezone offset."},"slot_end":{"type":"string","format":"date-time","title":"Slot End","description":"Slot end in ISO 8601 with timezone offset."},"service_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Service Id","description":"Optional service choice — required when the flow has a 'select' step."},"staff_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Staff Id","description":"Optional staff choice — required for round-robin holds."}},"additionalProperties":false,"type":"object","required":["slot_start","slot_end"],"title":"HoldSlotRequest","description":"POST /booking/{flow_id}/hold-slot payload."},"HoldSlotResponse":{"properties":{"hold_id":{"type":"string","format":"uuid","title":"Hold Id","description":"Opaque hold identifier."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"UTC timestamp when the hold is released automatically."}},"additionalProperties":false,"type":"object","required":["hold_id","expires_at"],"title":"HoldSlotResponse","description":"POST /booking/{flow_id}/hold-slot success payload."},"IdapBatchRequest":{"properties":{"refs":{"items":{"type":"string"},"type":"array","maxItems":100,"minItems":1,"title":"Refs","description":"List of idap:// references to fetch"},"fields":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Fields","description":"Comma-separated field projection"},"include":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Include","description":"Comma-separated include types (e.g. 'emails,phones')"}},"type":"object","required":["refs"],"title":"IdapBatchRequest","description":"Batch fetch request — up to 100 idap:// refs."},"IdapBatchResponse":{"properties":{"results":{"additionalProperties":{"$ref":"#/components/schemas/IdapResource"},"type":"object","title":"Results","description":"Resolved resources keyed by idap:// ref"},"errors":{"additionalProperties":{"type":"string"},"type":"object","title":"Errors","description":"Errors keyed by idap:// ref"}},"type":"object","title":"IdapBatchResponse","description":"Batch fetch response."},"IdapBulkFlagError":{"properties":{"resource_id":{"type":"string","maxLength":500,"minLength":1,"title":"Resource Id"},"error":{"type":"string","maxLength":200,"minLength":1,"title":"Error"}},"type":"object","required":["resource_id","error"],"title":"IdapBulkFlagError","description":"One resource's failure inside a bulk flag response."},"IdapBulkFlagResponse":{"properties":{"updated":{"type":"integer","minimum":0.0,"title":"Updated","description":"Count of resources whose flags were updated"},"failed":{"type":"integer","minimum":0.0,"title":"Failed","description":"Count of resources that failed (e.g. not found)"},"errors":{"items":{"$ref":"#/components/schemas/IdapBulkFlagError"},"type":"array","title":"Errors","description":"Per-resource errors (same length as `failed`)"}},"type":"object","required":["updated","failed"],"title":"IdapBulkFlagResponse","description":"Response for a bulk flag write — summary of successes and failures."},"IdapBulkFlagUpdate":{"properties":{"resource_id":{"type":"string","maxLength":500,"minLength":1,"title":"Resource Id"},"add":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Add","description":"Flags to add for this resource"},"remove":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Remove","description":"Flags to remove for this resource"}},"type":"object","required":["resource_id"],"title":"IdapBulkFlagUpdate","description":"Single update inside a bulk flag write request."},"IdapBulkFlagWrite":{"properties":{"updates":{"items":{"$ref":"#/components/schemas/IdapBulkFlagUpdate"},"type":"array","maxItems":100,"minItems":1,"title":"Updates","description":"List of per-resource flag updates (1-100)"},"flagged_by":{"type":"string","maxLength":200,"minLength":1,"title":"Flagged By","description":"Who initiated the bulk write — e.g. 'board:bulk_move'"},"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason","description":"Optional shared reason applied to every add in this batch"}},"type":"object","required":["updates","flagged_by"],"title":"IdapBulkFlagWrite","description":"Bulk flag write request body — up to 100 updates in one transaction.\n\nUsed by OPVS boards when an agent bulk-moves many cards between columns.\nAvoids firing 100 individual POST requests."},"IdapDuplicateCluster":{"properties":{"key_value":{"type":"string","maxLength":500,"minLength":1,"title":"Key Value","description":"Shared external value (e.g. the Google Place ID, domain, VAT) that every resource in this cluster has in common."},"count":{"type":"integer","minimum":2.0,"title":"Count","description":"Number of resources sharing this value (always >= 2 — single-occurrence values are filtered out by HAVING COUNT(*) > 1)."},"resource_ids":{"items":{"type":"string"},"type":"array","minItems":2,"title":"Resource Ids","description":"UUIDs of the resources in this cluster. For /businesses/duplicates these are business UUIDs in the client's `norm_cli_*.businesses` table."}},"type":"object","required":["key_value","count","resource_ids"],"title":"IdapDuplicateCluster","description":"One cluster of duplicate resources sharing a common key value."},"IdapDuplicatesResponse":{"properties":{"resource_type":{"allOf":[{"$ref":"#/components/schemas/ResourceType"}],"description":"Resource type the duplicates were grouped on."},"key":{"type":"string","maxLength":50,"minLength":1,"title":"Key","description":"The whitelisted key used for clustering (e.g. 'google_place_id', 'domain', 'vat')."},"clusters":{"items":{"$ref":"#/components/schemas/IdapDuplicateCluster"},"type":"array","title":"Clusters","description":"Duplicate clusters ordered by count DESC, then key_value ASC. Empty list means no duplicates exist for this key on this tenant."},"total_clusters":{"type":"integer","minimum":0.0,"title":"Total Clusters","description":"Total number of clusters returned (== len(clusters), capped by the request's `limit` parameter)."}},"type":"object","required":["resource_type","key","total_clusters"],"title":"IdapDuplicatesResponse","description":"Response from `/idap/<resource_type>/duplicates?key=…`."},"IdapFlagEntry":{"properties":{"action":{"type":"string","title":"Action","description":"'add' or 'remove'"},"flag":{"type":"string","maxLength":50,"minLength":1,"title":"Flag","description":"Flag name"},"flagged_by":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Flagged By","description":"Who set/removed the flag"},"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason","description":"Reason for the change"},"at":{"type":"string","format":"date-time","title":"At","description":"When the action occurred"}},"type":"object","required":["action","flag","at"],"title":"IdapFlagEntry","description":"Single flag history entry."},"IdapFlagResponse":{"properties":{"idap_ref":{"type":"string","maxLength":600,"minLength":1,"title":"Idap Ref","description":"idap://type/id reference"},"flags":{"items":{"type":"string"},"type":"array","title":"Flags","description":"Currently active flags"},"flag_history":{"items":{"$ref":"#/components/schemas/IdapFlagEntry"},"type":"array","title":"Flag History","description":"Recent flag history (most recent first)"}},"type":"object","required":["idap_ref"],"title":"IdapFlagResponse","description":"Response after writing flags — current state + recent history."},"IdapFlagWrite":{"properties":{"add":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Add","description":"Flags to add"},"remove":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Remove","description":"Flags to remove"},"flagged_by":{"type":"string","maxLength":200,"minLength":1,"title":"Flagged By","description":"Who set the flag — e.g. 'agent:maya_001', 'user:martin'"},"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason","description":"Optional reason for the flag change"}},"type":"object","required":["flagged_by"],"title":"IdapFlagWrite","description":"Request body for writing flags on a resource."},"IdapListResponse":{"properties":{"resource_type":{"allOf":[{"$ref":"#/components/schemas/ResourceType"}],"description":"Resource type"},"count":{"type":"integer","minimum":0.0,"title":"Count","description":"Number of items in this page"},"has_more":{"type":"boolean","title":"Has More","description":"Whether more items exist"},"next_cursor":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Next Cursor","description":"Opaque cursor for next page"},"items":{"items":{"$ref":"#/components/schemas/IdapResource"},"type":"array","title":"Items","description":"Resource items"}},"type":"object","required":["resource_type","count","has_more"],"title":"IdapListResponse","description":"Paginated list of resources."},"IdapResource":{"properties":{"idap_ref":{"type":"string","maxLength":600,"minLength":1,"title":"Idap Ref","description":"idap://type/id reference"},"resource_type":{"allOf":[{"$ref":"#/components/schemas/ResourceType"}],"description":"Resource type"},"resource_id":{"type":"string","maxLength":500,"minLength":1,"title":"Resource Id","description":"Resource identifier"},"tenant_id":{"type":"string","maxLength":100,"minLength":1,"title":"Tenant Id","description":"Client ID (tenant)"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"Resource creation time"},"modified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Modified At","description":"Last modification time"},"flags":{"items":{"type":"string"},"type":"array","title":"Flags","description":"Active flags on this resource"},"data":{"type":"object","title":"Data","description":"Resource data fields"},"related":{"anyOf":[{"additionalProperties":{"items":{"type":"object"},"type":"array"},"type":"object"},{"type":"null"}],"title":"Related","description":"Related child resources (populated when ?include= is used)"}},"type":"object","required":["idap_ref","resource_type","resource_id","tenant_id"],"title":"IdapResource","description":"Single resource returned by IDAP."},"IdapSourceConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"resource_type":{"type":"string","maxLength":50,"title":"Resource Type"},"filters":{"$ref":"#/components/schemas/IdapSourceFilters"},"column_flag_map":{"additionalProperties":{"type":"string"},"type":"object","title":"Column Flag Map"},"field_map":{"additionalProperties":{"type":"string"},"type":"object","title":"Field Map"},"last_sync_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Sync Cursor"},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Sync At"}},"type":"object","required":["resource_type"],"title":"IdapSourceConfig","description":"IDAP binding for a CRM board. When set, norm_cli_* rows sync as cards."},"IdapSourceFilters":{"properties":{"flags":{"type":"string","maxLength":200,"title":"Flags","default":"!rejected"},"campaign_id":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Campaign Id"}},"type":"object","title":"IdapSourceFilters"},"IdapStats":{"properties":{"resource_type":{"allOf":[{"$ref":"#/components/schemas/ResourceType"}],"description":"Resource type"},"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Total resource count"},"by_flag":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Flag","description":"Count per active flag"},"by_source":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Source","description":"Count per source worker"},"last_24h":{"additionalProperties":{"type":"integer"},"type":"object","title":"Last 24H","description":"Activity in last 24 hours"}},"type":"object","required":["resource_type","total"],"title":"IdapStats","description":"Aggregate stats for a resource type."},"ImageData":{"properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"b64_json":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"B64 Json"},"revised_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revised Prompt"}},"type":"object","title":"ImageData","description":"One entry in the response's ``data`` array."},"ImageGenerationRequest":{"properties":{"model":{"type":"string","maxLength":128,"minLength":1,"title":"Model","description":"OpenAI image model. Examples: 'dall-e-3', 'dall-e-2', 'gpt-image-1'."},"prompt":{"type":"string","maxLength":4000,"minLength":1,"title":"Prompt","description":"Text prompt describing the image. dall-e-3 max 4000 chars; dall-e-2 max 1000."},"n":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"N","description":"Number of images. dall-e-3 only supports n=1; dall-e-2 supports 1-10.","default":1},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size","description":"Image dimensions. dall-e-3: '1024x1024', '1024x1792', '1792x1024'. dall-e-2: '256x256', '512x512', '1024x1024'. None = provider default."},"quality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality","description":"dall-e-3 only: 'standard' (default) or 'hd' (2x cost)."},"style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Style","description":"dall-e-3 only: 'vivid' (default) or 'natural'."},"response_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Format","description":"'url' (default — 1h expiry) or 'b64_json' (inline base64)."},"user":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"User","description":"Optional end-user identifier for abuse monitoring (passed through to OpenAI)."}},"type":"object","required":["model","prompt"],"title":"ImageGenerationRequest","description":"Mirror of OpenAI's image-generation request body.\n\nField validators are kept minimal: we trust litellm to surface\nprovider-side validation errors (e.g. invalid size for dall-e-2 vs dall-e-3),\nand we trust ``get_client`` to gate at the SpiderGate boundary."},"ImageGenerationResponse":{"properties":{"created":{"type":"integer","title":"Created","description":"Unix timestamp of when the request completed."},"data":{"items":{"$ref":"#/components/schemas/ImageData"},"type":"array","title":"Data","description":"One entry per generated image."}},"type":"object","required":["created","data"],"title":"ImageGenerationResponse","description":"Mirror of OpenAI's image-generation response body."},"ImportJobListResponse":{"properties":{"imports":{"items":{"$ref":"#/components/schemas/ImportJobResponse"},"type":"array","title":"Imports"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["imports","total"],"title":"ImportJobListResponse"},"ImportJobResponse":{"properties":{"id":{"type":"string","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"source":{"$ref":"#/components/schemas/ImportSource"},"source_zip_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Zip Url"},"source_github_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Github Url"},"status":{"$ref":"#/components/schemas/ImportStatus"},"progress_stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Progress Stage"},"fidelity_warnings":{"items":{"$ref":"#/components/schemas/FidelityWarning"},"type":"array","title":"Fidelity Warnings"},"pages_created":{"type":"integer","title":"Pages Created","default":0},"components_created":{"type":"integer","title":"Components Created","default":0},"assets_uploaded":{"type":"integer","title":"Assets Uploaded","default":0},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"manifest":{"anyOf":[{"$ref":"#/components/schemas/ImportManifest"},{"type":"null"}]}},"type":"object","required":["id","client_id","source","status","started_at"],"title":"ImportJobResponse","description":"Returned by start, poll, and list endpoints. Lean projection of content_imports."},"ImportManifest":{"properties":{"source":{"$ref":"#/components/schemas/ImportSource"},"pages":{"items":{"$ref":"#/components/schemas/ImportedPage"},"type":"array","title":"Pages"},"components":{"items":{"$ref":"#/components/schemas/ImportedComponent"},"type":"array","title":"Components"},"assets":{"items":{"$ref":"#/components/schemas/ImportedAsset"},"type":"array","title":"Assets"},"fidelity_warnings":{"items":{"$ref":"#/components/schemas/FidelityWarning"},"type":"array","title":"Fidelity Warnings"}},"type":"object","required":["source"],"title":"ImportManifest","description":"The adapter's structured output. Persisted alongside content_imports."},"ImportRequest":{"properties":{"source":{"$ref":"#/components/schemas/ImportSource"},"source_zip_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Source Zip Url"},"source_github_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Source Github Url"}},"type":"object","required":["source"],"title":"ImportRequest","description":"Body for POST /dashboard/projects/{pid}/content/imports.\n\nExactly one of source_zip_url / source_github_url is required — same rule as\nthe DB CHECK constraint in migration 157."},"ImportSource":{"type":"string","enum":["figma_make","lovable","v0","bolt"],"title":"ImportSource"},"ImportStatus":{"type":"string","enum":["pending","extracting","parsing","parsed","importing","applied","failed"],"title":"ImportStatus","description":"Lifecycle statuses on content_imports.\n\nTwo-step lifecycle:\n  1. start_import() drives pending → extracting → parsing → parsed.\n     The manifest is persisted; the row sits at `parsed` until the user\n     calls /imports/{id}/apply with a valid confirm_token.\n  2. apply_import() drives parsed → importing → applied. Errors at any\n     stage flip to `failed` with an error_message.\n\nMirrors the CHECK constraint on content_imports.status (migration 158)."},"ImportedAsset":{"properties":{"source_path":{"type":"string","maxLength":1024,"minLength":1,"title":"Source Path","description":"Path inside the extracted bundle."},"target_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Target Url","description":"SpiderMedia URL after upload — set by the orchestrator, not the adapter."},"mime_type":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Mime Type"},"size_bytes":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Size Bytes"}},"type":"object","required":["source_path"],"title":"ImportedAsset","description":"A file in the import bundle that needs uploading to SpiderMedia."},"ImportedBlock":{"properties":{"type":{"type":"string","maxLength":32,"minLength":1,"title":"Type"},"component_slug":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Component Slug"},"props":{"type":"object","title":"Props"},"data":{"type":"object","title":"Data"}},"type":"object","required":["type"],"title":"ImportedBlock","description":"One block destined for a content_pages.blocks[] entry.\n\nThe adapter emits these as part of an ImportedPage. The orchestrator turns\nthem into the JSONB rows the renderer reads (BlockType union from content.py)."},"ImportedComponent":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Slug"},"name":{"type":"string","maxLength":256,"minLength":1,"title":"Name"},"framework":{"type":"string","enum":["react","vue","svelte"],"title":"Framework","default":"react"},"source_code":{"type":"string","minLength":1,"title":"Source Code"},"tailwind_classes":{"items":{"type":"string"},"type":"array","title":"Tailwind Classes"},"deps":{"items":{"type":"string"},"type":"array","title":"Deps","description":"External npm deps the component pulls in (e.g. ['framer-motion', 'react-hook-form']). Allowlist-checked at apply time."},"assets":{"items":{"type":"string"},"type":"array","title":"Assets","description":"Asset paths inside the import bundle this component references (rewritten to SpiderMedia URLs at apply time)."},"props_schema":{"type":"object","title":"Props Schema"},"default_props":{"type":"object","title":"Default Props"}},"type":"object","required":["slug","name","source_code"],"title":"ImportedComponent","description":"A Tier 4 component the adapter built from external React/Vue/Svelte source.\n\n`framework` controls how component_build_service.py compiles `source_code`.\n`tailwind_classes` is a hint — the build pipeline runs the locked Tailwind\nconfig over the source regardless and bakes the resulting CSS into the\ncomponent's `css` field, so this list is informational (used for fidelity\nreporting, not the actual compile input)."},"ImportedPage":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Slug"},"title":{"type":"string","maxLength":256,"minLength":1,"title":"Title"},"template":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Template"},"blocks":{"items":{"$ref":"#/components/schemas/ImportedBlock"},"type":"array","title":"Blocks"},"seo_meta":{"type":"object","title":"Seo Meta"}},"type":"object","required":["slug","title"],"title":"ImportedPage"},"IntegrationCreate":{"properties":{"provider_name":{"type":"string","maxLength":100,"minLength":1,"title":"Provider Name"},"key_label":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Key Label"},"credentials":{"type":"object","title":"Credentials"},"daily_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Limit"},"minute_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minute Limit"},"is_primary":{"type":"boolean","title":"Is Primary","default":false},"country_code":{"anyOf":[{"type":"string","maxLength":2},{"type":"null"}],"title":"Country Code"},"spend_limit_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spend Limit Amount"},"spend_limit_period":{"anyOf":[{"type":"string","pattern":"^(daily|weekly|monthly)$"},{"type":"null"}],"title":"Spend Limit Period"},"spend_limit_action":{"anyOf":[{"type":"string","pattern":"^(warn|block)$"},{"type":"null"}],"title":"Spend Limit Action"},"share_with_pool":{"type":"boolean","title":"Share With Pool","default":false}},"type":"object","required":["provider_name","credentials"],"title":"IntegrationCreate","description":"Request schema for creating a new API integration"},"IntegrationListResponse":{"properties":{"integrations":{"items":{"$ref":"#/components/schemas/IntegrationResponse"},"type":"array","title":"Integrations"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["integrations","total"],"title":"IntegrationListResponse","description":"Response for listing integrations"},"IntegrationOverviewResponse":{"properties":{"providers":{"items":{"$ref":"#/components/schemas/ProviderSummary"},"type":"array","title":"Providers"}},"type":"object","required":["providers"],"title":"IntegrationOverviewResponse","description":"Response for provider overview (aggregated stats)"},"IntegrationResponse":{"properties":{"id":{"type":"integer","title":"Id"},"provider_name":{"type":"string","title":"Provider Name"},"provider_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider Label"},"key_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Label"},"key_preview":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Key Preview"},"is_active":{"type":"boolean","title":"Is Active","default":true},"is_primary":{"type":"boolean","title":"Is Primary","default":false},"daily_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Limit"},"daily_count":{"type":"integer","title":"Daily Count","default":0},"minute_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minute Limit"},"minute_count":{"type":"integer","title":"Minute Count","default":0},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"health_status":{"type":"string","title":"Health Status","default":"healthy"},"consecutive_failures":{"type":"integer","title":"Consecutive Failures","default":0},"priority":{"type":"integer","title":"Priority","default":0},"country_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Code"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"spend_limit_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spend Limit Amount"},"spend_limit_period":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spend Limit Period"},"spend_limit_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spend Limit Action"},"spend_limit_exceeded":{"type":"boolean","title":"Spend Limit Exceeded","default":false},"cached_balance":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cached Balance"},"cached_usage_today":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cached Usage Today"},"cached_usage_month":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cached Usage Month"},"cached_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cached Currency"},"billing_synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Billing Synced At"},"share_with_pool":{"type":"boolean","title":"Share With Pool","default":false},"auth_type":{"type":"string","title":"Auth Type","default":"api_key"},"token_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Token Expires At"},"last_refreshed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Refreshed At"},"base_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Url"},"token_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Status"},"logs_requests_24h":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Logs Requests 24H"},"logs_spend_today":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Logs Spend Today"},"logs_spend_month":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Logs Spend Month"},"logs_last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Logs Last Used At"}},"type":"object","required":["id","provider_name","created_at"],"title":"IntegrationResponse","description":"Response schema for a single API integration"},"IntegrationSpendResponse":{"properties":{"integration_id":{"type":"integer","title":"Integration Id"},"balance":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Balance"},"usage_today":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Today"},"usage_monthly":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Monthly"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"spend_limit_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spend Limit Amount"},"spend_limit_period":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spend Limit Period"},"spend_limit_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spend Limit Action"},"spend_limit_exceeded":{"type":"boolean","title":"Spend Limit Exceeded","default":false},"synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Synced At"},"sync_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sync Error"},"history":{"items":{"$ref":"#/components/schemas/SpendSnapshot"},"type":"array","title":"History","default":[]}},"type":"object","required":["integration_id"],"title":"IntegrationSpendResponse","description":"Detailed spend data for a single integration"},"IntegrationUpdate":{"properties":{"key_label":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Key Label"},"credentials":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Credentials"},"daily_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Limit"},"minute_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minute Limit"},"is_primary":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Primary"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"country_code":{"anyOf":[{"type":"string","maxLength":2},{"type":"null"}],"title":"Country Code"},"spend_limit_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spend Limit Amount"},"spend_limit_period":{"anyOf":[{"type":"string","pattern":"^(daily|weekly|monthly)$"},{"type":"null"}],"title":"Spend Limit Period"},"spend_limit_action":{"anyOf":[{"type":"string","pattern":"^(warn|block)$"},{"type":"null"}],"title":"Spend Limit Action"},"share_with_pool":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Share With Pool"}},"type":"object","title":"IntegrationUpdate","description":"Request schema for updating an API integration"},"IntegrationsSettings":{"properties":{},"additionalProperties":true,"type":"object","title":"IntegrationsSettings"},"InvitationAcceptRequest":{"properties":{"token":{"type":"string","minLength":1,"title":"Token"}},"type":"object","required":["token"],"title":"InvitationAcceptRequest","description":"Accept an invitation by token."},"InvitationAcceptResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"brand_id":{"type":"integer","title":"Brand Id"},"brand_name":{"type":"string","title":"Brand Name"}},"type":"object","required":["success","message","brand_id","brand_name"],"title":"InvitationAcceptResponse","description":"Response after accepting invitation."},"InvitationCreate":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"role":{"allOf":[{"$ref":"#/components/schemas/MembershipRole"}],"default":"member"}},"type":"object","required":["email"],"title":"InvitationCreate","description":"Create an invitation to join a brand."},"InvitationListResponse":{"properties":{"invitations":{"items":{"$ref":"#/components/schemas/InvitationResponse"},"type":"array","title":"Invitations"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["invitations","total"],"title":"InvitationListResponse","description":"List of invitations."},"InvitationResponse":{"properties":{"id":{"type":"integer","title":"Id"},"email":{"type":"string","title":"Email"},"brand_id":{"type":"integer","title":"Brand Id"},"brand_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Name"},"role":{"$ref":"#/components/schemas/MembershipRole"},"status":{"$ref":"#/components/schemas/InvitationStatus"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"invited_by":{"type":"string","title":"Invited By"}},"type":"object","required":["id","email","brand_id","role","status","expires_at","created_at","invited_by"],"title":"InvitationResponse","description":"Invitation details."},"InvitationStatus":{"type":"string","enum":["pending","accepted","expired","canceled"],"title":"InvitationStatus"},"InviteDetailsResponse":{"properties":{"status":{"type":"string","title":"Status"},"provider_name":{"type":"string","title":"Provider Name"},"auth_type":{"type":"string","title":"Auth Type"},"expires_at":{"type":"string","title":"Expires At"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"contributor_email":{"type":"string","title":"Contributor Email"},"requester":{"type":"object","title":"Requester"},"help_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Help Url"}},"type":"object","required":["status","provider_name","auth_type","expires_at","contributor_email","requester"],"title":"InviteDetailsResponse"},"InviteMemberRequest":{"properties":{"email":{"type":"string","maxLength":254,"minLength":3,"title":"Email"},"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"role":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Role"},"groups":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":50},{"type":"null"}],"title":"Groups"},"site_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Site Name"},"login_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Login Url"}},"type":"object","required":["email"],"title":"InviteMemberRequest"},"JobListResponse":{"properties":{"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Total matching jobs"},"page":{"type":"integer","minimum":1.0,"title":"Page","description":"Current page number"},"page_size":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Page Size","description":"Results per page"},"jobs":{"items":{"$ref":"#/components/schemas/JobStatusResponse"},"type":"array","title":"Jobs","description":"Job list"}},"type":"object","required":["total","page","page_size","jobs"],"title":"JobListResponse","description":"Response schema for job list query"},"JobStatus":{"type":"string","enum":["queued","processing","completed","failed","cancelled"],"title":"JobStatus","description":"Job status enumeration"},"JobStatusResponse":{"properties":{"job_id":{"type":"string","minLength":1,"title":"Job Id","description":"Unique job identifier"},"type":{"type":"string","minLength":1,"title":"Type","description":"Job type"},"status":{"type":"string","minLength":1,"title":"Status","description":"Current job status"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10)"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Job creation timestamp"},"queued_at":{"type":"string","format":"date-time","title":"Queued At","description":"Queue timestamp"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At","description":"Processing start time"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"Completion time"},"worker_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Worker Id","description":"Worker that processed the job"},"retry_count":{"type":"integer","minimum":0.0,"title":"Retry Count","description":"Retries attempted","default":0},"max_retries":{"type":"integer","minimum":0.0,"title":"Max Retries","description":"Maximum retries allowed","default":3},"processing_time_seconds":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Processing Time Seconds","description":"Processing duration in seconds"},"pages_crawled":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Pages Crawled","description":"Pages crawled"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Error message if failed"}},"type":"object","required":["job_id","type","status","priority","created_at","queued_at"],"title":"JobStatusResponse","description":"Response schema for job status query"},"JobSubmit":{"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/JobType"}],"description":"Job type: spiderMaps or spiderSite"},"payload":{"type":"object","title":"Payload","description":"Job-specific payload"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = more important)","default":0}},"type":"object","required":["type","payload"],"title":"JobSubmit","description":"Generic job submission (backward compatibility)\n\nFor better API documentation, use the type-specific endpoints:\n- POST /api/v1/jobs/spiderMaps/submit\n- POST /api/v1/jobs/spiderSite/submit"},"JobSubmitResponse":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"Unique job identifier"},"type":{"type":"string","title":"Type","description":"Job type"},"status":{"type":"string","title":"Status","description":"Current job status"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Job creation timestamp"},"from_cache":{"type":"boolean","title":"From Cache","description":"Whether this job was deduplicated from cache","default":false},"message":{"type":"string","title":"Message","description":"Response message"}},"additionalProperties":true,"type":"object","required":["job_id","type","status","created_at","message"],"title":"JobSubmitResponse","description":"Response schema for job submission.\n\n``extra='allow'`` so the Wave 3 slice 4.5 ``guidance:`` block, spliced\nin at runtime by :func:`middleware.format_llm.decorate_response_with_guidance`\nwhen ``?format=llm`` opts in, survives ``response_model`` validation."},"JobType":{"type":"string","enum":["spiderMaps","spiderSite","spiderVerify","spiderPeople","spiderPhone","spiderMapsEnrich","spiderFacebookPage","spiderPublicInstagram","spiderPublicLinkedin","spiderLanding","spiderVideo","spiderMail","spiderCompanyData","spiderVayapin","companyIntel","leadSearch","localSeo","linkedinExtract"],"title":"JobType","description":"Job type enumeration"},"JsRuntime":{"type":"string","enum":["vanilla","web-component","island","none"],"title":"JsRuntime","description":"JavaScript runtime pattern for kind ∈ {interactive, dynamic, extension}.\n\nSet on content_components.js_runtime when kind != 'static'; NULL for static.\n\nvanilla       — inline <script> blocks; no module bundling.\nweb-component — Custom Elements API; HTMLElement-extending custom element.\nisland        — Astro-style hydrated island; partial hydration on view.\nnone          — kind in {dynamic, extension} with server-side rendering only;\n                no client-side JS shipped."},"LabelCreate":{"properties":{"name":{"type":"string","maxLength":50,"minLength":1,"title":"Name"},"color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","title":"Color","default":"#6B7280"}},"type":"object","required":["name"],"title":"LabelCreate","description":"Create a new label."},"LabelListResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"labels":{"items":{"$ref":"#/components/schemas/LabelResponse"},"type":"array","title":"Labels"}},"type":"object","required":["success","labels"],"title":"LabelListResponse","description":"List of labels for a client."},"LabelResponse":{"properties":{"id":{"type":"integer","title":"Id"},"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color"}},"type":"object","required":["id","name","color"],"title":"LabelResponse","description":"A label definition."},"LabelUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":50,"minLength":1},{"type":"null"}],"title":"Name"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"}},"type":"object","title":"LabelUpdate","description":"Update a label."},"Layout":{"properties":{"id":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Id","description":"Stable layout identifier. Used as block.layout. Lowercase kebab-case."},"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name","description":"Human-readable name shown in the editor's layout picker."},"description":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Description","description":"Short description of when to pick this layout."},"preview_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Url","description":"R2 URL of a small preview thumbnail for this layout."}},"additionalProperties":false,"type":"object","required":["id","name"],"title":"Layout","description":"A single layout variant for a component.\n\nStored as an entry in content_components.layouts JSONB array. The\nrenderer picks one via block.layout (the block-instance setting); if\nblock.layout is unset it picks the first entry (default layout)."},"LeadScoring-Input":{"properties":{"icp_fit_grade":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icp Fit Grade","description":"ICP fit grade (A/B/C/D)"},"engagement_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Engagement Score","description":"Engagement score 0-100"}},"type":"object","title":"LeadScoring","description":"Lead scoring data for tracking"},"LeadScoring-Output":{"properties":{"icp_fit_grade":{"type":"string","title":"Icp Fit Grade","default":""},"engagement_score":{"type":"integer","title":"Engagement Score","default":0},"lead_priority":{"type":"string","title":"Lead Priority","default":""},"fit_reasons":{"items":{"type":"string"},"type":"array","title":"Fit Reasons"},"red_flags":{"items":{"type":"string"},"type":"array","title":"Red Flags"},"champ_breakdown":{"$ref":"#/components/schemas/ChampBreakdown"},"decision_maker_quality":{"type":"string","title":"Decision Maker Quality","default":""},"budget_indicators":{"type":"string","title":"Budget Indicators","default":""},"recommended_action":{"type":"string","title":"Recommended Action","default":""},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","title":"LeadScoring","description":"Lead qualification scoring - always present even if empty (for Xano field mapping)"},"LeadSearchRequest":{"properties":{"search_query":{"type":"string","maxLength":255,"minLength":1,"title":"Search Query","description":"What to search for (e.g., 'restaurants', 'plumbers near downtown')"},"country_code":{"type":"string","maxLength":2,"minLength":2,"title":"Country Code","description":"ISO 2-letter country code (used for VayaPin and SpiderMaps)","default":"US"},"location":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Location","description":"Location to search in (e.g., 'Boston, MA'). If omitted, search_query should include the location."},"max_results":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Max Results","description":"Maximum businesses to find (default: 20 for quick results)","default":20},"lang":{"type":"string","title":"Lang","description":"Language code for results","default":"en"},"workflow":{"allOf":[{"$ref":"#/components/schemas/WorkflowConfig"}],"description":"Workflow pipeline configuration. Defaults to full pipeline (Maps → Site → Verify → VayaPin)."},"test":{"type":"boolean","title":"Test","description":"Route to test queue (for development)","default":false}},"type":"object","required":["search_query"],"title":"LeadSearchRequest","description":"Request schema for a single-location lead search.\n\nRuns the full workflow: SpiderMaps → SpiderSite → SpiderVerify → VayaPin\nfor a single search query without creating a campaign.\n\nResults are stored in the jobs table and queryable via GET /jobs/{job_id}/results."},"LeadSearchResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"job_id":{"type":"string","title":"Job Id","description":"Job ID to poll for results via GET /jobs/{job_id}/results"},"search_query":{"type":"string","title":"Search Query"},"country_code":{"type":"string","title":"Country Code"},"workflow_flow":{"type":"string","title":"Workflow Flow","description":"WindMill flow that was triggered"},"status":{"type":"string","title":"Status","description":"Initial job status","default":"queued"},"message":{"type":"string","title":"Message","description":"Instructions for the client","default":"Lead search submitted. Poll GET /api/v1/jobs/{job_id}/results for results."}},"type":"object","required":["job_id","search_query","country_code","workflow_flow"],"title":"LeadSearchResponse","description":"Response after submitting a lead search."},"LlmsTxtExtensionConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"structure":{"type":"string","enum":["minimal","blog-only","fastapi-style","mintlify"],"title":"Structure","default":"fastapi-style"},"include_authors":{"type":"boolean","title":"Include Authors","default":false},"max_items_per_section":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Max Items Per Section","default":100},"custom_intro":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Custom Intro"},"full_enabled":{"type":"boolean","title":"Full Enabled","default":false}},"type":"object","title":"LlmsTxtExtensionConfig","description":"`content_settings.extensions.llms_txt` shape (MP-LIB-6.3).\n\nDrives the `/llms.txt` document for LLM crawlers per llmstxt.org / the\nde-facto Mintlify convention. The `structure` preset selects which content\ntypes appear and how they're grouped; `custom_intro` lets a tenant inject\na paragraph between the blockquote tagline and the first H2 section."},"LocationImportItem":{"properties":{"search_string":{"type":"string","title":"Search String","description":"Search string for Google Maps (e.g., 'Paris, France')"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Human-readable display name"},"latitude":{"anyOf":[{"type":"number","maximum":90.0,"minimum":-90.0},{"type":"null"}],"title":"Latitude"},"longitude":{"anyOf":[{"type":"number","maximum":180.0,"minimum":-180.0},{"type":"null"}],"title":"Longitude"},"admin_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Region"},"population":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Population"}},"type":"object","required":["search_string"],"title":"LocationImportItem","description":"Single location item for import."},"LocationImportRequest":{"properties":{"country_code":{"type":"string","maxLength":2,"minLength":2,"title":"Country Code","description":"ISO 2-letter country code"},"country_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Name","description":"Full country name (auto-detected if not provided)"},"location_type":{"type":"string","enum":["city","postcode"],"title":"Location Type","description":"Type of locations being imported","default":"city"},"parent_city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent City","description":"Parent city (for postcode imports)"},"locations":{"items":{"$ref":"#/components/schemas/LocationImportItem"},"type":"array","minItems":1,"title":"Locations","description":"List of locations to import"}},"type":"object","required":["country_code","locations"],"title":"LocationImportRequest","description":"Request to import multiple locations."},"LocationImportResponse":{"properties":{"imported":{"type":"integer","title":"Imported","description":"Number of locations successfully imported"},"skipped":{"type":"integer","title":"Skipped","description":"Number of locations skipped (duplicates)","default":0},"errors":{"items":{"type":"string"},"type":"array","title":"Errors","description":"Error messages for failed imports"},"location_ids":{"items":{"type":"integer"},"type":"array","title":"Location Ids","description":"IDs of imported locations"}},"type":"object","required":["imported"],"title":"LocationImportResponse","description":"Response from location import."},"LocationInput":{"properties":{"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City name"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State","description":"State/province"},"country":{"type":"string","title":"Country","description":"Country code (ISO-2)"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code","description":"Postal/ZIP code"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address","description":"Full address"}},"type":"object","required":["country"],"title":"LocationInput","description":"Location for SpiderMaps discovery."},"LocationListResponse":{"properties":{"locations":{"items":{"$ref":"#/components/schemas/LocationResponse"},"type":"array","title":"Locations"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"total_pages":{"type":"integer","title":"Total Pages"}},"type":"object","required":["locations","total","page","page_size","total_pages"],"title":"LocationListResponse","description":"Response for listing locations."},"LocationResponse":{"properties":{"location_id":{"type":"integer","title":"Location Id"},"country_code":{"type":"string","title":"Country Code"},"country_name":{"type":"string","title":"Country Name"},"search_string":{"type":"string","title":"Search String"},"location_type":{"type":"string","title":"Location Type"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"parent_city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent City"},"admin_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Region"},"latitude":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latitude"},"longitude":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Longitude"},"population":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Population"},"needs_postcodes":{"type":"boolean","title":"Needs Postcodes","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["location_id","country_code","country_name","search_string","location_type","created_at"],"title":"LocationResponse","description":"Schema for location response."},"LocationStats":{"properties":{"total_locations":{"type":"integer","title":"Total Locations"},"total_countries":{"type":"integer","title":"Total Countries"},"total_cities":{"type":"integer","title":"Total Cities"},"total_postcodes":{"type":"integer","title":"Total Postcodes"},"cities_needing_postcodes":{"type":"integer","title":"Cities Needing Postcodes"},"top_countries":{"items":{"$ref":"#/components/schemas/CountryStats"},"type":"array","title":"Top Countries"}},"type":"object","required":["total_locations","total_countries","total_cities","total_postcodes","cities_needing_postcodes","top_countries"],"title":"LocationStats","description":"Overall location statistics."},"LogprobContent":{"properties":{"token":{"type":"string","title":"Token"},"logprob":{"type":"number","title":"Logprob"},"bytes":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Bytes"},"top_logprobs":{"items":{"$ref":"#/components/schemas/TopLogprob"},"type":"array","title":"Top Logprobs"}},"type":"object","required":["token","logprob"],"title":"LogprobContent","description":"Logprob for a content token."},"MailMessageFull":{"properties":{"id":{"type":"integer","title":"Id"},"mailbox_id":{"type":"integer","title":"Mailbox Id"},"message_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message Id"},"in_reply_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"In Reply To"},"thread_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thread Id"},"direction":{"type":"string","title":"Direction"},"from_address":{"type":"string","title":"From Address"},"to_addresses":{"items":{},"type":"array","title":"To Addresses"},"cc_addresses":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Cc Addresses"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"body_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Text"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html"},"date":{"type":"string","format":"date-time","title":"Date"},"is_read":{"type":"boolean","title":"Is Read"},"is_starred":{"type":"boolean","title":"Is Starred"},"labels":{"items":{},"type":"array","title":"Labels"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"attachment_count":{"type":"integer","title":"Attachment Count","description":"Number of attachments","default":0},"attachments":{"anyOf":[{"items":{"$ref":"#/components/schemas/AttachmentSummary"},"type":"array"},{"type":"null"}],"title":"Attachments","description":"Attachment summaries with previews"}},"type":"object","required":["id","mailbox_id","message_id","in_reply_to","thread_id","direction","from_address","to_addresses","cc_addresses","subject","body_text","body_html","date","is_read","is_starred","labels","created_at"],"title":"MailMessageFull","description":"Full email message with body and attachments."},"MailMessageSummary":{"properties":{"id":{"type":"integer","title":"Id"},"thread_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thread Id"},"direction":{"type":"string","title":"Direction"},"from_address":{"type":"string","title":"From Address"},"to_addresses":{"items":{},"type":"array","title":"To Addresses"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"body_text_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Text Preview","description":"First 200 chars of body"},"date":{"type":"string","format":"date-time","title":"Date"},"is_read":{"type":"boolean","title":"Is Read"},"is_starred":{"type":"boolean","title":"Is Starred"},"labels":{"items":{},"type":"array","title":"Labels"},"attachment_count":{"type":"integer","title":"Attachment Count","description":"Number of attachments","default":0},"mailbox_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mailbox Id","description":"Mailbox id (master view only)"},"mailbox_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mailbox Email","description":"Mailbox email (master view only)"}},"type":"object","required":["id","thread_id","direction","from_address","to_addresses","subject","date","is_read","is_starred","labels"],"title":"MailMessageSummary","description":"Summary of one email message (for inbox listing)."},"MailMessageUpdate":{"properties":{"is_read":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Read"},"is_starred":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Starred"},"labels":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Labels"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"MailMessageUpdate","description":"Update message flags and notes."},"MailProvider":{"type":"string","enum":["zoho","google_workspace","outlook"],"title":"MailProvider","description":"Supported email providers."},"MailSearchResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"email":{"type":"string","title":"Email"},"query":{"type":"string","title":"Query"},"total":{"type":"integer","title":"Total"},"messages":{"items":{"$ref":"#/components/schemas/MailMessageSummary"},"type":"array","title":"Messages"}},"type":"object","required":["email","query","total","messages"],"title":"MailSearchResponse","description":"Response for message search."},"MailTemplateCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Template name (unique per client)"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Template description"},"template_type":{"allOf":[{"$ref":"#/components/schemas/MailTemplateType"}],"description":"Template type: signature, header, layout, or full","default":"full"},"html_source":{"type":"string","minLength":1,"title":"Html Source","description":"Jinja2 HTML template source"},"text_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Source","description":"Optional plain text template"},"variables":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Variables","description":"Variable names used in template (auto-detected if not provided)"},"is_default":{"type":"boolean","title":"Is Default","description":"Set as default for this template type","default":false}},"type":"object","required":["name","html_source"],"title":"MailTemplateCreate","description":"Create a new email template.","example":{"description":"Welcome email for new contacts","html_source":"<html><body><h1>Welcome {{ name }}!</h1><p>{{ body }}</p></body></html>","name":"welcome-email","template_type":"full","variables":["name","body"]}},"MailTemplateListResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"total":{"type":"integer","title":"Total"},"templates":{"items":{"$ref":"#/components/schemas/MailTemplateResponse"},"type":"array","title":"Templates"}},"type":"object","required":["total","templates"],"title":"MailTemplateListResponse","description":"List templates response."},"MailTemplatePreviewRequest":{"properties":{"variables":{"type":"object","title":"Variables","description":"Variable values for rendering preview"}},"type":"object","title":"MailTemplatePreviewRequest","description":"Request to preview a template with sample data.\n\nField name note: `variables` is the spec-facing name (see SpiderMail\nStage 6b spec + the Phase 6b dashboard TemplatePreview modal) and\n`template_data` is the legacy name kept for back-compat with callers\nthat predate 2026-04-21. Both deserialize to the same dict; one or\nthe other may be supplied per request.","example":{"variables":{"body":"Thank you for your interest in our services.","company_name":"Acme Corp","name":"John Doe"}}},"MailTemplatePreviewResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"rendered_html":{"type":"string","title":"Rendered Html"},"rendered_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rendered Text"},"variables_used":{"items":{"type":"string"},"type":"array","title":"Variables Used","default":[]},"missing_variables":{"items":{"type":"string"},"type":"array","title":"Missing Variables","default":[]}},"type":"object","required":["rendered_html"],"title":"MailTemplatePreviewResponse","description":"Rendered template preview."},"MailTemplateResponse":{"properties":{"id":{"type":"integer","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"template_type":{"type":"string","title":"Template Type"},"html_source":{"type":"string","title":"Html Source"},"text_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Source"},"variables":{"items":{"type":"string"},"type":"array","title":"Variables","default":[]},"is_active":{"type":"boolean","title":"Is Active","default":true},"is_default":{"type":"boolean","title":"Is Default","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","template_type","html_source","created_at","updated_at"],"title":"MailTemplateResponse","description":"Template details (returned from API)."},"MailTemplateType":{"type":"string","enum":["signature","header","layout","full"],"title":"MailTemplateType","description":"Types of email templates."},"MailTemplateUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"template_type":{"anyOf":[{"$ref":"#/components/schemas/MailTemplateType"},{"type":"null"}]},"html_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Html Source"},"text_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Source"},"variables":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Variables"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"is_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Default"}},"type":"object","title":"MailTemplateUpdate","description":"Update an existing template."},"MailViewCreate":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"color":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Color"},"filter_config":{"$ref":"#/components/schemas/ViewFilterConfig"},"column_config":{"items":{"$ref":"#/components/schemas/ViewColumn"},"type":"array","title":"Column Config"},"sort_by":{"type":"string","maxLength":50,"minLength":1,"title":"Sort By","default":"date"},"sort_direction":{"type":"string","pattern":"^(ASC|DESC)$","title":"Sort Direction","default":"DESC"},"is_shared":{"type":"boolean","title":"Is Shared","default":false}},"type":"object","required":["name","filter_config"],"title":"MailViewCreate"},"MailViewListResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"views":{"items":{"$ref":"#/components/schemas/MailViewResponse"},"type":"array","title":"Views"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["views","total"],"title":"MailViewListResponse"},"MailViewResponse":{"properties":{"id":{"type":"integer","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"name":{"type":"string","title":"Name"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"filter_config":{"type":"object","title":"Filter Config"},"column_config":{"items":{},"type":"array","title":"Column Config"},"sort_by":{"type":"string","title":"Sort By"},"sort_direction":{"type":"string","title":"Sort Direction"},"is_shared":{"type":"boolean","title":"Is Shared"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"is_owner":{"type":"boolean","title":"Is Owner","description":"True if the caller created this view."},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","client_id","name","color","filter_config","column_config","sort_by","sort_direction","is_shared","created_by","is_owner","created_at","updated_at"],"title":"MailViewResponse"},"MailViewUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"color":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Color"},"filter_config":{"anyOf":[{"$ref":"#/components/schemas/ViewFilterConfig"},{"type":"null"}]},"column_config":{"anyOf":[{"items":{"$ref":"#/components/schemas/ViewColumn"},"type":"array"},{"type":"null"}],"title":"Column Config"},"sort_by":{"anyOf":[{"type":"string","maxLength":50,"minLength":1},{"type":"null"}],"title":"Sort By"},"sort_direction":{"anyOf":[{"type":"string","pattern":"^(ASC|DESC)$"},{"type":"null"}],"title":"Sort Direction"},"is_shared":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Shared"}},"type":"object","title":"MailViewUpdate"},"MailboxCreate":{"properties":{"email_address":{"type":"string","title":"Email Address","description":"Email address","example":"alice@company.com"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Display name for the sender"},"provider":{"allOf":[{"$ref":"#/components/schemas/MailProvider"}],"description":"Email provider"},"imap_host":{"type":"string","title":"Imap Host","description":"IMAP server hostname"},"imap_port":{"type":"integer","title":"Imap Port","description":"IMAP server port","default":993},"imap_username":{"type":"string","title":"Imap Username","description":"IMAP login username"},"imap_password":{"type":"string","title":"Imap Password","description":"IMAP login password (will be encrypted)"},"smtp_host":{"type":"string","title":"Smtp Host","description":"SMTP server hostname"},"smtp_port":{"type":"integer","title":"Smtp Port","description":"SMTP server port","default":587},"smtp_username":{"type":"string","title":"Smtp Username","description":"SMTP login username"},"smtp_password":{"type":"string","title":"Smtp Password","description":"SMTP login password (will be encrypted)"}},"type":"object","required":["email_address","provider","imap_host","imap_username","imap_password","smtp_host","smtp_username","smtp_password"],"title":"MailboxCreate","description":"Register a new email mailbox."},"MailboxResponse":{"properties":{"id":{"type":"integer","title":"Id"},"email_address":{"type":"string","title":"Email Address"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"provider":{"type":"string","title":"Provider"},"imap_host":{"type":"string","title":"Imap Host"},"imap_port":{"type":"integer","title":"Imap Port"},"smtp_host":{"type":"string","title":"Smtp Host"},"smtp_port":{"type":"integer","title":"Smtp Port"},"is_active":{"type":"boolean","title":"Is Active"},"last_poll_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Poll At"},"last_poll_uid":{"type":"integer","title":"Last Poll Uid"},"poll_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Poll Error"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","email_address","display_name","provider","imap_host","imap_port","smtp_host","smtp_port","is_active","last_poll_at","last_poll_uid","poll_error","created_at"],"title":"MailboxResponse","description":"Mailbox details (passwords excluded)."},"MailboxTestResult":{"properties":{"email_address":{"type":"string","title":"Email Address"},"imap_ok":{"type":"boolean","title":"Imap Ok"},"imap_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imap Error"},"smtp_ok":{"type":"boolean","title":"Smtp Ok"},"smtp_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Smtp Error"}},"type":"object","required":["email_address","imap_ok","smtp_ok"],"title":"MailboxTestResult","description":"Result of testing IMAP/SMTP connectivity."},"MailboxUpdate":{"properties":{"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Display name for From header"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Enable/disable mailbox polling"},"default_template_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Default Template Id","description":"Default template ID for this mailbox"},"template_variables":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Template Variables","description":"Default template variables (e.g. sender_name, company_name)"}},"type":"object","title":"MailboxUpdate","description":"Update mailbox settings including template defaults.","example":{"default_template_id":1,"display_name":"Alice Smith","template_variables":{"company_name":"Acme Corp","phone":"+1 555-123-4567","sender_name":"Alice Smith","title":"Sales Director"}}},"ManageBookingResponse":{"properties":{"booking_id":{"type":"string","format":"uuid","title":"Booking Id"},"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"cal_booking_uid":{"type":"string","maxLength":120,"minLength":1,"title":"Cal Booking Uid"},"status":{"type":"string","maxLength":32,"minLength":1,"title":"Status"},"slot_start":{"type":"string","format":"date-time","title":"Slot Start"},"slot_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Slot End"},"cal_meeting_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Cal Meeting Url"},"flow_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Flow Name"},"customer_timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Customer Timezone"},"contact_email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Contact Email"},"contact_first_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Contact First Name"},"contact_last_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Contact Last Name"},"can_reschedule":{"type":"boolean","title":"Can Reschedule","default":true},"can_cancel":{"type":"boolean","title":"Can Cancel","default":true}},"additionalProperties":false,"type":"object","required":["booking_id","flow_id","cal_booking_uid","status","slot_start"],"title":"ManageBookingResponse","description":"Payload returned by GET /booking/manage/{token}."},"ManageCancelBody":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Reason"}},"additionalProperties":false,"type":"object","title":"ManageCancelBody","description":"POST /booking/manage/{token}/cancel body."},"ManageRescheduleBody":{"properties":{"new_slot_start":{"type":"string","format":"date-time","title":"New Slot Start"},"new_slot_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"New Slot End"},"new_staff_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"New Staff Id"}},"additionalProperties":false,"type":"object","required":["new_slot_start"],"title":"ManageRescheduleBody","description":"POST /booking/manage/{token}/reschedule body."},"MapProviderEntry":{"properties":{"browser_key_encrypted":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Browser Key Encrypted"},"configured_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Configured At"},"configured_by":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Configured By"}},"additionalProperties":true,"type":"object","title":"MapProviderEntry","description":"One provider's per-tenant entry in `content_settings.map_providers`.\n\n`browser_key_encrypted` is the Fernet-encrypted ciphertext written by\n`marketplace_provider_keys.encrypt_browser_key`. Plaintext keys are\nNEVER persisted — the dashboard PATCH endpoint encrypts before write."},"MapProvidersConfig":{"properties":{"google_places":{"anyOf":[{"$ref":"#/components/schemas/MapProviderEntry"},{"type":"null"}]},"mapbox":{"anyOf":[{"$ref":"#/components/schemas/MapProviderEntry"},{"type":"null"}]},"google":{"anyOf":[{"$ref":"#/components/schemas/MapProviderEntry"},{"type":"null"}]},"leaflet":{"anyOf":[{"$ref":"#/components/schemas/MapProviderEntry"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"MapProvidersConfig","description":"`content_settings.map_providers` JSONB shape."},"MarkAllReadResponse":{"properties":{"brand_id":{"type":"integer","minimum":1.0,"title":"Brand Id"},"cleared":{"type":"integer","minimum":0.0,"title":"Cleared"}},"type":"object","required":["brand_id","cleared"],"title":"MarkAllReadResponse"},"MarkReadResponse":{"properties":{"id":{"type":"integer","title":"Id"},"marked":{"type":"boolean","title":"Marked"}},"type":"object","required":["id","marked"],"title":"MarkReadResponse"},"MarketplaceCategory":{"properties":{"slug":{"type":"string","title":"Slug","description":"URL slug (e.g. 'order-forms', 'geo-menu'). Primary key."},"title":{"type":"string","title":"Title","description":"Human-readable title shown in the dashboard sidebar + page header."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"One-line description shown above the gallery."},"asset_type":{"type":"string","title":"Asset Type","description":"Which asset class powers this page: 'site-template', 'bg-video', or 'component'."},"marketplace_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Marketplace Category","description":"For asset_type='component', the value compared against content_components.marketplace_category. Ignored for the other two types."},"default_tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Tag","description":"Optional default tag filter applied to the API list call."},"catalog_wave":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Catalog Wave","description":"Catalog wave this category lives in. Informational."},"row_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Row Count","description":"Live count of published global components with this marketplace_category. Only populated when include_counts=true."}},"type":"object","required":["slug","title","asset_type"],"title":"MarketplaceCategory","description":"A single marketplace category row."},"MarketplaceCategoryList":{"properties":{"categories":{"items":{"$ref":"#/components/schemas/MarketplaceCategory"},"type":"array","title":"Categories"}},"type":"object","required":["categories"],"title":"MarketplaceCategoryList"},"MarketplaceSearchItem":{"properties":{"asset_type":{"type":"string","enum":["bg_video","component","site_template"],"title":"Asset Type"},"id":{"type":"string","title":"Id"},"slug":{"type":"string","title":"Slug"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Url","description":"Best-available preview: bg_video.poster_url, component.preview_thumbnail_url, or site_template.preview_thumbnail_url."},"mood":{"items":{"type":"string"},"type":"array","title":"Mood"},"palette":{"items":{"type":"string"},"type":"array","title":"Palette"},"brand_fit_tags":{"items":{"type":"string"},"type":"array","title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scene Type"},"agent_meta":{"type":"object","title":"Agent Meta"},"is_featured":{"type":"boolean","title":"Is Featured","default":false}},"type":"object","required":["asset_type","id","slug","name"],"title":"MarketplaceSearchItem","description":"One row of the cross-table /marketplace/search result.\n\nLean projection — every asset type maps to the same shape so an agent\nthat does ``results | group_by(.asset_type)`` gets predictable fields."},"MarketplaceSearchResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/MarketplaceSearchItem"},"type":"array","title":"Results"},"total":{"type":"integer","minimum":0.0,"title":"Total","description":"Total rows matched across all asset tables BEFORE the limit."},"limit":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Limit"}},"type":"object","required":["results","total","limit"],"title":"MarketplaceSearchResponse","description":"Response shape for GET /content/marketplace/search."},"MasterSessionAlert":{"properties":{"type":{"type":"string","title":"Type"},"board":{"type":"string","title":"Board"},"task":{"type":"string","title":"Task"},"task_id":{"type":"string","title":"Task Id"},"detail":{"type":"string","title":"Detail"}},"type":"object","required":["type","board","task","task_id","detail"],"title":"MasterSessionAlert"},"MasterSessionResponse":{"properties":{"view":{"type":"string","title":"View","default":"master_session"},"board_type":{"type":"string","title":"Board Type"},"client_id":{"type":"string","title":"Client Id","default":""},"boards":{"type":"integer","title":"Boards","default":0},"total_tasks":{"type":"integer","title":"Total Tasks","default":0},"active_tasks":{"type":"integer","title":"Active Tasks","default":0},"board_summary":{"items":{"$ref":"#/components/schemas/BoardMeta"},"type":"array","title":"Board Summary"},"alerts":{"items":{"$ref":"#/components/schemas/MasterSessionAlert"},"type":"array","title":"Alerts"},"all_tasks_by_status":{"additionalProperties":{"items":{"$ref":"#/components/schemas/MasterTaskItem"},"type":"array"},"type":"object","title":"All Tasks By Status"}},"type":"object","required":["board_type"],"title":"MasterSessionResponse"},"MasterTaskItem":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"priority":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Priority"},"board":{"$ref":"#/components/schemas/TaskBoardRef"},"column":{"$ref":"#/components/schemas/TaskColumnRef"},"assignee":{"anyOf":[{"$ref":"#/components/schemas/TaskAssigneeRef"},{"type":"null"}]},"labels":{"items":{"type":"string"},"type":"array","title":"Labels"},"due_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due Date"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"comment_count":{"type":"integer","title":"Comment Count","default":0},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"custom_fields":{"type":"object","title":"Custom Fields"},"profile":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Profile"}},"type":"object","required":["id","title","status","board","column","created_at","updated_at"],"title":"MasterTaskItem"},"MasterViewGroupBy":{"type":"string","enum":["status","board","priority","assignee"],"title":"MasterViewGroupBy"},"MasterViewResponse":{"properties":{"view":{"type":"string","title":"View","default":"master"},"board_type":{"type":"string","title":"Board Type"},"client_id":{"type":"string","title":"Client Id"},"boards_included":{"type":"integer","title":"Boards Included","default":0},"total_tasks":{"type":"integer","title":"Total Tasks","default":0},"groups":{"items":{"$ref":"#/components/schemas/TaskGroup"},"type":"array","title":"Groups"},"board_summary":{"items":{"$ref":"#/components/schemas/BoardMeta"},"type":"array","title":"Board Summary"}},"type":"object","required":["board_type","client_id"],"title":"MasterViewResponse"},"MasterViewSort":{"type":"string","enum":["priority_desc","priority_asc","due_date_asc","due_date_desc","created_at_desc","updated_at_desc","lead_score_desc","lead_score_asc"],"title":"MasterViewSort"},"MediaAnalyticsBreakdown":{"properties":{"by_kind":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Kind","description":"Asset count per kind (image|video|doc)"},"by_tier":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Tier","description":"Asset count per storage tier (seaweedfs|r2|peertube)"}},"type":"object","title":"MediaAnalyticsBreakdown","description":"Catalog composition (counts of non-deleted assets)."},"MediaAnalyticsOverview":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"source":{"type":"string","title":"Source","description":"'peertube' when view data was fetched, 'unavailable' when PeerTube could not be reached"},"totals":{"$ref":"#/components/schemas/MediaAnalyticsTotals"},"top_assets":{"items":{"$ref":"#/components/schemas/MediaAnalyticsTopAsset"},"type":"array","title":"Top Assets","description":"Most-viewed assets (capped at 10)"},"breakdown":{"$ref":"#/components/schemas/MediaAnalyticsBreakdown"},"vs_platform":{"anyOf":[{"$ref":"#/components/schemas/MediaVsPlatform"},{"type":"null"}],"description":"vs-platform-average comparison from the daily snapshot rollup (4.3a, additive)"}},"type":"object","required":["source","totals","breakdown"],"title":"MediaAnalyticsOverview","description":"Envelope for ``GET /analytics/overview`` — the library rollup.\n\nThe 4.2 contract keys (``success``/``source``/``totals``/``top_assets``/\n``breakdown``) are FROZEN. ``vs_platform`` is ADDITIVE (4.3a) and defaults to\n``None`` so any consumer that ignores it is unaffected."},"MediaAnalyticsTopAsset":{"properties":{"asset_id":{"type":"string","title":"Asset Id","description":"Catalog asset UUID"},"peertube_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peertube Uuid","description":"PeerTube video uuid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"PeerTube video name, or a title derived from the object key"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Cumulative views; null if PeerTube unavailable"},"viewers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Viewers","description":"Current live viewers; null if PeerTube unavailable"}},"type":"object","required":["asset_id"],"title":"MediaAnalyticsTopAsset","description":"One row of the most-viewed list."},"MediaAnalyticsTotals":{"properties":{"video_count":{"type":"integer","minimum":0.0,"title":"Video Count","description":"Number of analysable video assets (kind=video, has peertube_uuid)"},"videos_with_data":{"type":"integer","minimum":0.0,"title":"Videos With Data","description":"How many of video_count actually returned view metrics (coverage; < video_count when the library holds unlisted/off-channel videos)","default":0},"total_views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Views","description":"Sum of PeerTube views over the videos with data; null if PeerTube unavailable"},"current_viewers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current Viewers","description":"Sum of current live viewers; null if PeerTube unavailable"}},"type":"object","required":["video_count"],"title":"MediaAnalyticsTotals","description":"Library-wide rollup numbers."},"MediaAssetAnalytics":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"asset_id":{"type":"string","title":"Asset Id","description":"Catalog asset UUID"},"kind":{"type":"string","title":"Kind","description":"Asset class: image | video | doc"},"peertube_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peertube Uuid","description":"PeerTube video uuid (video assets only)"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Cumulative views; null for non-video or when PeerTube unavailable"},"viewers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Viewers","description":"Current live viewers; null for non-video or when PeerTube unavailable"},"engagement_available":{"type":"boolean","title":"Engagement Available","description":"True when the asset has a PeerTube engagement surface (i.e. a video)"},"source":{"type":"string","title":"Source","description":"'peertube' | 'unavailable' | 'not_applicable'"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Display title (PeerTube name or key-derived)"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Catalog lifecycle status"},"storage_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Storage Tier","description":"seaweedfs | r2 | peertube"},"duration_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration S","description":"Video duration in seconds"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"Asset creation timestamp (UTC)"}},"type":"object","required":["asset_id","kind","engagement_available","source"],"title":"MediaAssetAnalytics","description":"Envelope for ``GET /analytics/assets/{asset_id}`` — per-asset engagement.\n\n``engagement_available`` is False for non-video assets (analytics is\nvideo-centric for now); ``source`` is one of ``peertube`` (fetched),\n``unavailable`` (video, but PeerTube unreachable → views null), or\n``not_applicable`` (non-video / no PeerTube backing)."},"MediaAssetCountsResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"total":{"type":"integer","title":"Total","description":"Total non-deleted assets matching the filters"},"by_kind":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Kind","description":"Count per kind (image | video | doc)"},"by_tier":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Tier","description":"Count per storage tier (seaweedfs | r2 | peertube)"}},"type":"object","required":["total"],"title":"MediaAssetCountsResponse","description":"Aggregate counts over the WHOLE catalog (FX2a counts surface).\n\nThe true per-kind / per-tier / total counts the dashboard Library's filter\nchips need — computed server-side over every matching row rather than\nclient-side over a newest-N page (which under-counted everything past the\nfirst page). Honours the same filters as the list endpoint."},"MediaAssetListResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"count":{"type":"integer","title":"Count","description":"Number of assets in this page"},"limit":{"type":"integer","title":"Limit","description":"Page size applied (clamped [1, 500])"},"offset":{"type":"integer","title":"Offset","description":"Pagination offset applied"},"assets":{"items":{"$ref":"#/components/schemas/MediaAssetResponse"},"type":"array","title":"Assets","description":"Assets, newest first"}},"type":"object","required":["count","limit","offset"],"title":"MediaAssetListResponse","description":"Envelope for the list endpoint — page of assets + pagination echo."},"MediaAssetResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Asset UUID (catalog primary key)"},"kind":{"type":"string","title":"Kind","description":"Asset class: image | video | doc"},"status":{"type":"string","title":"Status","description":"pending | processing | ready | failed"},"storage_tier":{"type":"string","title":"Storage Tier","description":"seaweedfs | r2 | peertube"},"key":{"type":"string","title":"Key","description":"Object key / path within the storage tier"},"bucket":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bucket","description":"S3/R2 bucket; NULL for peertube"},"peertube_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peertube Uuid","description":"PeerTube short-uuid for video assets"},"mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type","description":"MIME type"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes","description":"Size in bytes"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width","description":"Pixel width (image/video)"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height","description":"Pixel height (image/video)"},"duration_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration S","description":"Duration in seconds (video)"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder","description":"DAM folder path"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"DAM tags"},"metadata":{"type":"object","title":"Metadata","description":"Free-form JSONB metadata"},"source_worker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Worker","description":"Worker/agent that produced the asset"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"User/agent attribution"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"Creation timestamp (UTC)"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At","description":"Last-update timestamp (UTC)"}},"type":"object","required":["id","kind","status","storage_tier","key"],"title":"MediaAssetResponse","description":"One row of the per-tenant ``media_assets`` catalog (read shape).\n\nMirrors the columns of ``norm_cli_<tenant>.media_assets`` (migration 294),\nminus internal lifecycle noise (``deleted_at`` is always NULL on reads).\nNullable descriptive/dimension columns stay ``Optional`` so a sparse asset\n(e.g. a doc with no width/height) serializes cleanly."},"MediaAssetSearchResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query","description":"The substring query, if supplied"},"count":{"type":"integer","title":"Count","description":"Number of matching assets"},"limit":{"type":"integer","title":"Limit","description":"Page size applied (clamped [1, 500])"},"assets":{"items":{"$ref":"#/components/schemas/MediaAssetResponse"},"type":"array","title":"Assets","description":"Matches, newest first"}},"type":"object","required":["count","limit"],"title":"MediaAssetSearchResponse","description":"Envelope for the search endpoint — matches + the query echo."},"MediaAssetTrendResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"asset_id":{"type":"string","title":"Asset Id","description":"Catalog asset UUID"},"range":{"type":"string","title":"Range","description":"Requested window: 7d | 30d | 90d"},"points":{"items":{"$ref":"#/components/schemas/MediaTrendPoint"},"type":"array","title":"Points","description":"Daily series ascending by date"}},"type":"object","required":["asset_id","range"],"title":"MediaAssetTrendResponse","description":"Envelope for ``GET /analytics/assets/{id}/trend`` — the time series.\n\n``points`` IS the ``[{date, views, viewers}]`` series the 4.3-FE chart reads,\nascending by date. It's ``[]`` for a non-video asset, a video never\nsnapshotted, or a brand-new video on day-0 (trends accrue FORWARD — PeerTube\nhas no historical per-day series, so day-1 is a single point and fills in\nnightly). A missing asset is a 404, not an empty series."},"MediaAssetUpdate":{"properties":{"folder":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Folder","description":"DAM folder path"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":64},{"type":"null"}],"title":"Tags","description":"Replace the asset's tag list (≤64 tags)"},"status":{"anyOf":[{"type":"string","pattern":"^(pending|processing|ready|failed)$"},{"type":"null"}],"title":"Status","description":"Lifecycle status: pending | processing | ready | failed"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata","description":"Metadata keys to merge (shallow ||)"},"alt_text":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Alt Text","description":"Convenience — folds into metadata.alt"}},"type":"object","title":"MediaAssetUpdate","description":"PATCH body for a catalog asset (Phase 2.6 dashboard write surface).\n\nAll fields optional — a true PATCH: only the keys present in the request body\nare changed (the route reads ``model_fields_set`` to tell \"omitted\" from\n\"explicitly null\"). Used SOLELY by the dashboard-auth write route; the Bearer\n1.1 surface stays read-only. Only the mutable, user-facing columns are\nexposed — ``kind``/``storage_tier``/``key``/``bucket``/dimensions are owned by\nthe upload paths and are not editable here.\n\n``metadata`` is merged shallowly server-side (editing ``alt`` preserves\n``content_hash`` etc.); ``alt_text`` is a convenience that folds into\n``metadata.alt``."},"MediaAssetWatchtime":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 2xx","default":true},"asset_id":{"type":"string","title":"Asset Id","description":"Catalog asset UUID"},"peertube_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peertube Uuid","description":"PeerTube video uuid; null for non-video assets"},"source":{"type":"string","title":"Source","description":"'peertube' (fetched / empty-state) | 'unavailable' (LLM11/PeerTube down)"},"engagement_available":{"type":"boolean","title":"Engagement Available","description":"True when the asset has a PeerTube engagement surface (i.e. a video with a uuid)"},"overall":{"anyOf":[{"$ref":"#/components/schemas/MediaWatchtimeOverall"},{"type":"null"}],"description":"Overall watch-time block; null when unavailable or non-video"},"timeseries":{"anyOf":[{"$ref":"#/components/schemas/MediaWatchtimeSeries"},{"type":"null"}],"description":"Daily viewers + watch-time series; null when unavailable or non-video"}},"type":"object","required":["asset_id","source","engagement_available"],"title":"MediaAssetWatchtime","description":"Envelope for ``GET /analytics/assets/{asset_id}/watchtime`` — owner-stats\nwatch-time / retention (4.3b), proxied from PeerTube via the LLM11 import-api.\n\n``source`` is ``\"peertube\"`` when stats were fetched (or the empty-state for a\nnon-video asset) and ``\"unavailable\"`` when LLM11/PeerTube could not be reached\n— in which case ``overall`` and ``timeseries`` are ``None`` (NEVER a 500).\n``engagement_available`` is ``False`` for a non-video / no-uuid asset, in which\ncase ``overall``/``timeseries`` are also ``None`` (a graceful empty-state, not\nan error)."},"MediaDerivativeResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always true on a 200","default":true},"url":{"type":"string","title":"Url","description":"CDN URL of the derivative on R2"},"cache_hit":{"type":"boolean","title":"Cache Hit","description":"True if served from cache (no derive)"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width","description":"Derivative width in px"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height","description":"Derivative height in px"},"fmt":{"type":"string","title":"Fmt","description":"Produced format (avif may fall back to webp)"},"bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bytes","description":"Derivative size in bytes"}},"type":"object","required":["url","cache_hit","fmt"],"title":"MediaDerivativeResponse","description":"Result of ``GET /catalog/assets/{id}/derivative`` — the cached/derived\nvariant's CDN URL + its dimensions, format, and byte size."},"MediaListResponse":{"properties":{"media":{"items":{"$ref":"#/components/schemas/MediaResponse"},"type":"array","title":"Media"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page","default":1},"page_size":{"type":"integer","title":"Page Size","default":50}},"type":"object","required":["media","total"],"title":"MediaListResponse","description":"List of media files."},"MediaResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"filename":{"type":"string","title":"Filename"},"original_name":{"type":"string","title":"Original Name"},"r2_key":{"type":"string","title":"R2 Key"},"r2_url":{"type":"string","title":"R2 Url"},"mime_type":{"type":"string","title":"Mime Type"},"file_size":{"type":"integer","title":"File Size"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height"},"folder":{"type":"string","title":"Folder"},"alt_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alt Text"},"caption":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Caption"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","filename","original_name","r2_key","r2_url","mime_type","file_size","folder","created_at"],"title":"MediaResponse","description":"Media file response."},"MediaTrendPoint":{"properties":{"date":{"type":"string","format":"date","title":"Date","description":"The snapshot day (UTC)"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Cumulative views recorded that day; null if the snapshot couldn't read PeerTube"},"viewers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Viewers","description":"Live viewers recorded that day; null if unread"}},"type":"object","required":["date"],"title":"MediaTrendPoint","description":"One day of the per-asset trend series."},"MediaUpdate":{"properties":{"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder"},"alt_text":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Alt Text"},"caption":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Caption"}},"type":"object","title":"MediaUpdate","description":"Update media metadata."},"MediaVsPlatform":{"properties":{"client_avg_views":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Client Avg Views","description":"This tenant's mean views per video (latest snapshot per video); null on day-0"},"platform_avg_views":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Platform Avg Views","description":"Mean views per video across ALL tenants; null only when the whole rollup is empty"},"percentile":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Percentile","description":"This tenant's rank among all tenants by avg views/video, 0-100; null on day-0"}},"type":"object","title":"MediaVsPlatform","description":"vs-platform-average comparison (4.3a), computed from the daily snapshot\nrollup (``public.media_stats_daily``). All three are ``None`` when the tenant\nhas no snapshots yet (day-0) — a fresh tenant gets a null block, never an\nerror."},"MediaWatchtimeCountry":{"properties":{"country":{"type":"string","title":"Country","description":"ISO country name/code as PeerTube reports it"},"viewers":{"type":"integer","minimum":0.0,"title":"Viewers","description":"Viewers from this country","default":0}},"type":"object","required":["country"],"title":"MediaWatchtimeCountry","description":"One country row of the overall watch-time breakdown."},"MediaWatchtimeOverall":{"properties":{"total_watch_time_s":{"type":"integer","minimum":0.0,"title":"Total Watch Time S","description":"Total watch time across all viewers, in seconds","default":0},"average_watch_time_s":{"type":"integer","minimum":0.0,"title":"Average Watch Time S","description":"Average watch time per viewer, in seconds","default":0},"total_viewers":{"type":"integer","minimum":0.0,"title":"Total Viewers","description":"Total distinct viewers","default":0},"viewers_peak":{"type":"integer","minimum":0.0,"title":"Viewers Peak","description":"Peak concurrent viewers","default":0},"viewers_peak_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Viewers Peak Date","description":"ISO timestamp of the viewer peak; null if none"},"countries":{"items":{"$ref":"#/components/schemas/MediaWatchtimeCountry"},"type":"array","title":"Countries","description":"Per-country viewer breakdown; may be empty"}},"type":"object","title":"MediaWatchtimeOverall","description":"The overall watch-time block (4.3b). Watch-time is normalized to SECONDS."},"MediaWatchtimePoint":{"properties":{"date":{"type":"string","title":"Date","description":"UTC calendar day, YYYY-MM-DD"},"value":{"type":"integer","minimum":0.0,"title":"Value","description":"Daily sum (viewers, or watch-time seconds)","default":0}},"type":"object","required":["date"],"title":"MediaWatchtimePoint","description":"One day of a watch-time/viewers timeseries (PeerTube intervals aggregated to UTC days)."},"MediaWatchtimeSeries":{"properties":{"viewers":{"items":{"$ref":"#/components/schemas/MediaWatchtimePoint"},"type":"array","title":"Viewers","description":"Daily viewers series, ascending by date"},"watch_time_s":{"items":{"$ref":"#/components/schemas/MediaWatchtimePoint"},"type":"array","title":"Watch Time S","description":"Daily watch-time (seconds) series, ascending by date"}},"type":"object","title":"MediaWatchtimeSeries","description":"The two daily timeseries (4.3b). ``watch_time_s`` is from PeerTube's\n``aggregateWatchTime`` metric, normalized to seconds."},"MemberGateContextResponse":{"properties":{"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"},"project_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Uuid"},"edge_auth_enabled":{"type":"boolean","title":"Edge Auth Enabled","default":false},"edge_auth_global":{"type":"boolean","title":"Edge Auth Global","default":true}},"type":"object","title":"MemberGateContextResponse","description":"Login Initiative C / C2 — the per-request context the CF edge gate needs.\n\nThe renderer fetches this (cached in Worker KV) only when a page's `access`\nis non-public, to (a) decide whether the gate is live and (b) run the §15 R4\n`resolve_project(host) == jwt.project_id` isolation assertion.\n\nThe gate enforces `edge_auth_global AND edge_auth_enabled AND access != public`.\nBoth flags default to the inert combination (global armed, per-tenant off), so\nthe gate is a no-op until a tenant opts in."},"MembershipRole":{"type":"string","enum":["owner","admin","member"],"title":"MembershipRole"},"MembershipStatus":{"type":"string","enum":["active","pending","suspended"],"title":"MembershipStatus"},"MessageRole":{"type":"string","enum":["system","user","assistant","tool","function"],"title":"MessageRole","description":"Valid message roles in a chat conversation."},"ModelInfo":{"properties":{"id":{"type":"string","title":"Id","description":"Model identifier"},"object":{"const":"model","title":"Object","default":"model"},"created":{"type":"integer","title":"Created","description":"Unix timestamp"},"owned_by":{"type":"string","title":"Owned By","description":"Organization that owns the model"},"permission":{"items":{"$ref":"#/components/schemas/ModelPermission"},"type":"array","title":"Permission"},"root":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Root"},"parent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent"},"spidergate_info":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Spidergate Info","description":"SpiderGate metadata: provider, context_window, pricing, capabilities, etc."}},"type":"object","required":["id","created","owned_by"],"title":"ModelInfo","description":"Model information."},"ModelListResponse":{"properties":{"object":{"const":"list","title":"Object","default":"list"},"data":{"items":{"$ref":"#/components/schemas/ModelInfo"},"type":"array","title":"Data"}},"type":"object","required":["data"],"title":"ModelListResponse","description":"Model list response."},"ModelPermission":{"properties":{"id":{"type":"string","title":"Id"},"object":{"const":"model_permission","title":"Object","default":"model_permission"},"created":{"type":"integer","title":"Created"},"allow_create_engine":{"type":"boolean","title":"Allow Create Engine","default":false},"allow_sampling":{"type":"boolean","title":"Allow Sampling","default":true},"allow_logprobs":{"type":"boolean","title":"Allow Logprobs","default":true},"allow_search_indices":{"type":"boolean","title":"Allow Search Indices","default":false},"allow_view":{"type":"boolean","title":"Allow View","default":true},"allow_fine_tuning":{"type":"boolean","title":"Allow Fine Tuning","default":false},"organization":{"type":"string","title":"Organization","default":"*"},"group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group"},"is_blocking":{"type":"boolean","title":"Is Blocking","default":false}},"type":"object","required":["id","created"],"title":"ModelPermission","description":"Model permission entry."},"ModemHeartbeatData":{"properties":{"imei":{"type":"string","title":"Imei"},"public_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Ip"},"signal_strength":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Signal Strength"},"status":{"$ref":"#/components/schemas/ProxyModemStatus"},"is_healthy":{"type":"boolean","title":"Is Healthy"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"requests_since_last_heartbeat":{"type":"integer","title":"Requests Since Last Heartbeat","default":0},"bytes_since_last_heartbeat":{"type":"integer","title":"Bytes Since Last Heartbeat","default":0},"speed_test":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Speed Test","description":"Speed test result: {download_mbps, upload_mbps, latency_ms, jitter_ms, test_server, success, error_message}"}},"type":"object","required":["imei","public_ip","signal_strength","status","is_healthy"],"title":"ModemHeartbeatData","description":"Modem status in heartbeat"},"Mood":{"type":"string","enum":["calm","energetic","bold","confident","dreamy","futuristic","urban","minimal","warm","sensory","editorial","professional","friendly","clear","technical","credible"],"title":"Mood","description":"Universal axis — multi-value mood vocabulary.\n\nStored in mood TEXT[] columns on content_components / content_bg_videos /\ncontent_site_templates. Multi-value: a clip can be both \"calm\" and \"dreamy\".\n\nAdd new values here AND in the agent help endpoint output. DB doesn't enforce."},"MySubscription":{"properties":{"id":{"type":"string","minLength":1,"title":"Id"},"tariff_id":{"type":"string","minLength":1,"title":"Tariff Id"},"tariff_version_id":{"type":"integer","minimum":1.0,"title":"Tariff Version Id"},"tariff_name":{"type":"string","minLength":1,"title":"Tariff Name"},"product":{"type":"string","minLength":1,"title":"Product"},"monthly_price_usd":{"type":"number","minimum":0.0,"title":"Monthly Price Usd"},"status":{"type":"string","enum":["active","trialing","past_due","cancelled","paused"],"title":"Status"},"period_start":{"type":"string","title":"Period Start"},"period_end":{"type":"string","title":"Period End"},"usage_bars":{"items":{"$ref":"#/components/schemas/MyUsageBar"},"type":"array","title":"Usage Bars"},"dunning_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dunning Message"},"stripe_subscription_item_id":{"type":"string","title":"Stripe Subscription Item Id","default":""},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End","default":false}},"type":"object","required":["id","tariff_id","tariff_version_id","tariff_name","product","monthly_price_usd","status","period_start","period_end"],"title":"MySubscription"},"MyUsageBar":{"properties":{"service_type":{"type":"string","maxLength":64,"minLength":1,"title":"Service Type"},"service_label":{"type":"string","maxLength":64,"minLength":1,"title":"Service Label"},"window_label":{"type":"string","maxLength":32,"minLength":1,"title":"Window Label"},"used":{"type":"integer","minimum":0.0,"title":"Used"},"cap":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Cap"},"unit":{"type":"string","enum":["jobs","usd"],"title":"Unit","default":"jobs"}},"type":"object","required":["service_type","service_label","window_label","used"],"title":"MyUsageBar","description":"One usage bar per (service, window). ``cap=None`` means unlimited\n(the UI draws no bar)."},"NavItem-Input":{"properties":{"label":{"type":"string","minLength":1,"title":"Label"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon"},"children":{"items":{"$ref":"#/components/schemas/NavItem-Input"},"type":"array","title":"Children","default":[]},"is_external":{"type":"boolean","title":"Is External","default":false},"badge":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Badge"}},"type":"object","required":["label"],"title":"NavItem","description":"Navigation menu item."},"NavItem-Output":{"properties":{"label":{"type":"string","minLength":1,"title":"Label"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon"},"children":{"items":{"$ref":"#/components/schemas/NavItem-Output"},"type":"array","title":"Children","default":[]},"is_external":{"type":"boolean","title":"Is External","default":false},"badge":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Badge"}},"type":"object","required":["label"],"title":"NavItem","description":"Navigation menu item."},"NavigationLocation":{"type":"string","enum":["header","footer","docs_sidebar"],"title":"NavigationLocation"},"NavigationResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"location":{"$ref":"#/components/schemas/NavigationLocation"},"items":{"items":{"$ref":"#/components/schemas/NavItem-Output"},"type":"array","title":"Items"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["id","location","items"],"title":"NavigationResponse","description":"Navigation menu response.\n\n`updated_at` is Optional because the dashboard read handler returns a\nsynthetic empty NavigationResponse when no nav row exists yet for the\ntenant (graceful empty-state UX) — in that case there's no real\ntimestamp to surface. W1 (usability report 3, 2026-05-24) — the field\nused to be required and the dashboard fallback at\n`app/api/v1/dashboard_content.py:1637` blew up with a pydantic 500."},"NavigationUpdate":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NavItem-Input"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"NavigationUpdate","description":"Update navigation menu."},"NoShowRateResult":{"properties":{"business_id":{"type":"string","maxLength":64,"minLength":1,"title":"Business Id"},"since":{"type":"string","format":"date-time","title":"Since"},"no_show_count":{"type":"integer","minimum":0.0,"title":"No Show Count"},"completed_count":{"type":"integer","minimum":0.0,"title":"Completed Count"},"no_show_rate":{"type":"number","maximum":1.0,"minimum":0.0,"title":"No Show Rate"}},"additionalProperties":false,"type":"object","required":["business_id","since","no_show_count","completed_count","no_show_rate"],"title":"NoShowRateResult","description":"no_show_rate(business_id, since) response shape.\n\n``no_show_rate = no_show / (completed + no_show)``; returns 0.0 when the\ndenominator is 0 (never NaN)."},"NotificationDndRequest":{"properties":{"timezone":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Timezone","description":"IANA timezone identifier (e.g. 'Europe/Berlin')"},"dnd_start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dnd Start","description":"Quiet-hours start in HH:MM (24h)"},"dnd_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dnd End","description":"Quiet-hours end in HH:MM (24h)"},"dnd_days_off":{"anyOf":[{"type":"integer","maximum":127.0,"minimum":0.0},{"type":"null"}],"title":"Dnd Days Off","description":"Bitmap: Mon=1, Tue=2, Wed=4, Thu=8, Fri=16, Sat=32, Sun=64"},"clear_window":{"type":"boolean","title":"Clear Window","description":"When true, set dnd_start AND dnd_end to NULL","default":false}},"additionalProperties":false,"type":"object","title":"NotificationDndRequest","description":"Partial-update DND fields on dashboard_users.\n\nAll fields are optional — absent fields preserve the existing value.\nTo clear quiet hours, send `clear_window=true` (sending\n`dnd_start=null` alone is ambiguous between \"absent\" and \"clear\",\nso the explicit flag wins). dnd_days_off is the Mon=1 … Sun=64\nbitmap (range 0-127)."},"NotificationDndResponse":{"properties":{"timezone":{"type":"string","title":"Timezone"},"dnd_start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dnd Start"},"dnd_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dnd End"},"dnd_days_off":{"type":"integer","title":"Dnd Days Off"}},"type":"object","required":["timezone","dnd_start","dnd_end","dnd_days_off"],"title":"NotificationDndResponse","description":"Current DND state after the update."},"NotificationsSettings":{"properties":{"email_enabled":{"type":"boolean","title":"Email Enabled","default":true},"sms_enabled":{"type":"boolean","title":"Sms Enabled","default":false}},"additionalProperties":true,"type":"object","title":"NotificationsSettings"},"OAuthStartResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"},"state":{"type":"string","title":"State"}},"type":"object","required":["auth_url","state"],"title":"OAuthStartResponse"},"OpenApiImportRequest":{"properties":{"source_url":{"anyOf":[{"type":"string","maxLength":2048,"minLength":1},{"type":"null"}],"title":"Source Url","description":"HTTPS/HTTP URL of the OpenAPI/Swagger spec. Fetched server-side with SSRF protection."},"spec_content":{"anyOf":[{"type":"string","maxLength":5242880,"minLength":1},{"type":"null"}],"title":"Spec Content","description":"Raw spec text (JSON or YAML) — alternative to source_url."},"spec_format":{"type":"string","pattern":"^(auto|json|yaml)$","title":"Spec Format","description":"Spec body format. 'auto' parses JSON or YAML.","default":"auto"},"section":{"type":"string","maxLength":120,"minLength":1,"title":"Section","description":"Slug of the docs section the reference is generated under (importer-owned).","default":"api-reference"},"publish":{"type":"boolean","title":"Publish","description":"Publish the generated docs immediately (default: create as drafts for review).","default":false},"dry_run":{"type":"boolean","title":"Dry Run","description":"Preview the planned section + pages without writing anything.","default":false}},"type":"object","title":"OpenApiImportRequest","description":"Import an OpenAPI 3.x / Swagger 2.0 spec into the tenant's docs tree.\n\nProvide EXACTLY ONE of ``source_url`` (fetched SSRF-safely) or\n``spec_content`` (pasted JSON/YAML). The generated reference is written under\nthe ``section`` slug (importer-owned subtree); re-import is idempotent."},"OpenSearchExtensionConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"short_name":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Short Name"},"description":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Description"},"search_url_template":{"type":"string","maxLength":500,"title":"Search Url Template","default":"/search?q={searchTerms}"},"image_url":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Image Url"}},"type":"object","title":"OpenSearchExtensionConfig","description":"`content_settings.extensions.opensearch` shape (MP-LIB-6.3).\n\nDrives the OpenSearch description served at `/opensearch.xml` for browser\nsearch-engine integration. Every field is optional — sensible fallbacks\nderived from `content_settings.site_name` / `site_tagline` / `favicon_url`\nkeep the surface working with zero per-tenant config."},"OutputFormat":{"type":"string","enum":["json","yaml"],"title":"OutputFormat","description":"Output format for mail endpoints."},"OverviewStatsResponse":{"properties":{"leadsEnrichedToday":{"type":"integer","title":"Leadsenrichedtoday","default":0},"leadsEnrichedDelta":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Leadsenricheddelta"},"verifiedEmails":{"type":"integer","title":"Verifiedemails","default":0},"deliverability":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Deliverability"},"runningJobs":{"type":"integer","title":"Runningjobs","default":0},"runningJobsMix":{"items":{"$ref":"#/components/schemas/RunningJobMix"},"type":"array","title":"Runningjobsmix"},"gateSpend24h":{"type":"number","title":"Gatespend24H","default":0},"gateTokens24h":{"type":"integer","title":"Gatetokens24H","default":0},"gateProviderCount":{"type":"integer","title":"Gateprovidercount","default":0},"gateSparkline":{"items":{"type":"number"},"type":"array","title":"Gatesparkline"},"campaignHealth":{"items":{"$ref":"#/components/schemas/CampaignHealth"},"type":"array","title":"Campaignhealth"}},"type":"object","title":"OverviewStatsResponse"},"PATRequestByEmail":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Admin email address"},"project_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Project Name","description":"Project name"},"requested_scopes":{"items":{"type":"string"},"type":"array","title":"Requested Scopes","description":"Requested permission scopes","default":["jobs:submit","jobs:read","content:read","content:write","content:deploy"]},"ttl_hours":{"type":"integer","maximum":2160.0,"minimum":1.0,"title":"Ttl Hours","default":720},"fingerprint":{"anyOf":[{"$ref":"#/components/schemas/AgentFingerprint"},{"type":"null"}]},"multi_brand":{"type":"boolean","title":"Multi Brand","description":"Issue tokens for all accessible brands (requires CLI >= 0.9.0)","default":false},"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name","description":"Agent display name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Agent description"},"photo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photo","description":"Avatar URL or base64 data URI"},"monthly_budget_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Monthly Budget Usd","description":"Monthly budget cap in USD"},"rate_limit_rpm":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Rate Limit Rpm","description":"Requests per minute limit"},"rate_limit_rpd":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Rate Limit Rpd","description":"Requests per day limit"},"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models","description":"Allowed models (null=all)"}},"type":"object","required":["email"],"title":"PATRequestByEmail","description":"Simplified request - discovers client from admin email."},"PATRequestCreate":{"properties":{"client_id":{"type":"string","title":"Client Id","description":"Client ID to request access for"},"owner_email":{"type":"string","format":"email","title":"Owner Email","description":"Admin email to send approval request to"},"project_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Project Name","description":"Project name (shown in email)"},"requested_scopes":{"items":{"type":"string"},"type":"array","title":"Requested Scopes","description":"Requested permission scopes","default":["jobs:submit","jobs:read","content:read","content:write","content:deploy"]},"ttl_hours":{"type":"integer","maximum":2160.0,"minimum":1.0,"title":"Ttl Hours","description":"Token lifetime in hours after approval (1h–90d, default 30d)","default":720},"fingerprint":{"anyOf":[{"$ref":"#/components/schemas/AgentFingerprint"},{"type":"null"}],"description":"Agent fingerprint for audit trail"},"multi_brand":{"type":"boolean","title":"Multi Brand","description":"Issue tokens for all accessible brands (requires CLI >= 0.9.0)","default":false}},"type":"object","required":["client_id","owner_email"],"title":"PATRequestCreate","description":"Request body for initiating a PAT request."},"PATRequestListItem":{"properties":{"request_id":{"type":"string","title":"Request Id"},"client_id":{"type":"string","title":"Client Id"},"owner_email":{"type":"string","title":"Owner Email"},"project_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Name"},"status":{"$ref":"#/components/schemas/PATRequestStatus"},"fingerprint":{"type":"object","title":"Fingerprint"},"requested_at":{"type":"string","format":"date-time","title":"Requested At"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["request_id","client_id","owner_email","status","requested_at","expires_at"],"title":"PATRequestListItem","description":"Summary of a PAT request for list view."},"PATRequestListResponse":{"properties":{"requests":{"items":{"$ref":"#/components/schemas/PATRequestListItem"},"type":"array","title":"Requests"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["requests","total"],"title":"PATRequestListResponse","description":"Response for listing PAT requests (admin only)."},"PATRequestResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Public request identifier (req_xxx)"},"status":{"allOf":[{"$ref":"#/components/schemas/PATRequestStatus"}],"default":"pending"},"poll_token":{"type":"string","title":"Poll Token","description":"Token for polling status (keep secret)"},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Request expiration time"},"message":{"type":"string","title":"Message","default":"Approval email sent"}},"type":"object","required":["request_id","poll_token","expires_at"],"title":"PATRequestResponse","description":"Response after initiating a PAT request."},"PATRequestStatus":{"type":"string","enum":["pending","active","denied","expired","revoked"],"title":"PATRequestStatus","description":"Status of a PAT request."},"PATStatusResponse":{"properties":{"status":{"$ref":"#/components/schemas/PATRequestStatus"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token","description":"The PAT token (single-brand approval only)"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes","description":"Granted scopes"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration time"},"workspaces":{"anyOf":[{"items":{"$ref":"#/components/schemas/PATWorkspace"},"type":"array"},{"type":"null"}],"title":"Workspaces","description":"One token per accessible brand (only when multi_brand was requested)"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At","description":"When admin decided"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Status message"}},"type":"object","required":["status"],"title":"PATStatusResponse","description":"Response when polling for approval status."},"PATWorkspace":{"properties":{"client_id":{"type":"string","title":"Client Id","description":"Brand's client_id (cli_xxx)"},"brand_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Name","description":"Human-readable brand name for CLI output"},"token":{"type":"string","title":"Token","description":"spideriq_pat_* secret for this brand"},"opvs_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opvs Address","description":"Agent's OPVS handle (shared across brands)"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration (NULL = no-expiry)"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"}},"type":"object","required":["client_id","token"],"title":"PATWorkspace","description":"One approved workspace — a (brand, token) pair.\n\nMulti-brand approvals return an array of these so the CLI can save every\nbrand as a separate local workspace in one round-trip. Added 2026-04-17\nalongside the multi_brand opt-in flag on PATRequest."},"PageAccessRequest":{"properties":{"access":{"type":"string","maxLength":32,"title":"Access"},"allowed_groups":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":100},{"type":"null"}],"title":"Allowed Groups"}},"type":"object","required":["access"],"title":"PageAccessRequest"},"PageCreate":{"properties":{"slug":{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Slug"},"title":{"type":"string","maxLength":500,"minLength":1,"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"geo_ai_summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Geo Ai Summary","description":"GEO: concise LLM-facing summary, surfaced in /llms.txt + an AI meta tag. Inheritable from an ancestor folder."},"geo_ai_crawlers":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Geo Ai Crawlers","description":"GEO: AI-crawler policy hint (e.g. 'allow' / 'noai'). Inheritable from an ancestor folder."},"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string","pattern":"^(summary|summary_large_image)$"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string","pattern":"^(index|noindex),(follow|nofollow)$"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"blocks":{"items":{"$ref":"#/components/schemas/ContentBlock"},"type":"array","title":"Blocks"},"template":{"type":"string","title":"Template","default":"default"},"collection_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Collection Type","description":"For pages with template ∈ {dynamic_list, dynamic_item}: which collection to bind to. Allowed: posts, docs, directory_listings."},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"},"is_folder":{"type":"boolean","title":"Is Folder","description":"TRUE creates a dashboard-only folder (organization in the Pages list). Folders have no live URL and never appear in nav/sitemap/llms.","default":false}},"type":"object","required":["slug","title"],"title":"PageCreate","description":"Create a new page."},"PageListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"status":{"$ref":"#/components/schemas/ContentStatus"},"template":{"type":"string","title":"Template"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"creator_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Id"},"creator_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Name"},"creator_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Email"},"creator_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Image Url"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"is_folder":{"type":"boolean","title":"Is Folder","default":false},"last_edited_by_actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Edited By Actor Id"},"last_edited_by_actor_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Edited By Actor Role"},"last_edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Edited At"},"is_locked":{"type":"boolean","title":"Is Locked","default":false}},"type":"object","required":["id","slug","title","status","template","updated_at"],"title":"PageListItem","description":"Page list item (minimal).\n\nCreator fields are populated when the page was created via a session\n(Better Auth user_id stored in `content_pages.created_by`). API/PAT\ncreations leave `created_by` NULL today, so creator_* come back NULL\nfor those rows; the frontend renders a placeholder. Resolving PAT\ncreations to the PAT owner_email is a follow-up — see\ndocs/services/Frontend/audit-nav-shell/01-pages-tab.md Q1."},"PageListResponse":{"properties":{"pages":{"items":{"$ref":"#/components/schemas/PageListItem"},"type":"array","title":"Pages"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page","default":1},"page_size":{"type":"integer","title":"Page Size","default":50}},"type":"object","required":["pages","total"],"title":"PageListResponse","description":"List of pages with pagination."},"PageLockRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason","description":"Why the page is being locked (surfaced in 423 responses)."}},"type":"object","title":"PageLockRequest","description":"Body for POST /pages/{id}/lock.\n\n`reason` is surfaced in 423 Locked responses so the next session knows\nWHY they can't edit (\"client review week of 2026-05-12\"). Free-form text,\ncapped to keep the response envelope reasonable."},"PageLockResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"is_locked":{"type":"boolean","title":"Is Locked"},"locked_by_actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked By Actor Id"},"locked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Locked At"},"locked_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked Reason"}},"type":"object","required":["id","slug","is_locked"],"title":"PageLockResponse","description":"Response from POST /pages/{id}/lock + /unlock — the page row state\nAFTER the lock toggle. Subset of PageResponse focused on lock provenance."},"PageLockedErrorDetail":{"properties":{"error":{"const":"page_locked","title":"Error","default":"page_locked"},"message":{"type":"string","title":"Message"},"locked_by_actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked By Actor Id"},"locked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Locked At"},"locked_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked Reason"},"unlock_endpoint":{"type":"string","title":"Unlock Endpoint","description":"Where to POST to release the lock (paths matching the dual-mounted Phase 11+12 prefix)."}},"type":"object","required":["message","unlock_endpoint"],"title":"PageLockedErrorDetail","description":"423 Locked envelope returned when a mutation hits a locked page.\n\nMirrors the dry_run preview shape — agents that already understand\nconfirm-token envelopes can read this without retraining."},"PageMarkdownImport":{"properties":{"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string","pattern":"^(summary|summary_large_image)$"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string","pattern":"^(index|noindex),(follow|nofollow)$"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"markdown":{"type":"string","maxLength":262144,"minLength":1,"title":"Markdown","description":"Markdown source (≤256 KB). Prose → rich_text blocks; `:::component{...}` → component blocks."},"page_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Page Id","description":"Existing page to replace blocks on; omit to create."},"slug":{"anyOf":[{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$"},{"type":"null"}],"title":"Slug","description":"Flat slug — required when creating, ignored when updating (slug is immutable)."},"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title","description":"Required when creating; updates the title when set on an existing page."},"template":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Template","description":"Page template (create only); defaults to 'default'."},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"}},"additionalProperties":false,"type":"object","required":["markdown"],"title":"PageMarkdownImport","description":"Import a page from a Markdown body (Docs Platform v2 · 2.2) — the user's\nmarkdown→page converter.\n\nCreate a NEW page (omit ``page_id``, provide ``slug`` + ``title``) or REPLACE\nan existing page's blocks (set ``page_id``). Prose runs become ``rich_text``\nblocks and each ``:::component{...}`` directive becomes a ``component`` block\n(``content_markdown.markdown_to_page_blocks``). SEO fields from SeoMixin are\noptional and applied like ``PageCreate``."},"PagePreviewBlock":{"properties":{"type":{"type":"string","maxLength":128,"minLength":1,"title":"Type"},"data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Data"}},"additionalProperties":true,"type":"object","required":["type"],"title":"PagePreviewBlock","description":"Loose block envelope for the preview path.\n\nThe dashboard editor passes blocks through verbatim from the page row —\nwe don't re-validate per-block schemas here because the editor is the\nauthoritative validator at save time, and a draft block that's halfway\nthrough editing might not satisfy the strict schema yet. The preview\nendpoint accepts them and lets the renderer tolerate or skip whatever\ndoesn't make sense."},"PagePreviewRequest":{"properties":{"blocks":{"items":{"$ref":"#/components/schemas/PagePreviewBlock"},"type":"array","maxItems":200,"title":"Blocks","description":"In-flight blocks array from the editor. May be empty. May differ from the saved page row. Capped at 200 to bound payload size."},"draft_mode":{"type":"boolean","title":"Draft Mode","description":"Always true today — preview is unconditionally a draft render.","default":true}},"additionalProperties":false,"type":"object","title":"PagePreviewRequest","description":"Body for ``POST /api/v1/dashboard/content/pages/{page_id}/preview``.\n\n`blocks` is the in-flight blocks array — possibly empty, possibly\ndiverging from the persisted page row. `draft_mode` is informational\ntoday (renderer always treats preview as draft) but exists in the\ncontract so the frontend can flip it later without a schema bump."},"PagePreviewResponse":{"properties":{"preview_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Preview Url","description":"Absolute URL the iframe should load. Token-gated, public, no-store, expires within 15 minutes."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Absolute UTC timestamp at which the underlying token expires. Editor uses this to decide when to refetch the URL."}},"additionalProperties":false,"type":"object","required":["preview_url","expires_at"],"title":"PagePreviewResponse","description":"Response for ``POST /api/v1/dashboard/content/pages/{page_id}/preview``.\n\nThe iframe in the editor takes ``preview_url`` directly as ``src``;\nthe URL is same-origin (`app.spideriq.ai`) so the iframe can mount\ninside the dashboard without a cross-origin handshake."},"PageResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"blocks":{"items":{"$ref":"#/components/schemas/ContentBlock"},"type":"array","title":"Blocks"},"status":{"$ref":"#/components/schemas/ContentStatus"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"template":{"type":"string","title":"Template"},"collection_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Collection Type"},"sort_order":{"type":"integer","title":"Sort Order"},"is_folder":{"type":"boolean","title":"Is Folder","default":false},"seo_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"geo_ai_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Geo Ai Summary"},"geo_ai_crawlers":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Geo Ai Crawlers"},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"},"access":{"type":"string","title":"Access","default":"public"},"allowed_groups":{"items":{"type":"string"},"type":"array","title":"Allowed Groups"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"last_edited_by_actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Edited By Actor Id"},"last_edited_by_actor_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Edited By Actor Role"},"last_edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Edited At"},"is_locked":{"type":"boolean","title":"Is Locked","default":false},"locked_by_actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked By Actor Id"},"locked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Locked At"},"locked_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locked Reason"},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Url"},"warnings":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Warnings"}},"additionalProperties":true,"type":"object","required":["id","slug","title","blocks","status","template","sort_order","created_at","updated_at"],"title":"PageResponse","description":"Page response with all fields."},"PageRestoreRequest":{"properties":{},"type":"object","title":"PageRestoreRequest","description":"Body for POST /pages/{id}/restore — empty by default. Phase 11+12\ndry_run/confirm_token gate is read from query params, identical to the\npublish/unpublish/delete flow."},"PageUpdate":{"properties":{"geo_ai_summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Geo Ai Summary","description":"GEO: concise LLM-facing summary, surfaced in /llms.txt + an AI meta tag. Inheritable from an ancestor folder."},"geo_ai_crawlers":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Geo Ai Crawlers","description":"GEO: AI-crawler policy hint (e.g. 'allow' / 'noai'). Inheritable from an ancestor folder."},"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Og Title"},"og_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Og Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"twitter_card":{"anyOf":[{"type":"string","pattern":"^(summary|summary_large_image)$"},{"type":"null"}],"title":"Twitter Card"},"twitter_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter Image Url"},"canonical_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Canonical Url"},"robots":{"anyOf":[{"type":"string","pattern":"^(index|noindex),(follow|nofollow)$"},{"type":"null"}],"title":"Robots"},"keywords":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Keywords"},"json_ld":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Ld"},"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"blocks":{"anyOf":[{"items":{"$ref":"#/components/schemas/ContentBlock"},"type":"array"},{"type":"null"}],"title":"Blocks"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template"},"collection_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Collection Type","description":"See PageCreate.collection_type. Pass an empty-string sentinel to clear."},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id","description":"Parent page UUID for tree nesting. Cycle detection enforced server-side."},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"}},"additionalProperties":false,"type":"object","title":"PageUpdate","description":"Update page fields.\n\n``extra='forbid'`` rejects unknown keys with a structured 422 envelope.\nPrior to 2026-05-22, Pydantic's default ``extra='ignore'`` silently\ndropped fields like ``slug`` — the agent saw ``changed_fields: []`` in\ndry_run, consumed the confirm_token, and got a no-op mutation. The\nClaude Code session on 2026-05-22 burned a delete-and-recreate cycle\non this exact trap (usability report F-11).\n\nSlug is genuinely immutable on existing pages (no rename surface yet —\nsee F-13 / P3 roadmap). With ``extra='forbid'`` the agent gets a loud\n422 and a hint to delete + recreate, instead of a silent no-op.\nThe pre-mode model_validator below intercepts the common ``slug`` case\nwith a specific human-readable error message; other unknown keys fall\nthrough to Pydantic's generic ``extra_forbidden`` 422."},"PageVersionDetailResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"version_number":{"type":"integer","title":"Version Number"},"title":{"type":"string","title":"Title"},"changed_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Changed By"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"block_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Block Count"},"blocks_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blocks Size"},"blocks":{"items":{"$ref":"#/components/schemas/ContentBlock"},"type":"array","title":"Blocks","default":[]}},"type":"object","required":["id","version_number","title","created_at"],"title":"PageVersionDetailResponse","description":"Full version snapshot — includes the historical `blocks` body. Returned\nby the restore preview so the editor can show a side-by-side diff."},"PageVersionListResponse":{"properties":{"page_id":{"type":"string","format":"uuid","title":"Page Id"},"versions":{"items":{"$ref":"#/components/schemas/PageVersionResponse"},"type":"array","title":"Versions"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["page_id","versions","total"],"title":"PageVersionListResponse","description":"List of version snapshots for a single page (newest first)."},"PageVersionResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"version_number":{"type":"integer","title":"Version Number"},"title":{"type":"string","title":"Title"},"changed_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Changed By"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"block_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Block Count"},"blocks_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blocks Size"}},"type":"object","required":["id","version_number","title","created_at"],"title":"PageVersionResponse","description":"Page version history item.\n\nP4: returned by GET /pages/{id}/versions. Lists the snapshot log without\nthe heavy `blocks` payload — the editor uses `blocks_size` + `change_summary`\nto render the row, and fetches the body lazily on restore preview."},"PaymentsSettings":{"properties":{"stripe_connected":{"type":"boolean","title":"Stripe Connected","default":false},"default_deposit_pct":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Default Deposit Pct","default":0},"default_cancellation_fee_pct":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Default Cancellation Fee Pct","default":0},"no_show_fee_cents":{"type":"integer","maximum":100000000.0,"minimum":0.0,"title":"No Show Fee Cents","default":0},"tipping_enabled":{"type":"boolean","title":"Tipping Enabled","default":false},"tax_rate_pct":{"type":"number","maximum":100.0,"minimum":0.0,"title":"Tax Rate Pct","default":0.0}},"additionalProperties":true,"type":"object","title":"PaymentsSettings"},"PeopleConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Extract employees from LinkedIn","default":true},"max_employees":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Max Employees","description":"Max employees to extract","default":20},"profile_mode":{"type":"string","title":"Profile Mode","description":"short / full / full_email","default":"short"}},"type":"object","title":"PeopleConfig"},"PersonalizationHooks":{"properties":{"recent_news":{"type":"string","title":"Recent News","default":""},"case_studies":{"items":{"type":"string"},"type":"array","title":"Case Studies"},"company_values":{"type":"string","title":"Company Values","default":""}},"type":"object","title":"PersonalizationHooks","description":"Personalization hooks for outreach - always present even if empty"},"PlacePrediction":{"properties":{"place_id":{"type":"string","maxLength":512,"minLength":1,"title":"Place Id"},"description":{"type":"string","maxLength":512,"minLength":1,"title":"Description"},"primary_text":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Primary Text"},"secondary_text":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Secondary Text"}},"additionalProperties":false,"type":"object","required":["place_id","description"],"title":"PlacePrediction","description":"One autocomplete suggestion."},"PlacesAutocompleteRequest":{"properties":{"query":{"type":"string","maxLength":200,"minLength":1,"title":"Query","description":"The text the user has typed so far."},"session_token":{"type":"string","maxLength":128,"minLength":1,"title":"Session Token","description":"Per-session token Google uses to bill autocomplete + details as one session (cheaper). The renderer generates a UUID per field mount and reuses it across all calls in that session."},"country_bias":{"anyOf":[{"type":"string","maxLength":2,"minLength":2,"pattern":"^[A-Za-z]{2}$"},{"type":"null"}],"title":"Country Bias","description":"ISO 3166-1 alpha-2 country code to bias the results geographically. Case-insensitive (we lowercase before sending)."},"types":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":10},{"type":"null"}],"title":"Types","description":"Optional Google Places `includedPrimaryTypes` filter (e.g. ['address', 'establishment'])."}},"additionalProperties":false,"type":"object","required":["query","session_token"],"title":"PlacesAutocompleteRequest","description":"Input for the autocomplete proxy."},"PlacesAutocompleteResponse":{"properties":{"predictions":{"items":{"$ref":"#/components/schemas/PlacePrediction"},"type":"array","title":"Predictions"}},"additionalProperties":false,"type":"object","required":["predictions"],"title":"PlacesAutocompleteResponse","description":"Wrapped predictions list."},"PlacesDetailsRequest":{"properties":{"place_id":{"type":"string","maxLength":512,"minLength":1,"title":"Place Id"},"session_token":{"type":"string","maxLength":128,"minLength":1,"title":"Session Token"}},"additionalProperties":false,"type":"object","required":["place_id","session_token"],"title":"PlacesDetailsRequest","description":"Input for the details proxy."},"PlacesDetailsResponse":{"properties":{"place_id":{"type":"string","title":"Place Id"},"formatted_address":{"type":"string","title":"Formatted Address"},"address_components":{"items":{"$ref":"#/components/schemas/AddressComponent"},"type":"array","title":"Address Components"},"lat":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lat"},"lng":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lng"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"additionalProperties":false,"type":"object","required":["place_id","formatted_address","address_components"],"title":"PlacesDetailsResponse","description":"Structured place payload — the same shape the `place` field emits."},"PlaygroundRequest":{"properties":{"doc_path":{"type":"string","maxLength":512,"minLength":1,"title":"Doc Path"},"method":{"type":"string","maxLength":10,"minLength":1,"title":"Method"},"path":{"type":"string","maxLength":2048,"minLength":1,"title":"Path"},"query":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Query"},"headers":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Headers"},"body":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}],"title":"Body"}},"type":"object","required":["doc_path","method","path"],"title":"PlaygroundRequest","description":"One relayed \"Try it\" request composed in the docs chrome."},"PoliciesSettings":{"properties":{"cancellation_window_hours":{"type":"integer","maximum":2000.0,"minimum":0.0,"title":"Cancellation Window Hours","default":24},"reschedule_limit":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Reschedule Limit","default":3},"no_show_auto_charge":{"type":"boolean","title":"No Show Auto Charge","default":false},"late_grace_minutes":{"type":"integer","maximum":240.0,"minimum":0.0,"title":"Late Grace Minutes","default":10}},"additionalProperties":true,"type":"object","title":"PoliciesSettings"},"PoolStatsResponse":{"properties":{"total_integrations":{"type":"integer","title":"Total Integrations"},"enabled_integrations":{"type":"integer","title":"Enabled Integrations"},"pool_integrations":{"type":"integer","title":"Pool Integrations"},"private_integrations":{"type":"integer","title":"Private Integrations"},"healthy_integrations":{"type":"integer","title":"Healthy Integrations"},"unhealthy_integrations":{"type":"integer","title":"Unhealthy Integrations"},"total_daily_capacity":{"type":"integer","title":"Total Daily Capacity"},"total_daily_used":{"type":"integer","title":"Total Daily Used"},"capacity_used_percent":{"type":"number","title":"Capacity Used Percent"},"providers":{"items":{"$ref":"#/components/schemas/ProviderSummary"},"type":"array","title":"Providers"}},"type":"object","required":["total_integrations","enabled_integrations","pool_integrations","private_integrations","healthy_integrations","unhealthy_integrations","total_daily_capacity","total_daily_used","capacity_used_percent","providers"],"title":"PoolStatsResponse","description":"Statistics for the shared key pool"},"PostCategoryResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"sort_order":{"type":"integer","title":"Sort Order","default":0},"children":{"items":{"$ref":"#/components/schemas/PostCategoryResponse"},"type":"array","title":"Children","default":[]}},"type":"object","required":["id","name","slug"],"title":"PostCategoryResponse","description":"Blog category."},"PostCreate":{"properties":{"slug":{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9][a-z0-9-]*$","title":"Slug"},"title":{"type":"string","maxLength":500,"minLength":1,"title":"Title"},"excerpt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Excerpt"},"body":{"type":"object","title":"Body","description":"Tiptap JSON document"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"author_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Author Name"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"tag_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Tag Ids"},"category_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Category Ids"},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"featured_image_alt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Featured Image Alt"},"tldr_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tldr Summary"},"related_post_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Related Post Ids"},"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"},"vayapin_pins":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Vayapin Pins","description":"VayaPin pin ids (e.g. ['BB:TAPAS','BB:CHAMPERS']) — render a card strip on the post"}},"type":"object","required":["slug","title","body"],"title":"PostCreate","description":"Create a new blog post."},"PostListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"excerpt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Excerpt"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"status":{"$ref":"#/components/schemas/ContentStatus"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author":{"anyOf":[{"$ref":"#/components/schemas/AuthorSummary"},{"type":"null"}]},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"reading_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reading Time"},"view_count":{"type":"integer","title":"View Count","default":0},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]}},"type":"object","required":["id","slug","title","status"],"title":"PostListItem","description":"Post list item (minimal)."},"PostListResponse":{"properties":{"posts":{"items":{"$ref":"#/components/schemas/PostListItem"},"type":"array","title":"Posts"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page","default":1},"page_size":{"type":"integer","title":"Page Size","default":50}},"type":"object","required":["posts","total"],"title":"PostListResponse","description":"List of posts with pagination."},"PostResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"excerpt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Excerpt"},"body":{"type":"object","title":"Body"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"status":{"$ref":"#/components/schemas/ContentStatus"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"author_avatar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Avatar"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author":{"anyOf":[{"$ref":"#/components/schemas/AuthorSummary"},{"type":"null"}]},"reading_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reading Time"},"view_count":{"type":"integer","title":"View Count","default":0},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"featured_image_alt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Featured Image Alt"},"tldr_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tldr Summary"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"categories":{"items":{"type":"object"},"type":"array","title":"Categories","default":[]},"related_posts":{"items":{"$ref":"#/components/schemas/RelatedPostItem"},"type":"array","title":"Related Posts","default":[]},"vayapin_pins":{"items":{"type":"string"},"type":"array","title":"Vayapin Pins","default":[]},"seo_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seo Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Url"},"warnings":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Warnings"}},"type":"object","required":["id","slug","title","body","status","created_at","updated_at"],"title":"PostResponse","description":"Post response with all fields."},"PostScheduleRequest":{"properties":{"published_at":{"type":"string","format":"date-time","title":"Published At","description":"Future UTC timestamp to auto-publish the post at."}},"type":"object","required":["published_at"],"title":"PostScheduleRequest","description":"Body for POST /posts/{id}/schedule — the future time to auto-publish at."},"PostStatusUpdate":{"properties":{"status":{"$ref":"#/components/schemas/ContentStatus"}},"type":"object","required":["status"],"title":"PostStatusUpdate","description":"Update post status."},"PostUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title"},"slug":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Slug"},"excerpt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Excerpt"},"body":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Body"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"author_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Author Name"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"tag_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Tag Ids"},"category_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Category Ids"},"is_featured":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"},"featured_image_alt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Featured Image Alt"},"tldr_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tldr Summary"},"related_post_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Related Post Ids"},"seo_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Seo Title"},"seo_description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Seo Description"},"og_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url"},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"},"vayapin_pins":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":50},{"type":"null"}],"title":"Vayapin Pins","description":"VayaPin pin ids (e.g. ['BB:TAPAS','BB:CHAMPERS']); omit to leave unchanged, [] to clear"}},"type":"object","title":"PostUpdate","description":"Update post fields."},"PreferenceEvent":{"properties":{"key":{"type":"string","title":"Key"},"label":{"type":"string","title":"Label"},"description":{"type":"string","title":"Description"},"severity":{"type":"string","title":"Severity"},"standalone_only":{"type":"boolean","title":"Standalone Only"},"email":{"type":"boolean","title":"Email"},"in_app":{"type":"boolean","title":"In App"},"delivery":{"type":"string","title":"Delivery"},"source":{"type":"string","title":"Source"}},"type":"object","required":["key","label","description","severity","standalone_only","email","in_app","delivery","source"],"title":"PreferenceEvent","description":"One row in the settings-UI subscription matrix."},"PreferenceSection":{"properties":{"name":{"type":"string","title":"Name"},"events":{"items":{"$ref":"#/components/schemas/PreferenceEvent"},"type":"array","title":"Events"}},"type":"object","required":["name","events"],"title":"PreferenceSection","description":"A section header + its rows in the subscription matrix."},"PreferenceUpdate":{"properties":{"event_key":{"type":"string","maxLength":80,"minLength":1,"title":"Event Key"},"email":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Email"},"in_app":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"In App"},"delivery":{"anyOf":[{"type":"string","maxLength":16,"minLength":1},{"type":"null"}],"title":"Delivery"}},"additionalProperties":false,"type":"object","required":["event_key"],"title":"PreferenceUpdate","description":"One partial override in a PATCH batch."},"PreferencesPatchRequest":{"properties":{"updates":{"items":{"$ref":"#/components/schemas/PreferenceUpdate"},"type":"array","maxItems":200,"minItems":1,"title":"Updates"}},"additionalProperties":false,"type":"object","required":["updates"],"title":"PreferencesPatchRequest","description":"Bulk PATCH body — list of partial overrides."},"PreferencesPatchResponse":{"properties":{"updated":{"items":{"$ref":"#/components/schemas/PreferenceEvent"},"type":"array","title":"Updated"}},"type":"object","required":["updated"],"title":"PreferencesPatchResponse","description":"Echo of the persisted effective values for the touched events."},"PreferencesResponse":{"properties":{"brand_id":{"type":"integer","title":"Brand Id"},"sections":{"items":{"$ref":"#/components/schemas/PreferenceSection"},"type":"array","title":"Sections"}},"type":"object","required":["brand_id","sections"],"title":"PreferencesResponse","description":"Full settings-UI payload for one (user, brand) pair."},"ProcessingTimeEstimate":{"properties":{"typical_seconds":{"type":"integer","title":"Typical Seconds","description":"Typical processing time in seconds"},"min_seconds":{"type":"integer","title":"Min Seconds","description":"Minimum processing time"},"max_seconds":{"type":"integer","title":"Max Seconds","description":"Maximum processing time"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Processing time notes/caveats"}},"type":"object","required":["typical_seconds","min_seconds","max_seconds"],"title":"ProcessingTimeEstimate","description":"Estimated processing times for a skill"},"ProfileUpdate":{"properties":{"firstname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Firstname"},"lastname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lastname"},"mobile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mobile"}},"type":"object","title":"ProfileUpdate","description":"Profile update request."},"ProfileUpdateResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message"],"title":"ProfileUpdateResponse","description":"Profile update response."},"ProjectCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Human-facing project name."},"slug":{"anyOf":[{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},{"type":"null"}],"title":"Slug","description":"URL-safe slug, unique within the workspace. Auto-derived from name when omitted."}},"type":"object","required":["name"],"title":"ProjectCreate","description":"Create a new project in the caller's workspace."},"ProjectListResponse":{"properties":{"projects":{"items":{"$ref":"#/components/schemas/ProjectOut"},"type":"array","title":"Projects"},"total":{"type":"integer","minimum":0.0,"title":"Total"}},"type":"object","required":["projects","total"],"title":"ProjectListResponse"},"ProjectOut":{"properties":{"id":{"type":"string","title":"Id","description":"Project id (proj/uuid) — sent back as the X-Project-Id header."},"slug":{"type":"string","title":"Slug"},"name":{"type":"string","title":"Name"},"is_default":{"type":"boolean","title":"Is Default","description":"The workspace's default project (cannot be deleted)."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"page_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Page Count"},"post_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Post Count"},"doc_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Doc Count"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Hero image — og/logo/favicon."},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"},"last_deployed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Deployed At"}},"type":"object","required":["id","slug","name","is_default","created_at","updated_at"],"title":"ProjectOut","description":"A project row as returned to the dashboard."},"ProjectUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"slug":{"anyOf":[{"type":"string","maxLength":255,"minLength":1,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},{"type":"null"}],"title":"Slug"}},"type":"object","title":"ProjectUpdate","description":"Partial update — rename and/or re-slug a project."},"ProviderField":{"properties":{"name":{"type":"string","title":"Name"},"label":{"type":"string","title":"Label"},"type":{"type":"string","title":"Type"},"required":{"type":"boolean","title":"Required","default":true},"placeholder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Placeholder"}},"type":"object","required":["name","label","type"],"title":"ProviderField","description":"Field definition for provider credential forms"},"ProviderListResponse":{"properties":{"providers":{"items":{"$ref":"#/components/schemas/ProviderTemplate"},"type":"array","title":"Providers"}},"type":"object","required":["providers"],"title":"ProviderListResponse","description":"Response for listing available providers"},"ProviderSpendOverview":{"properties":{"provider_name":{"type":"string","title":"Provider Name"},"total_balance":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Balance"},"total_usage_today":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Usage Today"},"total_usage_month":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Usage Month"},"currency":{"type":"string","title":"Currency","default":"USD"},"keys_over_limit":{"type":"integer","title":"Keys Over Limit","default":0},"keys_with_errors":{"type":"integer","title":"Keys With Errors","default":0},"capabilities":{"items":{"type":"string"},"type":"array","title":"Capabilities","default":[]}},"type":"object","required":["provider_name"],"title":"ProviderSpendOverview","description":"Aggregated spend overview for all keys of a provider"},"ProviderSummary":{"properties":{"provider_name":{"type":"string","title":"Provider Name"},"provider_label":{"type":"string","title":"Provider Label"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"key_count":{"type":"integer","title":"Key Count"},"active_count":{"type":"integer","title":"Active Count"},"healthy_count":{"type":"integer","title":"Healthy Count"},"total_daily_capacity":{"type":"integer","title":"Total Daily Capacity"},"total_daily_used":{"type":"integer","title":"Total Daily Used"},"total_spend_today":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Spend Today"},"total_spend_month":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Spend Month"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"spend_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spend Currency"},"has_billing":{"type":"boolean","title":"Has Billing","default":false}},"type":"object","required":["provider_name","provider_label","key_count","active_count","healthy_count","total_daily_capacity","total_daily_used"],"title":"ProviderSummary","description":"Aggregated stats for a provider across all keys.\n\nSpend / usage / last_used fields are derived from `gate_request_logs`\n(the v3.2.0 litellm.Router flow's source of truth) — NOT the dead\n`api_integrations.daily_count` / `cached_usage_*` columns that the\nv1 `mark_usage` path used to write."},"ProviderTemplate":{"properties":{"name":{"type":"string","title":"Name"},"label":{"type":"string","title":"Label"},"description":{"type":"string","title":"Description"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon"},"fields":{"items":{"$ref":"#/components/schemas/ProviderField"},"type":"array","title":"Fields","default":[]},"defaults":{"type":"object","title":"Defaults","default":{}}},"type":"object","required":["name","label","description"],"title":"ProviderTemplate","description":"Provider template with credential field schema"},"ProvisionRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"backfill_limit":{"type":"integer","minimum":0.0,"title":"Backfill Limit","description":"Max rows per board backfilled from IDAP. 0 (default) means no cap — mirror every active row. Positive values clamp to that count (useful for testing).","default":0}},"type":"object","title":"ProvisionRequest","description":"Optional body for POST /crm/provision. Both fields are optional —\nthe endpoint is idempotent and has sensible defaults."},"ProvisionResponse":{"properties":{"group_id":{"type":"string","format":"uuid","title":"Group Id"},"client_id":{"type":"string","title":"Client Id"},"created":{"type":"boolean","title":"Created"},"boards":{"items":{"$ref":"#/components/schemas/BoardResponse"},"type":"array","title":"Boards"},"backfill":{"type":"object","title":"Backfill"}},"type":"object","required":["group_id","client_id","created"],"title":"ProvisionResponse"},"ProxyDeviceType":{"type":"string","enum":["modem","iphone","any"],"title":"ProxyDeviceType","description":"Proxy device type enumeration"},"ProxyModemBrief":{"properties":{"modem_id":{"type":"string","format":"uuid","title":"Modem Id"},"location_code":{"type":"string","title":"Location Code"},"country_code":{"type":"string","title":"Country Code"},"proxy_host":{"type":"string","title":"Proxy Host","description":"WireGuard IP of the agent"},"proxy_port":{"type":"integer","title":"Proxy Port"},"proxy_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Username"},"proxy_password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Password"},"public_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Ip"},"signal_strength":{"type":"integer","title":"Signal Strength"},"is_healthy":{"type":"boolean","title":"Is Healthy"}},"type":"object","required":["modem_id","location_code","country_code","proxy_host","proxy_port","proxy_username","proxy_password","public_ip","signal_strength","is_healthy"],"title":"ProxyModemBrief","description":"Brief modem info for pool listings"},"ProxyModemStatus":{"type":"string","enum":["online","offline","rotating","error"],"title":"ProxyModemStatus","description":"Modem status enumeration"},"ProxyPoolStats":{"properties":{"total_locations":{"type":"integer","title":"Total Locations"},"online_locations":{"type":"integer","title":"Online Locations"},"total_modems":{"type":"integer","title":"Total Modems"},"online_modems":{"type":"integer","title":"Online Modems"},"healthy_modems":{"type":"integer","title":"Healthy Modems"},"total_iphones":{"type":"integer","title":"Total Iphones","default":0},"online_iphones":{"type":"integer","title":"Online Iphones","default":0},"healthy_iphones":{"type":"integer","title":"Healthy Iphones","default":0},"total_devices":{"type":"integer","title":"Total Devices","default":0},"healthy_devices":{"type":"integer","title":"Healthy Devices","default":0},"total_requests_today":{"type":"integer","title":"Total Requests Today"},"total_rotations_today":{"type":"integer","title":"Total Rotations Today"},"by_country":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Country","description":"Device count by country"},"by_device_type":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Device Type","description":"Device count by type"}},"type":"object","required":["total_locations","online_locations","total_modems","online_modems","healthy_modems","total_requests_today","total_rotations_today"],"title":"ProxyPoolStats","description":"Pool statistics"},"ProxyRequest":{"properties":{"country_code":{"anyOf":[{"type":"string","maxLength":2,"minLength":2},{"type":"null"}],"title":"Country Code","description":"Preferred country"},"location_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Location Id","description":"Specific location (modems only)"},"location_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location Code","description":"Location code filter (e.g., 'ODS')"},"device_type":{"allOf":[{"$ref":"#/components/schemas/ProxyDeviceType"}],"description":"Device type filter","default":"any"},"sticky_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sticky Session Id","description":"Session ID for sticky proxy"},"exclude_modem_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Exclude Modem Ids","description":"Modems to exclude"},"exclude_iphone_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Exclude Iphone Ids","description":"iPhones to exclude"}},"type":"object","title":"ProxyRequest","description":"Request a proxy from the pool"},"ProxyResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"device_type":{"allOf":[{"$ref":"#/components/schemas/ProxyDeviceType"}],"description":"Type of device providing proxy","default":"modem"},"device_id":{"type":"string","title":"Device Id","description":"Device identifier (e.g., SpideriPhone-ODS-01)"},"modem_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Modem Id","description":"Modem UUID (if device_type=modem)"},"iphone_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Iphone Id","description":"iPhone UUID (if device_type=iphone)"},"proxy_url":{"type":"string","title":"Proxy Url","description":"Full proxy URL (http://user:pass@host:port)"},"proxy_host":{"type":"string","title":"Proxy Host"},"proxy_port":{"type":"integer","title":"Proxy Port"},"proxy_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Username"},"proxy_password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Password"},"public_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Ip"},"country_code":{"type":"string","title":"Country Code"},"location_code":{"type":"string","title":"Location Code"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"For sticky sessions"}},"type":"object","required":["success","device_id","proxy_url","proxy_host","proxy_port","proxy_username","proxy_password","public_ip","country_code","location_code"],"title":"ProxyResponse","description":"Proxy assignment response"},"PublishFlowRequest":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Title"},"length_minutes":{"anyOf":[{"type":"integer","maximum":1440.0,"minimum":1.0},{"type":"null"}],"title":"Length Minutes"},"team_id":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Team Id"},"dry_run":{"type":"boolean","title":"Dry Run","default":false},"confirm_token":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirm Token"}},"type":"object","title":"PublishFlowRequest","description":"Body for ``POST /flows/{flow_id}/publish``.\n\n``dry_run`` / ``confirm_token`` gate the destructive mutation via\n:func:`gate_destructive_mutation`. ``title``/``length_minutes``/``team_id``\nare forwarded to :func:`booking_service.provision_event_type` for\n``kind='booking'`` flows.\n\nSpiderFlow P1.Z — Cal.com fields are Optional and IGNORED for\n``kind='form'`` flows. Forms publish by flipping ``status='active'`` and\nsnapshotting; no Cal.com event-type is provisioned. The publish endpoint\nenforces \"Cal.com fields required iff kind='booking'\" — see\n:meth:`publish_booking_flow` for the runtime gate (we can't enforce in\nPydantic because ``kind`` isn't on the request — it's on the DB row)."},"PushLeadsRequest":{"properties":{"spideriq_campaign_id":{"type":"string","maxLength":64,"minLength":1,"title":"Spideriq Campaign Id"},"remote_campaign_id":{"type":"string","maxLength":128,"minLength":1,"title":"Remote Campaign Id"},"smartlead_campaign_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Smartlead Campaign Name"},"limit":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":1.0},{"type":"null"}],"title":"Limit"}},"type":"object","required":["spideriq_campaign_id","remote_campaign_id"],"title":"PushLeadsRequest"},"PushLeadsResponse":{"properties":{"requested":{"type":"integer","title":"Requested"},"pushed":{"type":"integer","title":"Pushed"},"already_pushed":{"type":"integer","title":"Already Pushed"},"provider_skipped":{"type":"integer","title":"Provider Skipped"},"cap_skipped":{"type":"integer","title":"Cap Skipped"},"reconciled_ids":{"type":"integer","title":"Reconciled Ids"},"active_after":{"type":"integer","title":"Active After"},"cap":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cap"}},"type":"object","required":["requested","pushed","already_pushed","provider_skipped","cap_skipped","reconciled_ids","active_after","cap"],"title":"PushLeadsResponse"},"QuotaStatusResponse":{"properties":{"can_create":{"type":"boolean","title":"Can Create"},"reason":{"type":"string","title":"Reason"},"daily_remaining":{"type":"integer","title":"Daily Remaining"},"monthly_remaining":{"type":"integer","title":"Monthly Remaining"},"concurrent_remaining":{"type":"integer","title":"Concurrent Remaining"},"daily_limit":{"type":"integer","title":"Daily Limit"},"monthly_limit":{"type":"integer","title":"Monthly Limit"},"max_concurrent":{"type":"integer","title":"Max Concurrent"},"max_locations":{"type":"integer","title":"Max Locations"},"active_campaigns":{"type":"integer","title":"Active Campaigns","default":0},"completed_campaigns":{"type":"integer","title":"Completed Campaigns","default":0},"total_campaigns":{"type":"integer","title":"Total Campaigns","default":0}},"type":"object","required":["can_create","reason","daily_remaining","monthly_remaining","concurrent_remaining","daily_limit","monthly_limit","max_concurrent","max_locations"],"title":"QuotaStatusResponse","description":"Campaign quota status for the current client."},"ReadinessCheckItem":{"properties":{"item":{"type":"string","title":"Item"},"label":{"type":"string","title":"Label"},"ok":{"type":"boolean","title":"Ok"},"detail":{"type":"string","title":"Detail"}},"type":"object","required":["item","label","ok","detail"],"title":"ReadinessCheckItem","description":"Single readiness check result."},"ReauthDetailsResponse":{"properties":{"status":{"type":"string","title":"Status","description":"'valid' when the token is live"},"credential_id":{"type":"integer","title":"Credential Id"},"provider_name":{"type":"string","title":"Provider Name"},"auth_type":{"type":"string","title":"Auth Type"},"key_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Label"},"health_status":{"type":"string","title":"Health Status"},"consecutive_failures":{"type":"integer","title":"Consecutive Failures"},"token_expired":{"type":"boolean","title":"Token Expired","description":"True when the OAuth token is past expiry"},"reason":{"type":"string","title":"Reason","description":"Human-readable why-reauth explanation"},"expires_at":{"type":"string","title":"Expires At","description":"When this re-auth link expires"}},"type":"object","required":["status","credential_id","provider_name","auth_type","health_status","consecutive_failures","token_expired","reason","expires_at"],"title":"ReauthDetailsResponse"},"RecentDeny":{"properties":{"id":{"type":"string","title":"Id"},"decided_at":{"type":"string","title":"Decided At"},"service_type":{"type":"string","title":"Service Type"},"reason_code":{"type":"string","title":"Reason Code","default":""},"human_message":{"type":"string","title":"Human Message","default":""},"retry_after_s":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Retry After S"},"current_usage":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Current Usage"},"limit_value":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Limit Value"}},"type":"object","required":["id","decided_at","service_type"],"title":"RecentDeny"},"RecentEventsResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/EventEnvelope"},"type":"array","title":"Events"}},"type":"object","required":["events"],"title":"RecentEventsResponse"},"RecentSalesEvent":{"properties":{"first_name":{"type":"string","maxLength":80,"minLength":1,"title":"First Name"},"city":{"type":"string","maxLength":120,"minLength":1,"title":"City"},"kind":{"type":"string","maxLength":40,"minLength":1,"title":"Kind","default":"purchase"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"}},"type":"object","required":["first_name","city","occurred_at"],"title":"RecentSalesEvent","description":"One PII-filtered recent customer event consumed by sys-proof-recent-sales-toast.\n\nThe endpoint NEVER emits last_name, email, phone, or post body. Server-side\nfilter rejects names < 1 char so the toast never renders an empty bubble."},"RecentSalesResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/RecentSalesEvent"},"type":"array","title":"Events"},"total":{"type":"integer","minimum":0.0,"title":"Total"},"category":{"type":"string","maxLength":64,"minLength":1,"title":"Category"},"min_age_minutes":{"type":"integer","maximum":1440.0,"minimum":0.0,"title":"Min Age Minutes"},"max_age_hours":{"type":"integer","maximum":720.0,"minimum":1.0,"title":"Max Age Hours"}},"type":"object","required":["events","total","category","min_age_minutes","max_age_hours"],"title":"RecentSalesResponse","description":"Response payload for GET /api/v1/content/proof/recent-sales."},"RecordType":{"type":"string","enum":["business","contact","email","profile"],"title":"RecordType","description":"Valid record types for deduplication"},"RedirectCreate":{"properties":{"from_path":{"type":"string","maxLength":1000,"minLength":1,"title":"From Path"},"to_path":{"type":"string","maxLength":1000,"minLength":1,"title":"To Path"},"status_code":{"type":"integer","maximum":308.0,"minimum":301.0,"title":"Status Code","default":301}},"type":"object","required":["from_path","to_path"],"title":"RedirectCreate","description":"Create a redirect."},"RedirectListResponse":{"properties":{"redirects":{"items":{"$ref":"#/components/schemas/RedirectResponse"},"type":"array","title":"Redirects"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["redirects","total"],"title":"RedirectListResponse","description":"List of redirects."},"RedirectResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"from_path":{"type":"string","title":"From Path"},"to_path":{"type":"string","title":"To Path"},"status_code":{"type":"integer","title":"Status Code"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","from_path","to_path","status_code","is_active","created_at"],"title":"RedirectResponse","description":"Redirect response."},"RegenerateResponse":{"properties":{"token":{"type":"string","title":"Token","description":"New spideriq_pat_* secret. Shown ONCE — not recoverable."},"token_prefix":{"type":"string","title":"Token Prefix"},"opvs_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opvs Address"},"message":{"type":"string","title":"Message","default":"Token regenerated"}},"type":"object","required":["token","token_prefix"],"title":"RegenerateResponse","description":"Returned once on successful rotation — contains the new plaintext PAT."},"RegionListItem":{"properties":{"region":{"type":"string","title":"Region"},"location_count":{"type":"integer","title":"Location Count"},"city_count":{"type":"integer","title":"City Count"},"postcode_count":{"type":"integer","title":"Postcode Count"}},"type":"object","required":["region","location_count","city_count","postcode_count"],"title":"RegionListItem","description":"An admin region (state/province) within a country.\n\n``city_count``/``postcode_count`` split the ``location_count`` so an agent\ncan see, before scoping a ZIP campaign, how many Maps searches a state's\npostcodes (ZIPs) will fan out into."},"RegionListResponse":{"properties":{"country_code":{"type":"string","title":"Country Code"},"regions":{"items":{"$ref":"#/components/schemas/RegionListItem"},"type":"array","title":"Regions"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["country_code","regions","total"],"title":"RegionListResponse","description":"Response for listing admin regions (states) of a country."},"RelatedPostItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"excerpt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Excerpt"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"sort_order":{"type":"integer","title":"Sort Order","default":0}},"type":"object","required":["id","slug","title"],"title":"RelatedPostItem","description":"Related post in a post response."},"ReleaseHoldResponse":{"properties":{"hold_id":{"type":"string","format":"uuid","title":"Hold Id"},"released":{"type":"boolean","title":"Released","description":"True if this call transitioned the hold; False if already released."}},"additionalProperties":false,"type":"object","required":["hold_id","released"],"title":"ReleaseHoldResponse","description":"POST /booking/holds/{hold_id}/release success payload."},"RemoteCampaignResponse":{"properties":{"remote_campaign_id":{"type":"string","title":"Remote Campaign Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"status":{"type":"string","title":"Status"},"lead_count":{"type":"integer","title":"Lead Count"}},"type":"object","required":["remote_campaign_id","name","status","lead_count"],"title":"RemoteCampaignResponse"},"RemoveLeadsRequest":{"properties":{"remote_campaign_id":{"type":"string","maxLength":128,"minLength":1,"title":"Remote Campaign Id"},"spideriq_campaign_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Spideriq Campaign Id"},"emails":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":10000},{"type":"null"}],"title":"Emails"}},"type":"object","required":["remote_campaign_id"],"title":"RemoveLeadsRequest"},"RemoveLeadsResponse":{"properties":{"matched":{"type":"integer","title":"Matched"},"removed":{"type":"integer","title":"Removed"},"unresolved":{"type":"integer","title":"Unresolved"},"active_after":{"type":"integer","title":"Active After"}},"type":"object","required":["matched","removed","unresolved","active_after"],"title":"RemoveLeadsResponse"},"RenderEventBody":{"properties":{"step_reached":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Step Reached","description":"Last step the visitor touched. NULL = just rendered."}},"additionalProperties":false,"type":"object","title":"RenderEventBody","description":"Body schema for POST /api/v1/booking/{flow_id}/render.\n\nCalled by the booking component (apps/booking-component/) on mount and\noptionally at each step transition to surface drop-off. Public endpoint,\nno auth — ``flow_id`` path param is the scoping primitive and must\nresolve to an ``active`` flow via ``resolve_flow_owner``."},"RenderEventResponse":{"properties":{"accepted":{"type":"boolean","title":"Accepted","description":"True if the event was persisted; false if the flow is unknown, archived, or the write failed (analytics is fire-and-forget)."}},"additionalProperties":false,"type":"object","required":["accepted"],"title":"RenderEventResponse","description":"Response for POST /render. Always 202 Accepted."},"RenderFlowResponse":{"properties":{"flow_id":{"type":"string","format":"uuid","title":"Flow Id"},"kind":{"type":"string","maxLength":16,"minLength":1,"title":"Kind","default":"booking"},"business":{"$ref":"#/components/schemas/_RenderBusiness"},"steps":{"items":{"type":"object"},"type":"array","title":"Steps"},"theme":{"anyOf":[{"$ref":"#/components/schemas/_RenderTheme"},{"type":"null"}]},"turnstile_site_key":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Turnstile Site Key"},"locale":{"type":"string","maxLength":16,"minLength":2,"title":"Locale","description":"BCP-47 primary subtag that resolved after Accept-Language parsing. 'en' when the header is absent or the tag has no matching translations.","default":"en"},"logic":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Logic"},"variables":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Variables"},"hidden_fields":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Hidden Fields"},"welcome_screens":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Welcome Screens"},"thankyou_screens":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Thankyou Screens"}},"additionalProperties":false,"type":"object","required":["flow_id","business"],"title":"RenderFlowResponse","description":"Public shape returned by GET /booking/{flow_id}/render.\n\nMatches `apps/booking-component/src/types.ts → RenderResponse` byte-for-byte\nso the customer-facing React component can consume it without remapping."},"ResearchConfig":{"properties":{"spidermaps":{"allOf":[{"$ref":"#/components/schemas/SpiderMapsConfig"}],"description":"SpiderMaps configuration (business discovery)"},"spidersite":{"allOf":[{"$ref":"#/components/schemas/SpiderSiteConfig"}],"description":"SpiderSite configuration (website crawling)"},"spidercompanydata":{"allOf":[{"$ref":"#/components/schemas/SpiderCompanyDataConfig"}],"description":"SpiderCompanyData configuration (company registry lookup)"},"social_enrichment":{"allOf":[{"$ref":"#/components/schemas/SocialEnrichmentConfig"}],"description":"Social media enrichment configuration"},"spiderverify":{"allOf":[{"$ref":"#/components/schemas/SpiderVerifyConfig"}],"description":"SpiderVerify configuration (email verification)"},"domain_filter":{"allOf":[{"$ref":"#/components/schemas/DomainFilterConfig"}],"description":"Domain filtering configuration"},"deduplication":{"type":"boolean","title":"Deduplication","description":"Enable FuzzIQ deduplication for businesses","default":true}},"type":"object","title":"ResearchConfig","description":"Complete configuration for company research workflow.\n\nPipeline: SpiderMaps → SpiderSite → SpiderCompanyData → Social Enrichment → SpiderVerify"},"ResearchProgress":{"properties":{"companies_found":{"type":"integer","title":"Companies Found","default":0},"companies_processed":{"type":"integer","title":"Companies Processed","default":0},"companies_failed":{"type":"integer","title":"Companies Failed","default":0},"emails_found":{"type":"integer","title":"Emails Found","default":0},"emails_verified":{"type":"integer","title":"Emails Verified","default":0},"percentage":{"type":"number","title":"Percentage","default":0.0}},"type":"object","title":"ResearchProgress","description":"Progress tracking for research job."},"ResearchResultsResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"research_id":{"type":"string","title":"Research Id"},"status":{"$ref":"#/components/schemas/ResearchStatus"},"progress":{"$ref":"#/components/schemas/ResearchProgress"},"companies":{"items":{"$ref":"#/components/schemas/CompanyResult"},"type":"array","title":"Companies"},"summary":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Summary"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["research_id","status","progress"],"title":"ResearchResultsResponse","description":"Response containing research job results."},"ResearchStage":{"type":"string","enum":["pending","spidermaps","spidersite","spidercompanydata","social_enrichment","spiderverify","aggregation","complete","failed"],"title":"ResearchStage","description":"Pipeline stages for company research workflow."},"ResearchStatus":{"type":"string","enum":["queued","processing","completed","failed","cancelled","paused"],"title":"ResearchStatus","description":"Status values for company research jobs."},"ResearchStatusResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"research_id":{"type":"string","title":"Research Id"},"status":{"$ref":"#/components/schemas/ResearchStatus"},"current_stage":{"$ref":"#/components/schemas/ResearchStage"},"progress":{"$ref":"#/components/schemas/ResearchProgress"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["research_id","status","current_stage","progress"],"title":"ResearchStatusResponse","description":"Response for research job status check."},"ResearchSubmitRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name","description":"Human-readable name for this research job"},"locations":{"anyOf":[{"items":{"$ref":"#/components/schemas/LocationInput"},"type":"array"},{"type":"null"}],"title":"Locations","description":"List of locations to search (triggers SpiderMaps stage)"},"domains":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Domains","description":"List of domains to research directly (skips SpiderMaps)"},"search_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Query","description":"Business search query (e.g., 'coffee shops', 'dentists')"},"config":{"allOf":[{"$ref":"#/components/schemas/ResearchConfig"}],"description":"Workflow configuration"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url","description":"Webhook URL for completion notification"},"webhook_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Secret","description":"Webhook secret for signature verification"},"priority":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Priority","description":"Job priority (1-10)","default":5}},"type":"object","title":"ResearchSubmitRequest","description":"Request body for submitting a company research job."},"ResearchSubmitResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"research_id":{"type":"string","title":"Research Id","description":"Unique research job ID"},"status":{"allOf":[{"$ref":"#/components/schemas/ResearchStatus"}],"default":"queued"},"input_type":{"type":"string","title":"Input Type","description":"'locations' or 'domains'"},"celery_workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Celery Workflow Id","description":"Celery workflow ID"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["research_id","input_type","created_at"],"title":"ResearchSubmitResponse","description":"Response from research job submission."},"ResearchUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"config":{"anyOf":[{"$ref":"#/components/schemas/ResearchConfig"},{"type":"null"}]},"priority":{"anyOf":[{"type":"integer","maximum":10.0,"minimum":1.0},{"type":"null"}],"title":"Priority"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"webhook_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Secret"}},"type":"object","title":"ResearchUpdateRequest","description":"Request body for updating research job configuration."},"Resource":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["staff","room","equipment"],"title":"Kind"},"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"location_id":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Location Id"},"quantity":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Quantity","default":1},"cleanup_minutes":{"type":"integer","maximum":240.0,"minimum":0.0,"title":"Cleanup Minutes","default":0},"metadata":{"type":"object","title":"Metadata"},"is_active":{"type":"boolean","title":"Is Active","default":true},"sort_order":{"type":"integer","maximum":10000.0,"minimum":0.0,"title":"Sort Order","default":0},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","kind","name"],"title":"Resource","description":"One bookable resource (staff | room | equipment).\n\nStored in ``norm_cli_*.booking_resources``. Kind-specific attributes live\nin ``metadata`` — see migration 142 for the documented JSONB shapes per\nkind."},"ResourceCreate":{"properties":{"kind":{"type":"string","enum":["staff","room","equipment"],"title":"Kind"},"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"location_id":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Location Id"},"quantity":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Quantity","default":1},"cleanup_minutes":{"type":"integer","maximum":240.0,"minimum":0.0,"title":"Cleanup Minutes","default":0},"metadata":{"type":"object","title":"Metadata"},"is_active":{"type":"boolean","title":"Is Active","default":true},"sort_order":{"type":"integer","maximum":10000.0,"minimum":0.0,"title":"Sort Order","default":0}},"additionalProperties":false,"type":"object","required":["kind","name"],"title":"ResourceCreate","description":"Payload for ``POST /booking/resources``."},"ResourceList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Resource"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Next Cursor"}},"additionalProperties":false,"type":"object","required":["items"],"title":"ResourceList"},"ResourceType":{"type":"string","enum":["businesses","domains","contacts","emails","phones","company_registry","linkedin_profiles","staff","media","bookings","services","booking_flows","booking_templates","booking_templates_global","pins"],"title":"ResourceType","description":"Resource types that map to norm_cli_* tables."},"ResourceUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"location_id":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Location Id"},"quantity":{"anyOf":[{"type":"integer","maximum":1000.0,"minimum":1.0},{"type":"null"}],"title":"Quantity"},"cleanup_minutes":{"anyOf":[{"type":"integer","maximum":240.0,"minimum":0.0},{"type":"null"}],"title":"Cleanup Minutes"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"sort_order":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Sort Order"}},"additionalProperties":false,"type":"object","title":"ResourceUpdate","description":"Partial update — ``kind`` is immutable (creating a room-typed row and\nflipping it to staff later would corrupt all existing assignments)."},"ResponseFormat":{"type":"string","enum":["text","json_object","json_schema"],"title":"ResponseFormat","description":"Response format types."},"ResponseFormatSpec":{"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/ResponseFormat"}],"default":"text"},"json_schema":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Json Schema"}},"type":"object","title":"ResponseFormatSpec","description":"Response format specification."},"RetryLocationRequest":{"properties":{"retry_mode":{"type":"string","enum":["full","site","verify"],"title":"Retry Mode","description":"full = re-run the whole workflow from SpiderMaps; site = keep Maps results, re-run SpiderSite + SpiderVerify; verify = keep Site results, re-run SpiderVerify only","default":"full"}},"type":"object","title":"RetryLocationRequest","description":"Request body for retrying a campaign location."},"RetryLocationResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"location_id":{"type":"integer","title":"Location Id"},"new_status":{"type":"string","title":"New Status"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"retry_mode":{"type":"string","title":"Retry Mode"},"retry_count":{"type":"integer","title":"Retry Count"},"message":{"type":"string","title":"Message"},"businesses_to_process":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Businesses To Process"},"businesses_with_emails":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Businesses With Emails"}},"type":"object","required":["success","location_id","new_status","retry_mode","retry_count","message"],"title":"RetryLocationResponse","description":"Response for a single-location retry."},"RevokeResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message"],"title":"RevokeResponse"},"RobotsExtensionConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"rules":{"items":{"$ref":"#/components/schemas/RobotsRule"},"type":"array","title":"Rules"},"auto_sitemap_link":{"type":"boolean","title":"Auto Sitemap Link","default":true},"extra_lines":{"items":{"type":"string"},"type":"array","title":"Extra Lines"}},"type":"object","title":"RobotsExtensionConfig","description":"`content_settings.extensions.robots` shape."},"RobotsRule":{"properties":{"user_agent":{"type":"string","maxLength":200,"title":"User Agent","default":"*"},"allow":{"items":{"type":"string"},"type":"array","title":"Allow"},"disallow":{"items":{"type":"string"},"type":"array","title":"Disallow"}},"type":"object","title":"RobotsRule","description":"One User-agent rule group in robots.txt."},"RoleRequest":{"properties":{"role":{"type":"string","maxLength":64,"minLength":1,"title":"Role"}},"type":"object","required":["role"],"title":"RoleRequest"},"RotationMethod":{"type":"string","enum":["reconnect","airplane","reboot"],"title":"RotationMethod","description":"IP rotation method enumeration"},"RotationRequest":{"properties":{"modem_id":{"type":"string","format":"uuid","title":"Modem Id"},"method":{"anyOf":[{"$ref":"#/components/schemas/RotationMethod"},{"type":"null"}]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["modem_id"],"title":"RotationRequest","description":"Request IP rotation"},"RotationResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"modem_id":{"type":"string","format":"uuid","title":"Modem Id"},"old_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Ip"},"new_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Ip"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["success","modem_id","old_ip","new_ip","duration_seconds","error_message"],"title":"RotationResponse","description":"Rotation result"},"RunDetail":{"properties":{"run_id":{"type":"string","title":"Run Id"},"flow_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow Slug"},"flow_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow Name"},"dispatch_type":{"type":"string","title":"Dispatch Type"},"status":{"type":"string","title":"Status"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode"},"batch_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Batch Id"},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"queued_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Queued At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"results_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Results Count"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Kind"},"actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id"},"actor_display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Display Name"},"actor_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Avatar Url"},"input":{"type":"object","title":"Input"},"output":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Output"},"sub_runs":{"items":{"type":"object"},"type":"array","title":"Sub Runs"}},"type":"object","required":["run_id","flow_slug","dispatch_type","status","mode","created_at","queued_at","started_at","completed_at","input"],"title":"RunDetail"},"RunGroupDetail":{"properties":{"run_group_id":{"type":"string","title":"Run Group Id"},"mode":{"type":"string","enum":["batch","campaign"],"title":"Mode"},"flow_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow Slug"},"dispatch_type":{"type":"string","title":"Dispatch Type"},"total":{"type":"integer","title":"Total"},"succeeded":{"type":"integer","title":"Succeeded"},"failed":{"type":"integer","title":"Failed"},"pending":{"type":"integer","title":"Pending"},"runs":{"items":{"$ref":"#/components/schemas/RunRecord"},"type":"array","title":"Runs"}},"type":"object","required":["run_group_id","mode","flow_slug","dispatch_type","total","succeeded","failed","pending","runs"],"title":"RunGroupDetail"},"RunRecord":{"properties":{"run_id":{"type":"string","title":"Run Id"},"flow_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow Slug"},"flow_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow Name"},"dispatch_type":{"type":"string","title":"Dispatch Type"},"status":{"type":"string","title":"Status"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode"},"batch_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Batch Id"},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"queued_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Queued At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"results_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Results Count"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Kind"},"actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id"},"actor_display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Display Name"},"actor_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Avatar Url"}},"type":"object","required":["run_id","flow_slug","dispatch_type","status","mode","created_at","queued_at","started_at","completed_at"],"title":"RunRecord"},"RunningJobMix":{"properties":{"workerType":{"type":"string","title":"Workertype"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["workerType","count"],"title":"RunningJobMix"},"SSOConfigRequest":{"properties":{"auto_provision":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Auto Provision"},"default_role":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Default Role"},"default_groups":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":50},{"type":"null"}],"title":"Default Groups"}},"type":"object","title":"SSOConfigRequest","description":"Per-project invite-gated JIT policy (C4c-1). Omitted field = unchanged."},"SSOProviderRequest":{"properties":{"issuer":{"type":"string","maxLength":2048,"minLength":8,"title":"Issuer"},"domain":{"type":"string","maxLength":253,"minLength":1,"title":"Domain"},"client_id":{"type":"string","maxLength":256,"minLength":1,"title":"Client Id"},"client_secret":{"type":"string","maxLength":1024,"minLength":1,"title":"Client Secret"},"discovery_endpoint":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Discovery Endpoint"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Scopes"},"pkce":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Pkce"},"mapping":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Mapping"}},"type":"object","required":["issuer","domain","client_id","client_secret"],"title":"SSOProviderRequest","description":"One project's OIDC identity-provider config (C4c-1). client_secret is\nrequired on every save (stored as one JSON blob; never returned on read)."},"SceneType":{"type":"string","enum":["hero-bold","feature-grid","pricing-tiers","social-proof","faq-accordion","conversion-cta","navigation-header","navigation-footer","data-collection-form","editorial-content","team-grid","city-aerial","nature-landscape","abstract-motion","food-prep","people-lifestyle","tech-hardware","marketing-site","docs-site","directory-site","portfolio-site"],"title":"SceneType","description":"Universal axis — single-value scene/intent vocabulary.\n\nStored in scene_type VARCHAR(64) on content_components / content_bg_videos /\ncontent_site_templates. Single-value (one row, one scene_type).\n\nThe vocabulary intentionally mixes asset-shapes (e.g. 'city-aerial' for\nbg-video, 'hero-bold' for component, 'marketing-site' for template).\nFiltering across asset types uses overlapping scene_type values when\nthey make sense."},"ScrollSequenceFromVideoRequest":{"properties":{"video_url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Video Url","description":"Source video URL (SpiderMedia or guarded third-party)"},"page_slug":{"type":"string","maxLength":200,"minLength":1,"title":"Page Slug","description":"Target page slug — must already exist"},"strategy":{"type":"string","enum":["target_frames","fps","duration_fps"],"title":"Strategy","description":"Frame-count strategy — passed through to SpiderVideo extract_frames","default":"target_frames"},"target_frames":{"anyOf":[{"type":"integer","maximum":600.0,"minimum":10.0},{"type":"null"}],"title":"Target Frames","default":120},"fps":{"anyOf":[{"type":"integer","maximum":60.0,"minimum":1.0},{"type":"null"}],"title":"Fps"},"duration_seconds":{"anyOf":[{"type":"integer","maximum":120.0,"minimum":1.0},{"type":"null"}],"title":"Duration Seconds"},"output_format":{"type":"string","enum":["webp","jpeg"],"title":"Output Format","default":"webp"},"output_width":{"type":"integer","maximum":1920.0,"minimum":320.0,"title":"Output Width","default":1280},"output_quality":{"type":"integer","maximum":95.0,"minimum":50.0,"title":"Output Quality","default":80},"scroll_distance_vh":{"type":"integer","maximum":800.0,"minimum":200.0,"title":"Scroll Distance Vh","default":400},"preload_strategy":{"type":"string","enum":["all","progressive"],"title":"Preload Strategy","default":"progressive"},"canvas_width":{"type":"integer","maximum":1920.0,"minimum":320.0,"title":"Canvas Width","default":1280},"canvas_height":{"type":"integer","maximum":1080.0,"minimum":240.0,"title":"Canvas Height","default":720},"background_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","title":"Background Color","default":"#ffffff"},"position":{"anyOf":[{"type":"string","enum":["append","prepend"]},{"$ref":"#/components/schemas/ScrollSequencePositionSpec"},{"type":"null"}],"title":"Position","description":"Where to insert the new block. 'append' (default), 'prepend', or a position spec.","default":"append"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","default":5},"poll_timeout_seconds":{"type":"integer","maximum":900.0,"minimum":30.0,"title":"Poll Timeout Seconds","default":300},"poll_interval_seconds":{"type":"number","maximum":10.0,"minimum":0.5,"title":"Poll Interval Seconds","default":2.0},"dry_run":{"type":"boolean","title":"Dry Run","description":"If true, returns the block JSON but does not touch the page","default":false}},"type":"object","required":["video_url","page_slug"],"title":"ScrollSequenceFromVideoRequest"},"ScrollSequenceFromVideoResponse":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run"},"job_id":{"type":"string","title":"Job Id"},"manifest":{"type":"object","title":"Manifest"},"block":{"type":"object","title":"Block"},"page":{"type":"object","title":"Page"},"preview_hint":{"type":"string","title":"Preview Hint"}},"type":"object","required":["dry_run","job_id","manifest","block","page","preview_hint"],"title":"ScrollSequenceFromVideoResponse"},"ScrollSequencePositionSpec":{"properties":{"index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Index","description":"Absolute insertion index in the blocks array"},"before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before","description":"Block id/component_slug/type — insert immediately before"},"after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"After","description":"Block id/component_slug/type — insert immediately after"}},"type":"object","title":"ScrollSequencePositionSpec","description":"Exactly one of: index, before, after. Omit to `append`."},"SearchFacets":{"properties":{"categories":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacetBucket"},"type":"array"},{"type":"null"}],"title":"Categories","description":"Category facets"},"job_types":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacetBucket"},"type":"array"},{"type":"null"}],"title":"Job Types","description":"Job type facets"},"rating_ranges":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacetBucket"},"type":"array"},{"type":"null"}],"title":"Rating Ranges","description":"Rating range facets"},"avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Rating","description":"Average rating"}},"type":"object","title":"SearchFacets","description":"Aggregated facets from search results."},"SearchFilters":{"properties":{"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"Filter by category"},"job_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Type","description":"Filter by job type (spiderMaps, spiderSite)"},"min_rating":{"anyOf":[{"type":"number","maximum":5.0,"minimum":0.0},{"type":"null"}],"title":"Min Rating","description":"Minimum rating filter"},"email_verified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Email Verified","description":"Filter by email verification status"},"min_icp_score":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Min Icp Score","description":"Minimum ICP score"}},"type":"object","title":"SearchFilters","description":"Search filters for business/lead queries."},"SearchHit":{"properties":{"_id":{"type":"string","title":" Id","description":"Document ID"},"_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":" Score","description":"Relevance score"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Business/lead name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Description"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"Category"},"rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rating","description":"Rating"},"reviews_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews Count","description":"Number of reviews"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address","description":"Address"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Phone number"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website","description":"Website URL"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Email address"},"location":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Location","description":"Geo coordinates"},"job_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Type","description":"Source job type"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Source job ID"},"icp_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Icp Score","description":"ICP score"},"indexed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Indexed At","description":"Index timestamp"}},"type":"object","required":["_id"],"title":"SearchHit","description":"Individual search result hit."},"SearchRequest":{"properties":{"query":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Query","description":"Full-text search query"},"filters":{"anyOf":[{"$ref":"#/components/schemas/SearchFilters"},{"type":"null"}],"description":"Search filters"},"geo":{"anyOf":[{"$ref":"#/components/schemas/GeoFilter"},{"type":"null"}],"description":"Geo-distance filter"},"page":{"type":"integer","minimum":1.0,"title":"Page","description":"Page number","default":1},"per_page":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Per Page","description":"Results per page","default":20},"sort_by":{"type":"string","pattern":"^(_score|rating|reviews_count|newest)$","title":"Sort By","description":"Sort field","default":"_score"}},"type":"object","title":"SearchRequest","description":"Full search request with query, filters, and pagination."},"SearchResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"total":{"type":"integer","title":"Total","description":"Total matching documents"},"hits":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Hits","description":"Search results"},"facets":{"anyOf":[{"$ref":"#/components/schemas/SearchFacets"},{"type":"null"}],"description":"Aggregation facets"},"page":{"type":"integer","title":"Page","description":"Current page"},"per_page":{"type":"integer","title":"Per Page","description":"Results per page"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if any"}},"type":"object","required":["total","hits","page","per_page"],"title":"SearchResponse","description":"Search response with hits, facets, and pagination."},"SelectableUnitsResponse":{"properties":{"units":{"items":{"$ref":"#/components/schemas/GeoUnit"},"type":"array","title":"Units"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["units","total"],"title":"SelectableUnitsResponse","description":"Response for the flat, alphabetically merged selectable-units list."},"SenderHealthResponse":{"properties":{"sender_id":{"type":"integer","title":"Sender Id"},"email_address":{"type":"string","title":"Email Address"},"provider":{"type":"string","title":"Provider"},"latest":{"anyOf":[{"$ref":"#/components/schemas/HealthSnapshotResponse"},{"type":"null"}]},"history":{"items":{"$ref":"#/components/schemas/HealthSnapshotResponse"},"type":"array","title":"History"}},"type":"object","required":["sender_id","email_address","provider","latest","history"],"title":"SenderHealthResponse"},"SenderResponse":{"properties":{"id":{"type":"integer","title":"Id"},"connection_id":{"type":"integer","title":"Connection Id"},"remote_account_id":{"type":"string","title":"Remote Account Id"},"email_address":{"type":"string","title":"Email Address"},"warmup_enabled":{"type":"boolean","title":"Warmup Enabled"},"status":{"type":"string","title":"Status"},"mailbox_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mailbox Id"},"provider":{"type":"string","title":"Provider"},"connection_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connection Label"}},"type":"object","required":["id","connection_id","remote_account_id","email_address","warmup_enabled","status","mailbox_id","provider","connection_label"],"title":"SenderResponse"},"SetPasswordRequest":{"properties":{"new_password":{"type":"string","title":"New Password"},"send_email":{"type":"boolean","title":"Send Email","default":false}},"type":"object","required":["new_password"],"title":"SetPasswordRequest","description":"Request to set a user's password directly."},"SetPasswordResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message"],"title":"SetPasswordResponse","description":"Response for password set operation."},"ShareRequest":{"properties":{"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"},"to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"},"ttl_days":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Ttl Days","default":30}},"type":"object","title":"ShareRequest","description":"POST body for creating a share link.\n\nEither `date` (single day) OR `from`+`to` (range). Both must be YYYY-MM-DD."},"SiteConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Crawl website for contacts, team, company info","default":true},"mode":{"type":"string","title":"Mode","description":"contacts / leads / full","default":"leads"},"max_pages":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Max Pages","description":"Max pages to crawl","default":25},"extract_team":{"type":"boolean","title":"Extract Team","description":"Extract team members via AI","default":true},"extract_company_info":{"type":"boolean","title":"Extract Company Info","description":"Extract company info via AI","default":true},"product_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Description","description":"Your product (for CHAMP scoring)"},"icp_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icp Description","description":"Your ICP (for lead scoring)"}},"type":"object","title":"SiteConfig"},"SiteMemberPatch":{"properties":{"status":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Status"},"groups":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":50},{"type":"null"}],"title":"Groups"}},"type":"object","title":"SiteMemberPatch"},"SiteTemplateAgentMeta":{"properties":{"page_count":{"anyOf":[{"type":"integer","maximum":200.0,"minimum":1.0},{"type":"null"}],"title":"Page Count","description":"Number of pages the template ships with. Agents use this to filter for 'just a landing page' vs 'full marketing site'."},"has_blog":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Blog","description":"TRUE if template includes a /blog index + post template."},"has_pricing":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Pricing","description":"TRUE if template includes a pricing page."},"has_directory":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Directory","description":"TRUE if template includes a directory listing page (programmatic SEO target)."},"has_booking":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Booking","description":"TRUE if template integrates SpiderBook booking flow."},"conversion_strategy":{"anyOf":[{"type":"string","enum":["primary-cta","secondary-cta","trust","scarcity","social-proof","education","navigation","none"]},{"type":"null"}],"title":"Conversion Strategy","description":"Primary funnel pattern for the template (mirrors ComponentAgentMeta.conversion_strategy)."},"style_aesthetic":{"anyOf":[{"type":"string","enum":["minimal","bold","editorial","playful","premium","technical","brutalist","soft"]},{"type":"null"}],"title":"Style Aesthetic","description":"Top-level style descriptor. Agents combine this with universal mood/palette to filter."},"component_set":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":100},{"type":"null"}],"title":"Component Set","description":"Component slugs this template ships with — useful for 'find me a template that uses sys-bg-video'. Validated as a list of slug strings; not FK-checked at write."}},"additionalProperties":false,"type":"object","title":"SiteTemplateAgentMeta","description":"Per-template discovery axes.\n\nCaptures template-level facts an agent needs to pick a starter for a\nbrief: how many pages, which features ship, what the conversion funnel\nlooks like."},"SiteTemplateCreateRequest":{"properties":{"slug":{"type":"string","maxLength":128,"minLength":1,"title":"Slug"},"name":{"type":"string","maxLength":256,"minLength":1,"title":"Name"},"source_client_id":{"type":"string","maxLength":64,"minLength":1,"title":"Source Client Id","description":"Substrate-tenant client_id (UUID or cli_xxx) whose pages are the master copies."},"source_page_slugs":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Source Page Slugs","description":"Page slugs in the substrate tenant to clone when this template is applied."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"preview_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Url"},"preview_thumbnail_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Thumbnail Url"},"industry":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Industry"},"use_case":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Use Case"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"source_component_slugs":{"items":{"type":"string"},"type":"array","title":"Source Component Slugs"},"source_nav_locations":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Nav Locations"},"source_settings_keys":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Settings Keys"},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"is_public":{"type":"boolean","title":"Is Public","default":true},"replication_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Replication Prompt"},"mood":{"anyOf":[{"items":{"$ref":"#/components/schemas/Mood"},"type":"array"},{"type":"null"}],"title":"Mood"},"palette":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":12},{"type":"null"}],"title":"Palette"},"brand_fit_tags":{"anyOf":[{"items":{"$ref":"#/components/schemas/BrandFit"},"type":"array"},{"type":"null"}],"title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"$ref":"#/components/schemas/SceneType"},{"type":"null"}]},"agent_meta":{"anyOf":[{"$ref":"#/components/schemas/SiteTemplateAgentMeta"},{"type":"null"}]}},"type":"object","required":["slug","name","source_client_id","source_page_slugs"],"title":"SiteTemplateCreateRequest","description":"Create a new site_template catalog row."},"SiteTemplateUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":256,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"preview_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Url"},"preview_thumbnail_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Preview Thumbnail Url"},"industry":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Industry"},"use_case":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Use Case"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"source_client_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Source Client Id"},"source_page_slugs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Page Slugs"},"source_component_slugs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Component Slugs"},"source_nav_locations":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Nav Locations"},"source_settings_keys":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Settings Keys"},"is_featured":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured"},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public"},"replication_prompt":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Replication Prompt"},"mood":{"anyOf":[{"items":{"$ref":"#/components/schemas/Mood"},"type":"array"},{"type":"null"}],"title":"Mood"},"palette":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":12},{"type":"null"}],"title":"Palette"},"brand_fit_tags":{"anyOf":[{"items":{"$ref":"#/components/schemas/BrandFit"},"type":"array"},{"type":"null"}],"title":"Brand Fit Tags"},"scene_type":{"anyOf":[{"$ref":"#/components/schemas/SceneType"},{"type":"null"}]},"agent_meta":{"anyOf":[{"$ref":"#/components/schemas/SiteTemplateAgentMeta"},{"type":"null"}]}},"type":"object","title":"SiteTemplateUpdateRequest","description":"Patch an existing site_template catalog row."},"SitemapChangeFreqOverrides":{"properties":{"pages":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Pages"},"posts":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Posts"},"docs":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Docs"}},"type":"object","title":"SitemapChangeFreqOverrides","description":"Per-section sitemap.xml `<changefreq>` overrides.\n\nDefaults match the historical hardcoded values in\n`ContentService.generate_sitemap` so leaving overrides empty preserves\npre-W6 behaviour exactly."},"SitemapEntry":{"properties":{"loc":{"type":"string","title":"Loc"},"lastmod":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lastmod"},"changefreq":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Changefreq"},"priority":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Priority"}},"type":"object","required":["loc"],"title":"SitemapEntry","description":"Sitemap entry."},"SitemapExtensionConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"include_drafts":{"type":"boolean","title":"Include Drafts","default":false},"include_archived":{"type":"boolean","title":"Include Archived","default":false},"change_freq":{"$ref":"#/components/schemas/SitemapChangeFreqOverrides"}},"type":"object","title":"SitemapExtensionConfig","description":"`content_settings.extensions.sitemap` shape."},"SitemapResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/SitemapEntry"},"type":"array","title":"Entries"}},"type":"object","required":["entries"],"title":"SitemapResponse","description":"Sitemap data."},"SkillCatalogResponse":{"properties":{"skills":{"items":{"$ref":"#/components/schemas/SkillMetadata"},"type":"array","title":"Skills","description":"List of all available skills"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of skills"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories","description":"Available categories"},"version":{"type":"string","title":"Version","description":"API version"}},"type":"object","required":["skills","total_count","categories","version"],"title":"SkillCatalogResponse","description":"Response for the skills catalog endpoint"},"SkillCategory":{"type":"string","enum":["scraping","enrichment","social","automation","media","communication"],"title":"SkillCategory","description":"Skill categories for organization and discovery"},"SkillCategoryResponse":{"properties":{"categories":{"items":{"type":"object"},"type":"array","title":"Categories","description":"Categories with skill counts"}},"type":"object","required":["categories"],"title":"SkillCategoryResponse","description":"Response for skills categories endpoint"},"SkillExample":{"properties":{"name":{"type":"string","title":"Name","description":"Example name"},"description":{"type":"string","title":"Description","description":"What this example demonstrates"},"payload":{"type":"object","title":"Payload","description":"Example payload"}},"type":"object","required":["name","description","payload"],"title":"SkillExample","description":"Example request for a skill"},"SkillMetadata":{"properties":{"type":{"type":"string","title":"Type","description":"Skill type identifier (e.g., 'spiderSite', 'spiderMaps')"},"label":{"type":"string","title":"Label","description":"Human-readable skill name"},"description":{"type":"string","title":"Description","description":"Brief description of what the skill does"},"category":{"allOf":[{"$ref":"#/components/schemas/SkillCategory"}],"description":"Skill category for organization"},"version":{"type":"string","title":"Version","description":"Current skill version"},"input_schema":{"type":"object","title":"Input Schema","description":"JSON Schema for payload validation"},"output_fields":{"items":{"type":"string"},"type":"array","title":"Output Fields","description":"Key output fields in the result"},"use_cases":{"items":{"type":"string"},"type":"array","title":"Use Cases","description":"Primary use cases for this skill"},"examples":{"items":{"$ref":"#/components/schemas/SkillExample"},"type":"array","title":"Examples","description":"Example requests with expected outcomes"},"limitations":{"items":{"type":"string"},"type":"array","title":"Limitations","description":"Known limitations and constraints"},"processing_time":{"allOf":[{"$ref":"#/components/schemas/ProcessingTimeEstimate"}],"description":"Expected processing time estimates"},"worker_info":{"allOf":[{"$ref":"#/components/schemas/WorkerInfo"}],"description":"Worker fleet information"},"supports_fuzziq":{"type":"boolean","title":"Supports Fuzziq","description":"Whether FuzzIQ deduplication is supported","default":false},"supports_workflow":{"type":"boolean","title":"Supports Workflow","description":"Whether workflow/campaign chaining is supported","default":false},"ai_features":{"items":{"type":"string"},"type":"array","title":"Ai Features","description":"Available AI-powered features"},"submit_endpoint":{"type":"string","title":"Submit Endpoint","description":"Full endpoint path for job submission"},"docs_url":{"type":"string","title":"Docs Url","description":"Link to detailed documentation"}},"type":"object","required":["type","label","description","category","version","input_schema","processing_time","worker_info","submit_endpoint","docs_url"],"title":"SkillMetadata","description":"Complete metadata for a SpiderIQ skill (job type).\nOptimized for AI agent consumption and discovery.","example":{"category":"scraping","description":"Crawls websites with intelligent page selection, contact extraction, and AI-powered company analysis","label":"SpiderSite - Website Scraping","limitations":["Cannot scrape pages requiring authentication","Maximum 50 pages per job"],"processing_time":{"max_seconds":120,"min_seconds":5,"notes":"SPA sites and AI features add 10-20 seconds","typical_seconds":30},"type":"spiderSite","use_cases":["Lead generation from company websites","Competitor analysis","Team member identification"],"version":"3.1.0"}},"SnoozeRequest":{"properties":{"snoozed_until":{"type":"string","format":"date-time","title":"Snoozed Until","description":"ISO 8601 timestamp to unsnooze"}},"type":"object","required":["snoozed_until"],"title":"SnoozeRequest","description":"Snooze a message until a specific time."},"SnoozeResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message_id":{"type":"integer","title":"Message Id"},"snoozed_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Snoozed Until"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message_id","message"],"title":"SnoozeResponse","description":"Response for snooze/unsnooze."},"SnoozedListResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"total":{"type":"integer","title":"Total"},"messages":{"items":{"$ref":"#/components/schemas/SnoozedMessage"},"type":"array","title":"Messages"}},"type":"object","required":["success","total","messages"],"title":"SnoozedListResponse","description":"List of snoozed messages."},"SnoozedMessage":{"properties":{"id":{"type":"integer","title":"Id"},"from_address":{"type":"string","title":"From Address"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"date":{"type":"string","format":"date-time","title":"Date"},"snoozed_until":{"type":"string","format":"date-time","title":"Snoozed Until"},"original_folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Folder"}},"type":"object","required":["id","from_address","date","snoozed_until"],"title":"SnoozedMessage","description":"Snoozed message summary."},"SocialEnrichmentConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable social media enrichment","default":true},"instagram":{"type":"boolean","title":"Instagram","description":"Enrich Instagram profiles","default":true},"facebook":{"type":"boolean","title":"Facebook","description":"Enrich Facebook pages","default":true},"linkedin":{"type":"boolean","title":"Linkedin","description":"Enrich LinkedIn company pages","default":true},"timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Timeout","default":30}},"type":"object","title":"SocialEnrichmentConfig","description":"Social media enrichment configuration."},"SourceBinding":{"properties":{"source_id":{"type":"string","maxLength":64,"minLength":1,"title":"Source Id","description":"ID of a content_data_sources row. e.g. 'posts', 'idap.cities'."},"role":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Role","description":"Role of this binding when a component takes multiple. e.g. 'primary' / 'related'. NULL when only one binding."},"default_filter":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Default Filter","description":"Default filter object the renderer applies if the block instance doesn't override. Keys must match the source's schema_json filters."},"default_sort":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Default Sort","description":"Default sort field. Format: 'field' or '-field' for descending."},"default_limit":{"anyOf":[{"type":"integer","maximum":500.0,"minimum":1.0},{"type":"null"}],"title":"Default Limit","description":"Default row limit when binding is collection-typed. Per-block override allowed via block.data_binding.limit."}},"additionalProperties":false,"type":"object","required":["source_id"],"title":"SourceBinding","description":"One source binding on a kind='dynamic' component.\n\nStored as an entry in content_components.sources JSONB array. Most\ncomponents have exactly one source; some (e.g. a List that decorates\neach row with a related fetch) have multiple."},"SpeechRequest":{"properties":{"model":{"type":"string","maxLength":128,"minLength":1,"title":"Model","description":"OpenAI TTS model. Examples: 'tts-1' (cheap, fast), 'tts-1-hd' (higher fidelity), 'gpt-4o-mini-tts'."},"input":{"type":"string","maxLength":4096,"minLength":1,"title":"Input","description":"Text to synthesise. OpenAI cap is 4096 chars per request."},"voice":{"type":"string","title":"Voice","description":"One of: alloy, echo, fable, onyx, nova, shimmer."},"response_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Format","description":"Output format: mp3 (default), opus, aac, flac, wav, pcm."},"speed":{"anyOf":[{"type":"number","maximum":4.0,"minimum":0.25},{"type":"null"}],"title":"Speed","description":"Playback speed multiplier. OpenAI range: 0.25 to 4.0."}},"type":"object","required":["model","input","voice"],"title":"SpeechRequest","description":"Mirror of OpenAI's POST /v1/audio/speech request body."},"SpendSnapshot":{"properties":{"snapshot_date":{"type":"string","format":"date","title":"Snapshot Date"},"balance":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Balance"},"usage_daily":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Daily"},"usage_weekly":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Weekly"},"usage_monthly":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Monthly"},"usage_total":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Total"},"currency":{"type":"string","title":"Currency","default":"USD"}},"type":"object","required":["snapshot_date"],"title":"SpendSnapshot","description":"Historical spend data point"},"SpiderBookFeatureStatus":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"True if SpiderBook is enabled for the calling client."},"feature":{"type":"string","title":"Feature","default":"spiderbook"}},"type":"object","required":["enabled"],"title":"SpiderBookFeatureStatus"},"SpiderCompanyDataConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable company registry lookup stage","default":false},"countries_filter":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Countries Filter","description":"Limit registry lookups to these ISO-2 country codes (e.g., ['US', 'GB']). None = auto-detect from company data"},"include_officers":{"type":"boolean","title":"Include Officers","description":"Fetch directors/officers data (may increase lookup time)","default":false},"include_financials":{"type":"boolean","title":"Include Financials","description":"Fetch financial data where available (US 10-K, UK accounts)","default":false},"include_vat_validation":{"type":"boolean","title":"Include Vat Validation","description":"Validate EU VAT numbers when detected (off by default)","default":false},"match_threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Match Threshold","description":"Minimum name match confidence to accept registry result","default":0.7},"timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Timeout","description":"Timeout for company registry lookup in seconds","default":30}},"type":"object","title":"SpiderCompanyDataConfig","description":"SpiderCompanyData configuration for company registry enrichment.\n\nEnriches companies with official registry data from:\n- US SEC EDGAR (CIK, officers, financials, SIC codes)\n- UK Companies House (registration #, directors, accounts)\n- EU VIES (VAT validation, company name, address)"},"SpiderCompanyDataJobPayload":{"properties":{"mode":{"allOf":[{"$ref":"#/components/schemas/CompanyDataMode"}],"description":"Operation mode: search, lookup, or vat","default":"search"},"name":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Name","description":"Company name to search for","example":"Apple Inc"},"identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Identifier","description":"Registry-specific company identifier (CIK for US, Company Number for UK)","example":"0000320193"},"country":{"anyOf":[{"type":"string","maxLength":2,"minLength":2},{"type":"null"}],"title":"Country","description":"Country code (ISO 3166-1 alpha-2: US, GB, EU, etc.)","example":"US"},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number","description":"EU VAT number with country prefix (e.g., GB123456789)","example":"GB123456789"},"limit":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Limit","description":"Maximum number of results to return (for search mode)","default":10},"include_financials":{"type":"boolean","title":"Include Financials","description":"Extract financial data from company filings (UK only)","default":false},"financials_mode":{"anyOf":[{"type":"string","enum":["url_only","ixbrl","regex","ocr","auto"]},{"type":"null"}],"title":"Financials Mode","description":"Financials extraction mode: url_only (free), ixbrl (free, accurate), regex (free, ~70%), ocr (paid, ~95%), auto (default: ixbrl→regex→ocr)"},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false}},"type":"object","title":"SpiderCompanyDataJobPayload","description":"Payload schema for SpiderCompanyData jobs (v2.41.0)\n\nMulti-country company data enrichment from public registries:\n- US SEC EDGAR (free, no API key required)\n- UK Companies House (free API key required)\n- EU VIES VAT Validation (free, no API key required)\n\n## Modes\n- **search**: Search companies by name\n- **lookup**: Lookup company by registry ID\n- **vat**: Validate EU VAT number\n\n## Examples\n\nSearch by name:\n```json\n{\"name\": \"Apple Inc\", \"country\": \"US\"}\n```\n\nLookup by ID:\n```json\n{\"identifier\": \"0000320193\", \"country\": \"US\", \"mode\": \"lookup\"}\n```\n\nVAT validation:\n```json\n{\"vat_number\": \"GB123456789\", \"mode\": \"vat\"}\n```"},"SpiderCompanyDataJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderCompanyDataJobPayload"}],"description":"Job payload with company search/lookup parameters"},"priority":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Priority","description":"Job priority (1=lowest, 10=highest)","default":5}},"type":"object","required":["payload"],"title":"SpiderCompanyDataJobSubmit","description":"Job submission wrapper for SpiderCompanyData jobs."},"SpiderFacebookPageJobPayload":{"properties":{"url":{"type":"string","title":"Url","description":"Facebook page URL (e.g., https://www.facebook.com/instagram)","example":"https://www.facebook.com/instagram"},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only). Use for testing new code before deploying to production.","default":false}},"type":"object","required":["url"],"title":"SpiderFacebookPageJobPayload","description":"Payload schema for SpiderFacebookPage business page scraping jobs (v2.29.0)\n\nScrapes Facebook business page data including contact info, hours, ratings.\nUses SpiderProxy for IP rotation and SpiderMedia for profile picture storage."},"SpiderFacebookPageJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderFacebookPageJobPayload"}],"description":"SpiderFacebookPage job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderFacebookPageJobSubmit","description":"Submit a SpiderFacebookPage business page scraping job (v2.29.0)\n\nScrapes Facebook business page data including:\n- Profile picture (stored in SpiderMedia)\n- Contact info (email, phone, address, website)\n- Business hours and price range\n- Ratings, likes, and followers\n- Category and services\n\nUses SpiderProxy for IP rotation to avoid Facebook blocking.","examples":[{"payload":{"url":"https://www.facebook.com/instagram"},"priority":0},{"payload":{"test":true,"url":"https://www.facebook.com/pizzaburgbd"},"priority":5}]},"SpiderLandingJobPayload":{"properties":{"url":{"type":"string","title":"Url","description":"Landing page URL (often a tracking/redirect URL from Facebook ads)","example":"https://tracking.example.com/redirect?ad_id=123"},"ad_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ad Id","description":"Facebook Ad ID for correlation with ad library data"},"options":{"anyOf":[{"$ref":"#/components/schemas/SpiderLandingOptions"},{"type":"null"}],"description":"Capture configuration options (all enabled by default)"},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false}},"type":"object","required":["url"],"title":"SpiderLandingJobPayload","description":"Payload schema for SpiderLanding landing page capture jobs (v2.32.0)\n\nCaptures landing pages from ad URLs with:\n- Screenshots (above-fold + full-page)\n- Self-contained HTML with embedded assets (via monolith)\n- AI-extracted marketing content (headlines, CTAs, testimonials)","examples":[{"ad_id":"fb_123456789","options":{"capture_full_page":true,"capture_html_bundle":true,"capture_screenshot":true,"extract_content":true},"url":"https://landing-page.com/offer"},{"test":true,"url":"https://tracking.example.com/ad?id=123"}]},"SpiderLandingJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderLandingJobPayload"}],"description":"SpiderLanding job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderLandingJobSubmit","description":"Submit a SpiderLanding landing page capture job (v2.32.0)\n\nCaptures landing pages from ad URLs with:\n- Screenshots (above-fold + full-page)\n- Self-contained HTML with embedded assets (via monolith)\n- AI-extracted marketing content (headlines, CTAs, testimonials, design patterns)\n\nPerfect for competitor landing page analysis and ad intelligence.\nFiles stored in SpiderMedia (per-client SeaweedFS bucket).","examples":[{"payload":{"ad_id":"fb_123456789","url":"https://landing-page.com/offer"},"priority":5},{"payload":{"options":{"capture_full_page":true,"capture_html_bundle":true,"capture_screenshot":true,"dismiss_popups":true,"extract_content":true},"test":true,"url":"https://tracking.example.com/ad?id=123"},"priority":0}]},"SpiderLandingOptions":{"properties":{"capture_screenshot":{"type":"boolean","title":"Capture Screenshot","description":"Capture above-fold screenshot","default":true},"capture_full_page":{"type":"boolean","title":"Capture Full Page","description":"Capture full-page screenshot","default":true},"capture_html_bundle":{"type":"boolean","title":"Capture Html Bundle","description":"Download self-contained HTML with embedded assets","default":true},"extract_content":{"type":"boolean","title":"Extract Content","description":"Use AI to extract marketing content (headlines, CTAs, testimonials)","default":true},"dismiss_popups":{"type":"boolean","title":"Dismiss Popups","description":"Attempt to dismiss cookie banners and popups via AI","default":true},"scroll_for_lazy_load":{"type":"boolean","title":"Scroll For Lazy Load","description":"Scroll page to trigger lazy loading before capture","default":true},"viewport":{"anyOf":[{"$ref":"#/components/schemas/SpiderLandingViewport"},{"type":"null"}],"description":"Custom viewport dimensions"},"timeout_seconds":{"type":"integer","maximum":300.0,"minimum":10.0,"title":"Timeout Seconds","description":"Maximum time for page capture","default":60},"max_redirects":{"type":"integer","maximum":20.0,"minimum":1.0,"title":"Max Redirects","description":"Maximum redirects to follow","default":10}},"type":"object","title":"SpiderLandingOptions","description":"Capture options for SpiderLanding jobs"},"SpiderLandingViewport":{"properties":{"width":{"type":"integer","maximum":3840.0,"minimum":320.0,"title":"Width","description":"Viewport width in pixels","default":1440},"height":{"type":"integer","maximum":2160.0,"minimum":480.0,"title":"Height","description":"Viewport height in pixels","default":900}},"type":"object","title":"SpiderLandingViewport","description":"Viewport configuration for screenshot capture"},"SpiderMailAction":{"type":"string","enum":["send","reply","forward"],"title":"SpiderMailAction","description":"Action types for SpiderMail jobs (write operations via queue)."},"SpiderMailJobPayload":{"properties":{"action":{"allOf":[{"$ref":"#/components/schemas/SpiderMailAction"}],"description":"Email action: send, reply, or forward"},"from_email":{"type":"string","title":"From Email","description":"Sender email address (must be a registered mailbox)","example":"alice@company.com"},"to":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"To","description":"Recipient email addresses","example":["bob@prospect.com"]},"cc":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Cc","description":"CC recipients"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject","description":"Email subject line (required for send, optional for reply/forward)","example":"Quick question about your services"},"body_text":{"type":"string","minLength":1,"title":"Body Text","description":"Plain text email body"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html","description":"Optional HTML email body"},"attachments":{"anyOf":[{"items":{"$ref":"#/components/schemas/AttachmentUpload"},"type":"array"},{"type":"null"}],"title":"Attachments","description":"Attachments to send with the email (base64-encoded)"},"reply_to_message_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reply To Message Id","description":"Database ID of the message to reply to (required for reply/forward actions)"},"reply_all":{"type":"boolean","title":"Reply All","description":"Reply to all recipients (only used with reply action)","default":false},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false}},"type":"object","required":["action","from_email","body_text"],"title":"SpiderMailJobPayload","description":"Payload schema for SpiderMail jobs (v2.40.0)\n\nUsed for send, reply, and forward operations that go through the job queue.\nRead operations (inbox, search, thread) use direct GET endpoints instead.","examples":[{"action":"send","body_text":"Hi Bob,\n\nI noticed your company...","from_email":"alice@company.com","subject":"Quick question","to":["bob@prospect.com"]},{"action":"reply","body_text":"Thanks for getting back to me...","from_email":"alice@company.com","reply_to_message_id":1234}]},"SpiderMailJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderMailJobPayload"}],"description":"SpiderMail job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10)","default":0}},"type":"object","required":["payload"],"title":"SpiderMailJobSubmit","description":"Submit a SpiderMail job (send/reply/forward)."},"SpiderMapsBusiness":{"properties":{"name":{"type":"string","title":"Name","description":"Business name","default":""},"place_id":{"type":"string","title":"Place Id","description":"Google Place ID","default":""},"rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rating","description":"Average rating (1.0-5.0)"},"reviews_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews Count","description":"Number of reviews"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address","description":"Full address"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Phone number (raw format)"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website","description":"Business website"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories","description":"Business categories/types"},"coordinates":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Coordinates","description":"Latitude and longitude"},"link":{"type":"string","title":"Link","description":"Google Maps link","default":""},"business_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Status","description":"OPERATIONAL, CLOSED_TEMPORARILY, etc."},"price_range":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Range","description":"Price range: $, $$, $$$, $$$$"},"working_hours":{"anyOf":[{"type":"string"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Working Hours","description":"Working hours - either structured dict or summary string"},"phone_e164":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone E164","description":"Phone in E.164 format: +15551234567"},"phone_national":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone National","description":"Phone in national format: (555) 123-4567"},"phone_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone Type","description":"Phone type: MOBILE, FIXED_LINE, VOIP, TOLL_FREE, etc."},"phone_valid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Phone Valid","description":"Whether phone number format is valid"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Primary business image URL (stored in SeaweedFS)"},"fuzziq_unique":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique","description":"True if this business is unique (not seen before in client's canonical database)"}},"type":"object","title":"SpiderMapsBusiness","description":"Single business listing from Google Maps"},"SpiderMapsConfig":{"properties":{"max_results":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Max Results","description":"Maximum businesses to discover per location","default":100},"search_radius_km":{"type":"number","maximum":50.0,"minimum":0.5,"title":"Search Radius Km","description":"Search radius in kilometers","default":10.0},"include_place_details":{"type":"boolean","title":"Include Place Details","description":"Fetch additional Google Place details","default":true}},"type":"object","title":"SpiderMapsConfig","description":"SpiderMaps configuration for location-based discovery."},"SpiderMapsData":{"properties":{"query":{"type":"string","title":"Query","description":"Search query used","default":""},"results_count":{"type":"integer","title":"Results Count","description":"Number of businesses returned","default":0},"businesses":{"items":{"$ref":"#/components/schemas/SpiderMapsBusiness"},"type":"array","title":"Businesses","description":"Array of business listings"},"metadata":{"type":"object","title":"Metadata","description":"Search metadata"}},"type":"object","title":"SpiderMapsData","description":"SpiderMaps scraping results - business listings from Google Maps","example":{"businesses":[{"address":"123 Main St, New York, NY 10001","business_status":"OPERATIONAL","categories":["Italian restaurant","Fine dining"],"coordinates":{"latitude":40.7128,"longitude":-74.006},"image_url":"https://media.spideriq.ai/vibe/gmaps_ChIJxxxxxx_1.jpg","link":"https://www.google.com/maps/place/...","name":"Example Restaurant","phone":"+1-212-555-0100","phone_e164":"+12125550100","phone_national":"(212) 555-0100","phone_type":"FIXED_LINE","phone_valid":true,"place_id":"ChIJxxxxxx","price_range":"$$$","rating":4.5,"reviews_count":1234,"website":"https://example-restaurant.com","working_hours":{"Monday":"5-10 PM","Tuesday":"5-10 PM"}}],"metadata":{"extract_reviews":false,"language":"en","max_results":20},"query":"restaurants in Manhattan, NY","results_count":20}},"SpiderMapsEnrichJobPayload":{"properties":{"google_place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Place Id","description":"Google Place ID (ChIJxxxx)"},"google_cid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Cid","description":"Google CID (0x...:0x...)"},"place_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Place Url","description":"Direct Google Maps place URL"},"original_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Original Data","description":"Original SpiderMaps result to merge with"},"exclude_vps":{"items":{"type":"string"},"type":"array","title":"Exclude Vps","description":"VPS IDs to exclude for IP diversity"},"use_proxy":{"type":"boolean","title":"Use Proxy","description":"Use mobile proxy for IP diversity","default":true},"enrich_options":{"anyOf":[{"$ref":"#/components/schemas/SpiderMapsEnrichOptions"},{"type":"null"}],"description":"Enrichment configuration"},"snowball":{"anyOf":[{"$ref":"#/components/schemas/SpiderMapsEnrichSnowball"},{"type":"null"}],"description":"Recursive discovery settings"},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false}},"type":"object","title":"SpiderMapsEnrichJobPayload","description":"Payload schema for SpiderMapsEnrich deep place enrichment jobs (v1.1.0)\n\nVisits individual Google Maps place pages to extract detailed data including:\n- Review analysis with negative review detection\n- Photos uploaded to SpiderMedia\n- Review tags, menu link, street view URL\n- Popular times / busy hours\n- Owner responses to reviews\n\nRequires at least one place identifier (google_place_id, google_cid, or place_url).","examples":[{"enrich_options":{"photos":{"enabled":true,"max_count":10},"reviews":{"enabled":true,"max_count":50,"negative_only":false},"store_images":true},"google_place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4"},{"enrich_options":{"reviews":{"enabled":true,"max_count":20,"negative_only":true}},"place_url":"https://www.google.com/maps/place/Example+Restaurant/@40.7128,-74.0060","test":true}]},"SpiderMapsEnrichJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderMapsEnrichJobPayload"}],"description":"SpiderMapsEnrich job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":5}},"type":"object","required":["payload"],"title":"SpiderMapsEnrichJobSubmit","description":"Submit a SpiderMapsEnrich deep place enrichment job\n\nExtract detailed place data by visiting individual Google Maps pages:\n- Review analysis with negative review detection\n- Photos uploaded to client's SpiderMedia bucket\n- Review tags, menu link, street view URL\n- Popular times / busy hours\n- Owner responses tracking\n\nRequires at least one place identifier.","examples":[{"payload":{"enrich_options":{"photos":{"enabled":true,"max_count":5},"reviews":{"enabled":true,"max_count":50,"negative_only":true},"store_images":true},"google_place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4"},"priority":5}]},"SpiderMapsEnrichOptions":{"properties":{"reviews":{"anyOf":[{"$ref":"#/components/schemas/SpiderMapsEnrichReviewsConfig"},{"type":"null"}],"description":"Review extraction settings"},"photos":{"anyOf":[{"$ref":"#/components/schemas/SpiderMapsEnrichPhotosConfig"},{"type":"null"}],"description":"Photo extraction settings"},"popular_times":{"type":"boolean","title":"Popular Times","description":"Extract busy hours data","default":true},"store_images":{"type":"boolean","title":"Store Images","description":"Upload photos to SpiderMedia","default":true}},"type":"object","title":"SpiderMapsEnrichOptions","description":"Enrichment options for SpiderMapsEnrich jobs"},"SpiderMapsEnrichPhotosConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable photo extraction","default":true},"max_count":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Max Count","description":"Maximum photos to extract","default":10}},"type":"object","title":"SpiderMapsEnrichPhotosConfig","description":"Configuration for photo extraction in SpiderMapsEnrich"},"SpiderMapsEnrichReviewsConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable review extraction","default":true},"max_count":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Max Count","description":"Maximum reviews to extract (max: 200)","default":50},"include_all":{"type":"boolean","title":"Include All","description":"Include all reviews in response","default":true},"negative_only":{"type":"boolean","title":"Negative Only","description":"Only return 1-2 star reviews","default":false}},"type":"object","title":"SpiderMapsEnrichReviewsConfig","description":"Configuration for review extraction in SpiderMapsEnrich"},"SpiderMapsEnrichSnowball":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable recursive related place discovery","default":false},"max_depth":{"type":"integer","maximum":3.0,"minimum":1.0,"title":"Max Depth","description":"Maximum recursion depth","default":2},"max_places_per_seed":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Max Places Per Seed","description":"Max related places per seed","default":10}},"type":"object","title":"SpiderMapsEnrichSnowball","description":"Snowball expansion configuration for recursive related place discovery"},"SpiderMapsJobPayload":{"properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Direct Google Maps URL (e.g., https://www.google.com/maps/place/...)","example":"https://www.google.com/maps/place/..."},"search_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Query","description":"Search query (alternative to URL). Example: 'restaurants in New York'","example":"coffee shops in San Francisco"},"max_results":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Max Results","description":"Maximum number of results to scrape (1-100). flowstest-1.2-r2: default raised from 10 to 20 to match the @spideriq/mcp search_google_maps tool default; le=100 added so the route docstring's 'Input should be less than or equal to 100' is now actually enforced.","default":20},"extract_reviews":{"type":"boolean","title":"Extract Reviews","description":"Extract customer reviews (increases processing time)","default":false},"extract_photos":{"type":"boolean","title":"Extract Photos","description":"Extract photo URLs from listings","default":false},"lang":{"type":"string","title":"Lang","description":"Language code for Google Maps (en, es, fr, de, it, pt, etc.)","default":"en"},"country":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Country","description":"Country name or ISO code (e.g. 'Germany', 'PT') appended to the search query for geo-disambiguation. Read by the worker as `country` (NOT `country_code`)."},"headless":{"type":"boolean","title":"Headless","description":"Run browser in headless mode (recommended for production)","default":true},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only). Use for testing new code before deploying to production.","default":false},"store_images":{"type":"boolean","title":"Store Images","description":"Store first image for each business in SeaweedFS. Returns permanent URL in image_url field.","default":true},"validate_phones":{"type":"boolean","title":"Validate Phones","description":"Validate and format phone numbers using libphonenumber. Returns phone_e164, phone_type, phone_valid fields.","default":true},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"Enable FuzzIQ deduplication. Adds results to client's canonical database and marks duplicates. Default: uses client setting."},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return only unique records (filter out duplicates). Default: uses client setting."},"skip_proxy":{"type":"boolean","title":"Skip Proxy","description":"Skip SpiderProxy mobile proxy assignment. Uses datacenter IP instead. For A/B testing proxy effectiveness.","default":false},"workflow":{"anyOf":[{"$ref":"#/components/schemas/WorkflowConfig"},{"type":"null"}],"description":"Workflow configuration for automatic job chaining. When enabled, automatically triggers SpiderSite for each business domain found, then SpiderVerify for extracted emails. Omit or set to null for standard behavior (SpiderMaps only, no chaining)."}},"type":"object","title":"SpiderMapsJobPayload","description":"Payload schema for SpiderMaps business scraping jobs (v2.0.0)\n\nScrapes business listings from maps with reviews and photos.","examples":[{"extract_photos":false,"extract_reviews":true,"headless":true,"lang":"en","max_results":20,"search_query":"italian restaurants in Boston","store_images":true,"validate_phones":true},{"extract_photos":true,"extract_reviews":false,"headless":true,"lang":"en","max_results":10,"url":"https://www.google.com/maps/place/..."},{"extract_photos":true,"extract_reviews":true,"headless":true,"lang":"es","max_results":30,"search_query":"restaurantes en Madrid"},{"extract_photos":false,"extract_reviews":false,"headless":true,"lang":"en","max_results":100,"search_query":"coffee shops in San Francisco"},{"extract_photos":true,"extract_reviews":true,"headless":true,"lang":"fr","max_results":50,"search_query":"hotels in Paris"},{"max_results":20,"search_query":"coffee shops in Boston","workflow":{"filter_review_sites":true,"filter_social_media":true,"spidersite":{"enabled":true,"extract_company_info":true,"max_pages":10},"spiderverify":{"enabled":true,"max_emails_per_business":5}}}]},"SpiderMapsJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderMapsJobPayload"}],"description":"SpiderMaps job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderMapsJobSubmit","description":"Submit a SpiderMaps business scraping job\n\nScrape business listings from maps with contact info, reviews, and photos.\nZero AI tokens used. Processing time: 30-90 seconds for 20 results.","examples":[{"payload":{"extract_reviews":true,"lang":"en","max_results":20,"search_query":"italian restaurants in Boston"},"priority":5}]},"SpiderPeopleData":{"properties":{"mode":{"type":"string","enum":["profile","search","company"],"title":"Mode","description":"Operation mode"},"profile":{"anyOf":[{"$ref":"#/components/schemas/SpiderPeopleProfile"},{"type":"null"}],"description":"LinkedIn profile (profile mode, when nested rendering is requested)"},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query","description":"Search query used (search mode)"},"results_count":{"type":"integer","title":"Results Count","description":"Number of results (search mode) — also used as employees count for company mode","default":0},"profiles":{"items":{"$ref":"#/components/schemas/SpiderPeopleSearchResult"},"type":"array","title":"Profiles","description":"Search results (search mode)"},"company_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Url","description":"LinkedIn company URL (company mode)"},"company_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Slug","description":"LinkedIn company slug (company mode, e.g. 'pleo')"},"employees":{"items":{"$ref":"#/components/schemas/SpiderPeopleEmployee"},"type":"array","title":"Employees","description":"Employee records (company mode)"},"employees_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Employees Count","description":"Number of employees returned (company mode)"},"cached":{"type":"boolean","title":"Cached","description":"Whether result was from cache","default":false},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Data source: brightdata (profile mode), brightdata_google (search mode), apify (company mode)"},"metadata":{"type":"object","title":"Metadata","description":"Additional metadata"}},"type":"object","required":["mode"],"title":"SpiderPeopleData","description":"SpiderPeople job results.\n\nThe worker emits a flat dict with mode-specific fields populated and the\nothers null. This typed view declares only the load-bearing structured\narrays (profiles / employees / experience / education); additional flat\nmetadata fields surfaced by the worker (city, country_code, banner_image,\nschema_version, etc.) are accepted via model_config and surface unchanged\nin the API response. Tracked for full alignment under task 277f56a1.","examples":[{"name":"Profile Mode","value":{"cached":false,"mode":"profile","profile":{"connections":500,"current_company":"Google","education":[],"experience":[],"first_name":"John","full_name":"John Doe","headline":"Senior Software Engineer at Google","last_name":"Doe","linkedin_id":"john-doe","linkedin_url":"https://www.linkedin.com/in/john-doe","location":"San Francisco, CA"},"source":"brightdata"}},{"name":"Search Mode","value":{"cached":false,"mode":"search","profiles":[{"headline":"Machine Learning Engineer","linkedin_id":"ai-engineer-1","linkedin_url":"https://www.linkedin.com/in/ai-engineer-1","name":"AI Engineer 1"}],"query":"5 AI engineers in Israel","results_count":5,"source":"brightdata_google"}},{"name":"Company Mode","value":{"cached":false,"company_slug":"pleo","company_url":"https://www.linkedin.com/company/pleo","employees":[{"full_name":"Jane Doe","linkedin_id":"jdoe","linkedin_url":"https://www.linkedin.com/in/jdoe","location":"London, UK","open_profile":false,"premium":true,"title":"Director of Engineering"}],"employees_count":2,"mode":"company","source":"apify"}}]},"SpiderPeopleEducation":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"School/institution name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Degree or field of study"},"start_year":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Year","description":"Start year"},"end_year":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Year","description":"End year"}},"type":"object","title":"SpiderPeopleEducation","description":"Education entry."},"SpiderPeopleEmployee":{"properties":{"linkedin_url":{"type":"string","title":"Linkedin Url","description":"Employee LinkedIn profile URL"},"linkedin_id":{"type":"string","title":"Linkedin Id","description":"Employee LinkedIn ID"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name","description":"Full name"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"First name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"Last name"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Job title at the company"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location","description":"Location string"},"profile_pic_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Pic Url","description":"Profile picture URL"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Email (only when profile_mode='full_email')"},"skills":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Skills","description":"Skills array (only when profile_mode='full' or 'full_email')"},"education":{"anyOf":[{"items":{"$ref":"#/components/schemas/SpiderPeopleEducation"},"type":"array"},{"type":"null"}],"title":"Education","description":"Education entries (only when profile_mode='full' or 'full_email')"},"experience":{"anyOf":[{"items":{"$ref":"#/components/schemas/SpiderPeopleExperience"},"type":"array"},{"type":"null"}],"title":"Experience","description":"Experience entries (only when profile_mode='full' or 'full_email')"},"open_profile":{"type":"boolean","title":"Open Profile","description":"Whether the LinkedIn account allows InMail without connection","default":false},"premium":{"type":"boolean","title":"Premium","description":"Whether the LinkedIn account is Premium","default":false}},"type":"object","required":["linkedin_url","linkedin_id"],"title":"SpiderPeopleEmployee","description":"Single employee record (company mode, Apify harvestapi output)."},"SpiderPeopleExperience":{"properties":{"company":{"type":"string","title":"Company","description":"Company name","default":""},"title":{"type":"string","title":"Title","description":"Job title","default":""},"duration":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Duration","description":"Duration at role"},"description_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description Html","description":"Role description"},"company_logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Logo Url","description":"Company logo URL (Bright Data CDN)"}},"type":"object","title":"SpiderPeopleExperience","description":"Work experience entry."},"SpiderPeopleJobPayload":{"properties":{"mode":{"type":"string","enum":["profile","search","company"],"title":"Mode","description":"Operation mode: 'profile' (get profile), 'search' (find profiles), 'company' (extract employees)","default":"profile"},"linkedin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin Url","description":"LinkedIn profile URL (required for profile mode)","example":"https://www.linkedin.com/in/john-doe"},"search_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Query","description":"Natural language search query (required for search mode)","example":"5 AI engineers in Israel"},"search_limit":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Search Limit","description":"Maximum number of profiles to return in search (1-50)","default":10},"country_code":{"anyOf":[{"type":"string","maxLength":2},{"type":"null"}],"title":"Country Code","description":"Two-letter ISO country code for SERP locale (search mode, e.g. 'DE', 'US')"},"company_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Url","description":"LinkedIn company URL (required for company mode)","example":"https://www.linkedin.com/company/pleo"},"max_employees":{"type":"integer","maximum":2000.0,"minimum":1.0,"title":"Max Employees","description":"Maximum employees to extract in company mode (1-2000)","default":100},"profile_mode":{"anyOf":[{"type":"string","enum":["short","full","full_email"]},{"type":"null"}],"title":"Profile Mode","description":"Employee detail level: 'short' ($4/1K), 'full' ($8/1K), 'full_email' ($12/1K)","default":"short"},"person_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Person Name","description":"Person's name (optional context, used by callers but not by the WindMill scripts)"},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"Enable FuzzIQ deduplication. Adds LinkedIn profiles to client's canonical database. Default: uses client setting."},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return only unique records (filter out duplicates). Default: uses client setting."}},"type":"object","title":"SpiderPeopleJobPayload","description":"Payload schema for SpiderPeople LinkedIn jobs.\n\nThree modes, all routed to WindMill scripts under f/spideriq/spiderpeople_*:\n- profile: fetch a LinkedIn profile by URL (Bright Data datasets)\n- search: natural-language profile search (Bright Data SERP)\n- company: extract employees from a company page (Apify harvestapi)","examples":[{"linkedin_url":"https://www.linkedin.com/in/john-doe","mode":"profile"},{"country_code":"IL","mode":"search","search_limit":10,"search_query":"5 AI engineers in Israel"},{"company_url":"https://www.linkedin.com/company/pleo","max_employees":50,"mode":"company","profile_mode":"short"}]},"SpiderPeopleJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderPeopleJobPayload"}],"description":"SpiderPeople job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderPeopleJobSubmit","description":"Submit a SpiderPeople LinkedIn job.\n\nThree modes:\n- profile: fetch profile data by URL (Bright Data)\n- search: find profiles using natural language (Bright Data SERP)\n- company: extract employees from a company page (Apify)","examples":[{"payload":{"linkedin_url":"https://www.linkedin.com/in/john-doe","mode":"profile"},"priority":5},{"payload":{"mode":"search","search_limit":10,"search_query":"5 AI engineers in Israel"},"priority":5},{"payload":{"company_url":"https://www.linkedin.com/company/pleo","max_employees":50,"mode":"company","profile_mode":"short"},"priority":5}]},"SpiderPeopleProfile":{"properties":{"linkedin_url":{"type":"string","title":"Linkedin Url","description":"LinkedIn profile URL"},"linkedin_id":{"type":"string","title":"Linkedin Id","description":"LinkedIn username"},"first_name":{"type":"string","title":"First Name","description":"First name","default":""},"last_name":{"type":"string","title":"Last Name","description":"Last name","default":""},"full_name":{"type":"string","title":"Full Name","description":"Full name","default":""},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline","description":"Professional headline"},"about":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"About","description":"About/summary section"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location","description":"Location"},"current_company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Company","description":"Current company"},"profile_pic_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Pic Url","description":"Profile picture URL"},"connections":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Connections","description":"Number of connections"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Number of followers"},"experience":{"items":{"$ref":"#/components/schemas/SpiderPeopleExperience"},"type":"array","title":"Experience"},"education":{"items":{"$ref":"#/components/schemas/SpiderPeopleEducation"},"type":"array","title":"Education"}},"type":"object","required":["linkedin_url","linkedin_id"],"title":"SpiderPeopleProfile","description":"LinkedIn profile data (profile mode)."},"SpiderPeopleSearchResult":{"properties":{"linkedin_url":{"type":"string","title":"Linkedin Url","description":"LinkedIn profile URL"},"linkedin_id":{"type":"string","title":"Linkedin Id","description":"LinkedIn username"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Person's name"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Search result title"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline","description":"Professional headline"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location","description":"Location"},"snippet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Snippet","description":"Search result snippet"}},"type":"object","required":["linkedin_url","linkedin_id"],"title":"SpiderPeopleSearchResult","description":"Single search result (search mode)."},"SpiderPhoneAutomatorConfig":{"properties":{"action_timeout":{"type":"number","maximum":120.0,"minimum":5.0,"title":"Action Timeout","description":"Timeout for each action","default":30.0},"max_retries":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Max Retries","description":"Maximum retries on failure","default":3},"max_items":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Max Items","description":"Maximum items to extract","default":100},"capture_screenshots":{"type":"boolean","title":"Capture Screenshots","description":"Capture screenshots for debugging","default":true},"behavior":{"anyOf":[{"$ref":"#/components/schemas/SpiderPhoneBehaviorConfig"},{"type":"null"}],"description":"Human behavior configuration"}},"type":"object","title":"SpiderPhoneAutomatorConfig","description":"Automator configuration"},"SpiderPhoneBehaviorConfig":{"properties":{"min_action_delay":{"type":"number","maximum":5.0,"minimum":0.1,"title":"Min Action Delay","description":"Minimum delay between actions (seconds)","default":0.5},"max_action_delay":{"type":"number","maximum":10.0,"minimum":0.5,"title":"Max Action Delay","description":"Maximum delay between actions (seconds)","default":2.0},"typo_probability":{"type":"number","maximum":0.1,"minimum":0.0,"title":"Typo Probability","description":"Probability of typing typo (then backspace)","default":0.01},"break_probability":{"type":"number","maximum":0.2,"minimum":0.0,"title":"Break Probability","description":"Probability of taking a short break","default":0.05},"slower_at_night":{"type":"boolean","title":"Slower At Night","description":"Slow down automation during night hours","default":true}},"type":"object","title":"SpiderPhoneBehaviorConfig","description":"Human-like behavior configuration for anti-detection"},"SpiderPhoneData":{"properties":{"success":{"type":"boolean","title":"Success","description":"Whether automation succeeded"},"platform":{"type":"string","title":"Platform","description":"Platform automated"},"action":{"type":"string","title":"Action","description":"Action performed"},"data":{"type":"object","title":"Data","description":"Extracted data"},"items_extracted":{"type":"integer","title":"Items Extracted","description":"Number of items extracted","default":0},"phone_id":{"type":"string","title":"Phone Id","description":"Phone that processed the job","default":""},"duration_seconds":{"type":"number","title":"Duration Seconds","description":"Processing time","default":0},"actions_performed":{"type":"integer","title":"Actions Performed","description":"Number of actions performed","default":0},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if failed"},"error_screenshot":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Screenshot","description":"Screenshot path if error"}},"type":"object","required":["success","platform","action"],"title":"SpiderPhoneData","description":"SpiderPhone automation results\n\nContains extracted data from mobile platform automation.","examples":[{"name":"LinkedIn Profile","value":{"action":"scrape_profile","actions_performed":12,"data":{"about":"Building the future of AI...","connections":500,"education":[],"experience":[],"headline":"CEO at TechStartup","location":"San Francisco, CA","name":"John Doe"},"duration_seconds":15.5,"items_extracted":1,"phone_id":"phone-3","platform":"linkedin","success":true}},{"name":"Google Maps Search","value":{"action":"search_businesses","actions_performed":35,"data":{"businesses":[{"address":"123 Main St","name":"Example Restaurant","phone":"+1-555-123-4567","rating":4.5,"reviews_count":1234}],"query":"restaurants in NYC"},"duration_seconds":45.2,"items_extracted":20,"phone_id":"phone-1","platform":"googlemaps","success":true}}]},"SpiderPhoneJobPayload":{"properties":{"platform":{"allOf":[{"$ref":"#/components/schemas/SpiderPhonePlatform"}],"description":"Target platform: linkedin, instagram, tiktok, googlemaps, facebook"},"action":{"type":"string","title":"Action","description":"Platform-specific action to perform"},"params":{"type":"object","title":"Params","description":"Action-specific parameters"},"config":{"anyOf":[{"$ref":"#/components/schemas/SpiderPhoneAutomatorConfig"},{"type":"null"}],"description":"Optional automator configuration"},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false}},"type":"object","required":["platform","action"],"title":"SpiderPhoneJobPayload","description":"Payload schema for SpiderPhone mobile automation jobs (v2.19.0)\n\nFeatures:\n- Mobile automation using Android phone farm (10 phones)\n- Support for LinkedIn, Instagram, TikTok, Google Maps, Facebook\n- Human-like behavior patterns for anti-detection\n- Connection pooling with health monitoring\n\nArchitecture:\n- Phones connected via WireGuard VPN (10.10.0.11-20)\n- uiautomator2 for Android automation\n- MacroDroid for on-device resilience","examples":[{"action":"scrape_profile","params":{"profile_name":"John Doe"},"platform":"linkedin"},{"action":"search_people","params":{"max_results":20,"query":"CEO tech startup"},"platform":"linkedin"},{"action":"scrape_profile","params":{"include_posts":true,"max_posts":10,"username":"techcompany"},"platform":"instagram"},{"action":"search_businesses","params":{"extract_details":true,"max_results":20,"query":"restaurants in NYC"},"platform":"googlemaps"},{"action":"search_users","params":{"max_results":10,"query":"tech influencer"},"platform":"tiktok"},{"action":"scrape_page","params":{"include_posts":true,"page_name":"TechStartup"},"platform":"facebook"}]},"SpiderPhoneJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderPhoneJobPayload"}],"description":"SpiderPhone job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderPhoneJobSubmit","description":"Submit a SpiderPhone mobile automation job\n\nAutomate mobile apps using Android phone farm:\n- LinkedIn: Profile scraping, people search, connection requests\n- Instagram: Profile scraping, hashtag search, followers\n- TikTok: Profile scraping, user search, video metrics\n- Google Maps: Business search, reviews extraction\n- Facebook: Profile/page scraping, search","examples":[{"payload":{"action":"scrape_profile","params":{"profile_name":"John Doe"},"platform":"linkedin"},"priority":5},{"payload":{"action":"search_businesses","params":{"max_results":20,"query":"restaurants in NYC"},"platform":"googlemaps"},"priority":5}]},"SpiderPhonePlatform":{"type":"string","enum":["linkedin","instagram","tiktok","googlemaps","facebook"],"title":"SpiderPhonePlatform","description":"Supported mobile platforms"},"SpiderPublicInstagramJobPayload":{"properties":{"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username","description":"Instagram username (without @)","example":"natgeo"},"instagram_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram Url","description":"Instagram profile URL","example":"https://www.instagram.com/natgeo/"},"extract_contact_from_bio":{"type":"boolean","title":"Extract Contact From Bio","description":"Parse bio text for email/phone using regex patterns","default":true},"store_profile_image":{"type":"boolean","title":"Store Profile Image","description":"Upload profile image to SpiderMedia (client's bucket)","default":true},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only). Use for testing new code before deploying to production.","default":false}},"type":"object","title":"SpiderPublicInstagramJobPayload","description":"Payload schema for SpiderPublicInstagram profile scraping jobs (v2.30.0)\n\nExtracts public Instagram profile data including:\n- Bio and profile picture\n- Business contact details (email, phone) for business accounts\n- Follower/following counts\n- Regex parsing of bio for email/phone\n\nNo Instagram login required - uses direct API endpoint.\nMobile proxies recommended (datacenter IPs get blocked).","examples":[{"extract_contact_from_bio":true,"store_profile_image":true,"username":"natgeo"},{"instagram_url":"https://www.instagram.com/cristiano/","test":true}]},"SpiderPublicInstagramJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderPublicInstagramJobPayload"}],"description":"SpiderPublicInstagram job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderPublicInstagramJobSubmit","description":"Submit a SpiderPublicInstagram profile scraping job (v2.30.0)\n\nScrapes public Instagram profile data including:\n- Profile picture (stored in SpiderMedia)\n- Bio and contact details\n- Business email/phone (for business accounts)\n- Follower/following/post counts\n\nNo login required. Uses SpiderProxy for IP rotation.\n\nRate limits:\n- Max 200 requests/hour per IP\n- 3-10 second delays between requests","examples":[{"payload":{"username":"natgeo"},"priority":0},{"payload":{"instagram_url":"https://www.instagram.com/cristiano/","test":true},"priority":5}]},"SpiderPublicLinkedinJobPayload":{"properties":{"mode":{"type":"string","enum":["get_company","get_profile","search_companies","search_people"],"title":"Mode","description":"Operation mode: get_company, get_profile, search_companies, or search_people"},"public_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Id","description":"LinkedIn public ID (e.g., 'microsoft' for company, 'satya-nadella' for profile)","example":"microsoft"},"linkedin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin Url","description":"Full LinkedIn URL (company or profile)","example":"https://www.linkedin.com/company/microsoft/"},"keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Keywords","description":"Search keywords (required for search modes)","example":"AI startup funding"},"max_results":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Max Results","description":"Maximum search results to return","default":10},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location","description":"Location filter for people search (e.g., 'San Francisco Bay Area')"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","description":"Company filter for people search"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Title filter for people search (e.g., 'CEO', 'CTO')"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry","description":"Industry filter for company search"},"store_logo":{"type":"boolean","title":"Store Logo","description":"Upload company/profile image to SpiderMedia (client's bucket)","default":true},"include_posts":{"type":"boolean","title":"Include Posts","description":"Include recent posts/updates (increases processing time)","default":false},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only). Use for testing new code before deploying to production.","default":false},"skip_proxy":{"type":"boolean","title":"Skip Proxy","description":"Skip SpiderProxy mobile proxy (NOT RECOMMENDED - datacenter IPs blocked immediately by LinkedIn)","default":false}},"type":"object","required":["mode"],"title":"SpiderPublicLinkedinJobPayload","description":"Payload schema for SpiderPublicLinkedin scraping jobs (v2.31.0)\n\nUses LinkedIn Voyager API to extract:\n- Company profiles (name, description, size, industry, headquarters)\n- People profiles (headline, experience, education, skills)\n- Company search by keywords\n- People search by keywords with filters\n\nRequires LinkedIn account credentials (managed via admin API).\nMobile proxies mandatory (datacenter IPs blocked immediately by LinkedIn).\n\nRate limits: ~900 requests before warning, 30-50 safe per account/day.","examples":[{"mode":"get_company","public_id":"microsoft","store_logo":true},{"linkedin_url":"https://www.linkedin.com/in/satya-nadella/","mode":"get_profile","store_logo":true},{"industry":"Technology","keywords":"AI startup","max_results":20,"mode":"search_companies"},{"keywords":"CTO fintech","location":"New York","max_results":10,"mode":"search_people","test":true}]},"SpiderPublicLinkedinJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderPublicLinkedinJobPayload"}],"description":"SpiderPublicLinkedin job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderPublicLinkedinJobSubmit","description":"Submit a SpiderPublicLinkedin job (v2.31.0)\n\nScrapes LinkedIn data using Voyager API:\n- Company profiles (get_company)\n- People profiles (get_profile)\n- Company search (search_companies)\n- People search (search_people)\n\nRequires LinkedIn account credentials (managed via admin API).\nUses SpiderProxy for IP rotation (mobile proxies mandatory).\n\nRate limits:\n- ~900 requests before LinkedIn warning\n- Safe: 30-50 requests/day per account\n- New accounts: 7-day warmup (5 -> 50 requests/day)","examples":[{"payload":{"mode":"get_company","public_id":"microsoft"},"priority":0},{"payload":{"linkedin_url":"https://www.linkedin.com/in/satya-nadella/","mode":"get_profile","store_logo":true},"priority":5},{"payload":{"keywords":"AI startup funding","max_results":20,"mode":"search_companies","test":true},"priority":3}]},"SpiderSiteConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable website crawling stage","default":true},"max_pages":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Max Pages","description":"Maximum pages to crawl","default":10},"crawl_strategy":{"type":"string","enum":["bestfirst","bfs","dfs"],"title":"Crawl Strategy","description":"Crawling strategy","default":"bestfirst"},"target_pages":{"items":{"type":"string"},"type":"array","title":"Target Pages","description":"Page types to prioritize","default":["contact","about","team"]},"enable_spa":{"type":"boolean","title":"Enable Spa","description":"Enable SPA rendering","default":true},"spa_timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Spa Timeout","default":30},"extract_team":{"type":"boolean","title":"Extract Team","description":"Extract team members using AI","default":false},"extract_company_info":{"type":"boolean","title":"Extract Company Info","description":"Extract company info using AI","default":true},"timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Timeout","default":30}},"type":"object","title":"SpiderSiteConfig","description":"SpiderSite configuration for website crawling."},"SpiderSiteData":{"properties":{"url":{"type":"string","title":"Url","description":"Website URL crawled","default":""},"pages_crawled":{"type":"integer","title":"Pages Crawled","description":"Number of pages successfully crawled","default":0},"crawl_status":{"type":"string","title":"Crawl Status","description":"Crawl status: success, partial, failed","default":"unknown"},"emails":{"items":{"type":"string"},"type":"array","title":"Emails","description":"Email addresses found (filtered, no tracking emails)"},"phones":{"items":{"type":"object"},"type":"array","title":"Phones","description":"Phone numbers with validation (v2.12.0). Each contains: raw, e164, national, international, country_code, type, valid"},"addresses":{"items":{"type":"string"},"type":"array","title":"Addresses","description":"Physical addresses found"},"logo":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Logo","description":"Website logo (v2.12.0). Contains: url (SeaweedFS hosted), original_url, source, confidence, type"},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin","description":"LinkedIn profile/company URL"},"twitter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter","description":"Twitter/X profile URL"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Facebook","description":"Facebook page URL"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram","description":"Instagram profile URL"},"youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube","description":"YouTube channel URL"},"tiktok":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tiktok","description":"TikTok profile URL"},"github":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Github","description":"GitHub organization/user URL"},"pinterest":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pinterest","description":"Pinterest profile URL"},"snapchat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Snapchat","description":"Snapchat profile URL"},"reddit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reddit","description":"Reddit profile/subreddit URL"},"medium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Medium","description":"Medium profile URL"},"discord":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Discord","description":"Discord server invite URL"},"whatsapp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Whatsapp","description":"WhatsApp contact/business URL"},"telegram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram","description":"Telegram contact/channel URL"},"markdown_compendium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Markdown Compendium","description":"AI-generated markdown summary of website"},"compendium":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Compendium","description":"Compendium metadata (size, storage location, etc.)"},"company_vitals":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Company Vitals","description":"Company information (if AI extraction enabled)"},"pain_points":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Pain Points","description":"Business pain points identified (if AI enabled) - contains inferred_challenges and recent_mentions"},"lead_scoring":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Lead Scoring","description":"Lead scoring data (if AI enabled)"},"team_members":{"items":{"type":"object"},"type":"array","title":"Team Members","description":"Team members found (if AI enabled)"},"personalization_hooks":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Personalization Hooks","description":"Personalization data for outreach (if AI enabled)"},"custom_analysis":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Analysis","description":"Custom AI analysis result (if custom_ai_prompt was used)"},"metadata":{"type":"object","title":"Metadata","description":"Crawl metadata and statistics"},"meta":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Meta","description":"Landing-page metadata grouped by namespace: core (title, description, keywords, canonical, lang, alternates), icons, og (OpenGraph), twitter (Twitter Card), dc (Dublin Core), mobile, microsoft, pragma (http-equiv), verification. Populated from the first crawled page only. Groups with no tags are omitted."},"schema_org":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Schema Org","description":"Raw JSON-LD Schema.org blocks from the landing page, filtered to business-relevant types (Organization, LocalBusiness + subtypes, Place, Product, Person, WebSite, ContactPoint, PostalAddress, GeoCoordinates, OpeningHoursSpecification). Contains: blocks (list of raw JSON-LD dicts), count, by_type (index: @type → block indices), dropped_types (content-type @types that existed but were filtered, e.g. Article, BreadcrumbList, Review)."}},"type":"object","title":"SpiderSiteData","description":"SpiderSite scraping results - FLAT structure (2-3 levels max)\n\nBreaking Change: Simplified from nested structure for easier integration\nOld: results.results.contact_info.social_media.linkedin\nNew: data.linkedin\n\nv2.12.0: phones now contain validated data with E.164 format, type, etc.\nv2.12.0: logo field added with hosted URL on SeaweedFS","example":{"addresses":["123 Main St, San Francisco, CA 94105"],"compendium":{"available":true,"chars":15234,"cleanup_level":"fit","storage_location":"inline"},"crawl_status":"success","emails":["contact@example.com","sales@example.com"],"facebook":"https://facebook.com/example","github":"https://github.com/example","instagram":"https://instagram.com/example","linkedin":"https://linkedin.com/company/example","logo":{"confidence":"high","original_url":"https://example.com/assets/logo.png","source":"selector_0","type":"image","url":"https://media.spideriq.ai/vibe/logo_example-com_abc123.png"},"markdown_compendium":"# Example Company\n\nLeading provider of...","metadata":{"browser_rendering_available":true,"crawl_strategy":"sitemap","sitemap_used":true,"spa_enabled":true,"total_emails_found":2,"total_phones_found":1},"pages_crawled":10,"phones":[{"calling_code":"1","country_code":"US","e164":"+15551234567","international":"+1 555 123 4567","national":"(555) 123-4567","possible":true,"raw":"+1-555-123-4567","type":"FIXED_LINE_OR_MOBILE","valid":true}],"team_members":[],"twitter":"https://twitter.com/example","url":"https://example.com","youtube":"https://youtube.com/example"}},"SpiderSiteJobPayload":{"properties":{"url":{"type":"string","title":"Url","description":"Website URL to crawl (must include https://)","example":"https://example.com"},"mode":{"type":"string","enum":["contacts","compendium","leads","full"],"title":"Mode","description":"Extraction mode with smart defaults. 'contacts' (5 pages, css+jsonld), 'compendium' (10 pages, markdown), 'leads' (50 pages, AI team extraction), 'full' (100 pages, everything enabled)","default":"contacts"},"overrides":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Overrides","description":"Override mode defaults. Example: {\"max_pages\": 20, \"strategies\": [\"css\", \"regex\"]}"},"max_pages":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Max Pages","description":"Maximum pages to crawl (1-50). Higher values = more data but slower processing.","default":10},"crawl_strategy":{"type":"string","enum":["bestfirst","bfs","dfs"],"title":"Crawl Strategy","description":"Crawling strategy: 'bestfirst' (intelligent prioritization), 'bfs' (breadth-first), 'dfs' (depth-first). Note: Sitemap-first used automatically if sitemap discovered.","default":"bestfirst"},"target_pages":{"items":{"type":"string"},"type":"array","title":"Target Pages","description":"Page types to prioritize. Works with 36+ languages (e.g., 'kontakt' in German, 'contacto' in Spanish)","default":["contact","about","team","news","blog"]},"enable_spa":{"type":"boolean","title":"Enable Spa","description":"Enable automatic SPA detection and Playwright rendering for JavaScript-heavy sites (React/Vue/Angular)","default":true},"spa_timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Spa Timeout","description":"Playwright page load timeout in seconds (only used when SPA detected). Increase for slow-loading sites.","default":30},"extract_team":{"type":"boolean","title":"Extract Team","description":"Extract team members using AI (~500 tokens). Includes names, titles, emails, LinkedIn profiles.","default":false},"extract_company_info":{"type":"boolean","title":"Extract Company Info","description":"Extract company summary using AI (~500 tokens). Includes services, target audience, industry.","default":false},"extract_pain_points":{"type":"boolean","title":"Extract Pain Points","description":"Analyze business challenges using AI (~500 tokens). Infers pain points from news, blog posts, job listings.","default":false},"product_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Description","description":"Your product description. Enables CHAMP lead scoring when combined with icp_description (+1,500 tokens)."},"icp_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icp Description","description":"Your ideal customer profile. Enables CHAMP lead scoring when combined with product_description (+1,500 tokens)."},"timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Timeout","description":"HTTP request timeout per page in seconds","default":30},"compendium":{"allOf":[{"$ref":"#/components/schemas/CompendiumConfig"}],"description":"Markdown compendium configuration. Generates intelligent, deduplicated markdown optimized for LLMs with 3-tier storage (inline <1MB, database 1-10MB, R2 >10MB)."},"custom_ai_prompt":{"anyOf":[{"$ref":"#/components/schemas/CustomAIPromptConfig"},{"type":"null"}],"description":"Custom AI prompt to analyze the compendium and return structured JSON. Requires compendium.enabled=true."},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only). Use for testing new code before deploying to production.","default":false},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"Enable FuzzIQ deduplication. Adds extracted emails to client's canonical database and marks duplicates. Default: uses client setting."},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return only unique records (filter out duplicates). Default: uses client setting."},"extraction":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Extraction","description":"v3.0 extraction config: strategies (css/jsonld/regex/microformat), llm_validation (bool). Example: {\"strategies\": [\"css\", \"jsonld\"]}"},"analyze":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Analyze","description":"v3.0 analysis config: company_vitals (bool), team_members (bool), pain_points (bool). Example: {\"company_vitals\": true}"},"persist":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Persist","description":"v3.0 persistence config: to_normalized (bool) - persist to client's normalized CRM schema. Example: {\"to_normalized\": true}"}},"type":"object","required":["url"],"title":"SpiderSiteJobPayload","description":"Payload schema for SpiderSite website lead generation jobs (v3.1.0)\n\nFeatures:\n- v3.1.0: Mode-based configuration with smart defaults (contacts/compendium/leads/full)\n- v3.0.0: Multi-strategy extraction (css/jsonld/regex/microformat)\n- v2.7.0: AI Context Engine with smart markdown compendiums and R2 storage\n- v2.4.0: SPA auto-detection with Playwright rendering\n- v2.3.0: Sitemap-first crawling with intelligent page selection\n- v2.2.1: AI opt-in defaults (0 tokens unless explicitly enabled)\n- v2.1.0: Multilingual support (36+ European languages)","examples":[{"mode":"contacts","url":"https://example.com"},{"mode":"leads","url":"https://example.com"},{"mode":"contacts","overrides":{"max_pages":15},"url":"https://example.com"},{"compendium":{"cleanup_level":"fit","enabled":true},"crawl_strategy":"bestfirst","enable_spa":true,"extract_company_info":false,"extract_pain_points":false,"extract_team":false,"max_pages":5,"spa_timeout":30,"target_pages":["contact","about","team"],"timeout":30,"url":"https://example.com"},{"compendium":{"cleanup_level":"minimal","enabled":true,"max_chars":50000},"crawl_strategy":"bestfirst","enable_spa":true,"extract_company_info":true,"extract_pain_points":false,"extract_team":false,"max_pages":10,"spa_timeout":30,"timeout":30,"url":"https://vayapin.com"},{"compendium":{"cleanup_level":"fit","enabled":true,"remove_duplicates":true},"crawl_strategy":"bestfirst","enable_spa":true,"extract_company_info":false,"extract_pain_points":false,"extract_team":true,"max_pages":15,"spa_timeout":30,"target_pages":["team","about","careers"],"timeout":30,"url":"https://techstartup.io"},{"compendium":{"cleanup_level":"citations","enabled":true,"max_chars":200000},"crawl_strategy":"bestfirst","enable_spa":true,"extract_company_info":true,"extract_pain_points":true,"extract_team":true,"max_pages":20,"spa_timeout":45,"target_pages":["contact","about","team","news","blog"],"timeout":45,"url":"https://saas-company.com"},{"compendium":{"cleanup_level":"raw","enabled":true,"include_in_response":true,"max_chars":500000},"crawl_strategy":"bestfirst","enable_spa":true,"extract_company_info":true,"extract_pain_points":true,"extract_team":true,"icp_description":"Mid-market B2B SaaS companies with 50-500 employees, $10M-$100M ARR, selling to enterprise","max_pages":25,"product_description":"AI-powered sales automation platform that helps B2B teams close deals 3x faster","spa_timeout":60,"target_pages":["contact","about","team","news","blog"],"timeout":60,"url":"https://enterprise-target.com"},{"compendium":{"cleanup_level":"minimal","enabled":true},"crawl_strategy":"bestfirst","enable_spa":true,"extract_company_info":true,"max_pages":10,"spa_timeout":60,"target_pages":["contact","about"],"timeout":45,"url":"https://react-spa-site.com"},{"compendium":{"enabled":false},"crawl_strategy":"bfs","enable_spa":false,"extract_pain_points":true,"max_pages":30,"target_pages":["kontakt","über-uns","team","nachrichten","blog"],"timeout":30,"url":"https://blog-heavy-site.com"}]},"SpiderSiteJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderSiteJobPayload"}],"description":"SpiderSite job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderSiteJobSubmit","description":"Submit a SpiderSite website lead generation job\n\nIntelligent website crawling with AI-powered lead extraction.\nAI usage: Opt-in (0 tokens by default). Processing time: 5-60 seconds.\n\nFeatures:\n- v2.4.0: SPA auto-detection with Playwright\n- v2.3.0: Sitemap-first crawling\n- v2.2.1: AI opt-in defaults (zero cost)\n- v2.1.0: Multilingual support (36+ languages)","examples":[{"payload":{"enable_spa":true,"extract_company_info":true,"max_pages":5,"url":"https://vayapin.com"},"priority":5}]},"SpiderVayapinJobPayload":{"properties":{"business_name":{"type":"string","title":"Business Name","description":"Business name for the VayaPin profile","example":"Restaurant Tight"},"country_code":{"type":"string","maxLength":2,"minLength":2,"title":"Country Code","description":"2-letter ISO country code (e.g., 'DK', 'US', 'DE')","example":"DK"},"gmaps_coordinates":{"$ref":"#/components/schemas/GmapsCoordinates"},"place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Place Id","description":"Google Place ID for stable CWJ row lookup (avoids brittle business_name matching)"},"markdown_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Markdown Url","description":"URL to crawled website markdown file (from SpiderSite/SpiderMedia)","example":"https://media.spideriq.ai/crawls/abc123.md"},"markdown_compendium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Markdown Compendium","description":"Direct markdown content (alternative to markdown_url)","example":"# Company Name\n\nDescription of the business..."},"business_phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Phone","description":"Business phone number","example":"+4533116996"},"business_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Address","description":"Full business address"},"original_website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Website","description":"Original website URL","example":"https://restauranttight.dk"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain","description":"Website domain","example":"restauranttight.dk"},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street","description":"Street address"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City name"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code","description":"Postal/ZIP code"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State","description":"State/Province"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"Country name"},"emails_verified":{"anyOf":[{"items":{"$ref":"#/components/schemas/VerifiedEmail"},"type":"array"},{"type":"null"}],"title":"Emails Verified","description":"List of verified emails from SpiderVerify"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Facebook","description":"Facebook page URL"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram","description":"Instagram profile URL"},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin","description":"LinkedIn page URL"},"twitter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter","description":"Twitter/X profile URL"},"youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube","description":"YouTube channel URL"},"tiktok":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tiktok","description":"TikTok profile URL"},"booking_appointment_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Appointment Link","description":"Booking appointment URL"},"reservation_links":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reservation Links","description":"Reservation URL"},"menu_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Menu Link","description":"Menu URL"},"order_links":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order Links","description":"Online ordering URL"},"gmaps_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Link","description":"Google Maps URL"},"company_info":{"anyOf":[{"$ref":"#/components/schemas/CompanyInfo-Input"},{"type":"null"}],"description":"AI-extracted company information from SpiderSite"},"logo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo","description":"URL to logo image"},"gmaps_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Image Url","description":"URL to Google Maps photo"},"lead_scoring":{"anyOf":[{"$ref":"#/components/schemas/LeadScoring-Input"},{"type":"null"}],"description":"Lead scoring data for analytics"},"test":{"type":"boolean","title":"Test","description":"If true, routes to test worker queue","default":false}},"type":"object","required":["business_name","country_code","gmaps_coordinates"],"title":"SpiderVayapinJobPayload","description":"Payload schema for SpiderVayapin jobs (v2.53.0)\n\nCreates complete VayaPin profiles from enriched business data.\nRequires business basics, location, and markdown content URL."},"SpiderVayapinJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderVayapinJobPayload"}],"description":"SpiderVayapin job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":5}},"type":"object","required":["payload"],"title":"SpiderVayapinJobSubmit","description":"Submit a SpiderVayapin job (v2.53.0)"},"SpiderVerifyConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable email verification","default":true},"check_gravatar":{"type":"boolean","title":"Check Gravatar","description":"Check Gravatar images","default":false},"check_dnsbl":{"type":"boolean","title":"Check Dnsbl","description":"Check spam blacklists","default":false},"smtp_timeout_secs":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Smtp Timeout Secs","default":45},"max_emails_per_company":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Max Emails Per Company","default":10}},"type":"object","title":"SpiderVerifyConfig","description":"SpiderVerify configuration for email verification."},"SpiderVerifyData":{"properties":{"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total","description":"Total emails verified"},"summary":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Summary","description":"Summary counts by status: {valid: 5, invalid: 2, risky: 1, unknown: 0, fuzziq_skipped: 0}"},"billable_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Billable Count","description":"Number of billable verifications"},"results":{"anyOf":[{"items":{"$ref":"#/components/schemas/SpiderVerifyResult"},"type":"array"},{"type":"null"}],"title":"Results","description":"Array of verification results"},"metadata":{"type":"object","title":"Metadata","description":"Verification metadata"}},"type":"object","title":"SpiderVerifyData","description":"SpiderVerify verification results — canonical bulk-of-1 shape (flows-test §1.3 Round 2).\n\nSingle-email and bulk submissions both produce the same response shape:\n`{total, summary, results: [SpiderVerifyResult, ...], billable_count, metadata}`.\nSingle-email mode is bulk-of-1 — `results[0]` holds the single email's data.\n\nThe legacy single-mode top-level fields (email/status/score/flags/...) were removed\nbecause they were always None even for single-email jobs (worker emits bulk shape;\n`build_verify_results_payload` wraps single→bulk). Clients must read `results[0]`.","examples":[{"name":"Single Email (bulk-of-1)","value":{"billable_count":1,"metadata":{"total_time_seconds":4.2},"results":[{"domain":{"mx_found":true,"mx_records":["mx1.example.com","mx2.example.com"],"name":"example.com","smtp_provider":"Google"},"email":"john.smith@example.com","flags":{"has_full_inbox":false,"has_gravatar":true,"is_catch_all":false,"is_deliverable":true,"is_disabled":false,"is_disposable":false,"is_free_email":false,"is_role_account":false,"is_valid_syntax":true},"gravatar_url":"https://www.gravatar.com/avatar/abc123","quality":"good","score":95,"status":"valid","sub_status":""}],"summary":{"invalid":0,"risky":0,"unknown":0,"valid":1},"total":1}},{"name":"Bulk Emails","value":{"billable_count":3,"metadata":{"total_time_seconds":9.5},"results":[{"email":"ceo@startup.io","score":92,"status":"valid"},{"email":"contact@enterprise.com","score":88,"status":"valid"},{"email":"fake@nonexistent.xyz","score":0,"status":"invalid"}],"summary":{"fuzziq_skipped":0,"invalid":1,"risky":0,"unknown":0,"valid":2},"total":3}}]},"SpiderVerifyDomain":{"properties":{"name":{"type":"string","title":"Name","description":"Domain name"},"mx_found":{"type":"boolean","title":"Mx Found","description":"Domain has MX records"},"mx_records":{"items":{"type":"string"},"type":"array","title":"Mx Records","description":"MX records for the domain"},"smtp_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Smtp Provider","description":"Detected SMTP provider (Google, Microsoft, etc.)"}},"type":"object","required":["name","mx_found"],"title":"SpiderVerifyDomain","description":"Domain information for verified email"},"SpiderVerifyFlags":{"properties":{"is_valid_syntax":{"type":"boolean","title":"Is Valid Syntax","description":"Email has valid syntax"},"is_deliverable":{"type":"boolean","title":"Is Deliverable","description":"Email is likely deliverable (SMTP verified)"},"is_free_email":{"type":"boolean","title":"Is Free Email","description":"Email is from a free provider (Gmail, Yahoo, etc.)","default":false},"is_disposable":{"type":"boolean","title":"Is Disposable","description":"Email is from a disposable/temporary provider","default":false},"is_role_account":{"type":"boolean","title":"Is Role Account","description":"Email is a role account (info@, support@, etc.)","default":false},"is_catch_all":{"type":"boolean","title":"Is Catch All","description":"Domain accepts all email addresses","default":false},"is_disabled":{"type":"boolean","title":"Is Disabled","description":"Email account is disabled","default":false},"has_full_inbox":{"type":"boolean","title":"Has Full Inbox","description":"Mailbox is full","default":false},"has_gravatar":{"type":"boolean","title":"Has Gravatar","description":"Email has an associated Gravatar","default":false}},"type":"object","required":["is_valid_syntax","is_deliverable"],"title":"SpiderVerifyFlags","description":"Boolean flags for email verification result"},"SpiderVerifyJobPayload":{"properties":{"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Single email address to verify","example":"john@example.com"},"emails":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Emails","description":"Bulk email addresses to verify (max 100 per request)","example":["john@example.com","jane@example.com"]},"from_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Email","description":"MAIL FROM address used for SMTP check. If not specified, worker uses email rotation."},"hello_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hello Name","description":"EHLO/HELO hostname for SMTP handshake. If not specified, worker uses its configured domain."},"check_gravatar":{"type":"boolean","title":"Check Gravatar","description":"Check if email has an associated Gravatar image","default":false},"smtp_timeout_secs":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Smtp Timeout Secs","description":"SMTP connection timeout in seconds","default":45},"check_dnsbl":{"type":"boolean","title":"Check Dnsbl","description":"Check email domain against DNSBL spam trap lists","default":false},"test":{"type":"boolean","title":"Test","description":"Route job to test queue (local workers only)","default":false},"fuzziq_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Enabled","description":"Enable FuzzIQ deduplication. Adds verified emails to client's canonical database. Default: uses client setting."},"fuzziq_unique_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fuzziq Unique Only","description":"Return only unique records (filter out duplicates). Default: uses client setting."}},"type":"object","title":"SpiderVerifyJobPayload","description":"Payload schema for SpiderVerify email verification jobs (v2.12.0)\n\nFeatures:\n- Single email or bulk (up to 100) verification\n- SMTP-level deliverability checking via Reacher backend\n- Catch-all, disposable, role account detection\n- Optional DNSBL spam trap risk scoring\n- 3-second rate limiting between emails to avoid IP blocking","examples":[{"check_gravatar":true,"email":"john.smith@example.com","smtp_timeout_secs":45},{"check_dnsbl":true,"emails":["ceo@startup.io","contact@enterprise.com","info@company.org"]}]},"SpiderVerifyJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderVerifyJobPayload"}],"description":"SpiderVerify job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":0}},"type":"object","required":["payload"],"title":"SpiderVerifyJobSubmit","description":"Submit a SpiderVerify email verification job\n\nVerifies email deliverability at SMTP level using Reacher backend.\nRate limited to 20 emails/minute per VPS to avoid IP blocking.","examples":[{"payload":{"check_gravatar":true,"email":"john.smith@example.com"},"priority":5},{"payload":{"check_dnsbl":true,"emails":["ceo@startup.io","contact@enterprise.com"]},"priority":3}]},"SpiderVerifyResult":{"properties":{"email":{"type":"string","title":"Email","description":"Email address that was verified"},"status":{"type":"string","enum":["valid","invalid","risky","unknown"],"title":"Status","description":"Verification status: valid (safe to send), invalid (don't send), risky (proceed with caution), unknown (couldn't determine)"},"sub_status":{"type":"string","title":"Sub Status","description":"Detailed status reason (e.g., 'catch_all', 'disposable', 'role_account', 'timeout')","default":""},"quality":{"type":"string","enum":["good","risky","bad","unknown"],"title":"Quality","description":"Email quality for deliverability"},"score":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Score","description":"Email quality score 0-100 (100 = perfect)","default":0},"flags":{"allOf":[{"$ref":"#/components/schemas/SpiderVerifyFlags"}],"description":"Verification flags"},"domain":{"anyOf":[{"$ref":"#/components/schemas/SpiderVerifyDomain"},{"type":"null"}],"description":"Domain information"},"dnsbl":{"anyOf":[{"$ref":"#/components/schemas/DNSBLResult"},{"type":"null"}],"description":"DNSBL spam trap check result (if enabled)"},"gravatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gravatar Url","description":"Gravatar image URL (if check_gravatar enabled)"},"did_you_mean":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Did You Mean","description":"Suggested correction for typos"}},"type":"object","required":["email","status","quality","flags"],"title":"SpiderVerifyResult","description":"Single email verification result"},"SpiderVideoJobPayload":{"properties":{"projectName":{"type":"string","maxLength":200,"minLength":1,"title":"Projectname","description":"Output filename (without extension)"},"aspectRatio":{"type":"string","pattern":"^(9:16|16:9)$","title":"Aspectratio","description":"9:16 (portrait) or 16:9 (landscape)","default":"9:16"},"scenes":{"items":{"$ref":"#/components/schemas/SpiderVideoScene"},"type":"array","maxItems":50,"minItems":1,"title":"Scenes","description":"Video scenes to stitch (max 50)"},"transitionDurationInFrames":{"type":"integer","maximum":60.0,"minimum":0.0,"title":"Transitiondurationinframes","description":"Fade duration in frames (15 = 0.5s at 30fps)","default":15},"musicUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Musicurl","description":"Background music URL (mp3, wav)"},"musicVolume":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Musicvolume","description":"Music volume 0-1","default":0.3},"preprocess":{"anyOf":[{"$ref":"#/components/schemas/SpiderVideoPreprocess"},{"type":"null"}]},"upload":{"anyOf":[{"$ref":"#/components/schemas/SpiderVideoUpload"},{"type":"null"}]},"test":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Test","description":"Route to test queue","default":false}},"type":"object","required":["projectName","scenes"],"title":"SpiderVideoJobPayload","description":"Payload for SpiderVideo job submission"},"SpiderVideoJobSubmit":{"properties":{"payload":{"allOf":[{"$ref":"#/components/schemas/SpiderVideoJobPayload"}],"description":"SpiderVideo job configuration"},"priority":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Priority","description":"Job priority (0-10, higher = processed first)","default":5}},"type":"object","required":["payload"],"title":"SpiderVideoJobSubmit","description":"Submit a SpiderVideo video stitching job (v2.33.0)\n\nStitches AI-generated video scenes into final videos with:\n- Fade transitions between scenes\n- Portrait (9:16) and landscape (16:9) support\n- Background music with volume control\n- FFmpeg preprocessing for invalid formats\n- SpiderMedia upload integration"},"SpiderVideoPreprocess":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Auto-fix invalid formats with FFmpeg","default":false}},"type":"object","title":"SpiderVideoPreprocess","description":"Preprocessing options for video compatibility"},"SpiderVideoScene":{"properties":{"videoUrl":{"type":"string","title":"Videourl","description":"URL to video file (mp4, webm, mov)"},"durationInSeconds":{"type":"number","maximum":300.0,"exclusiveMinimum":0.0,"title":"Durationinseconds","description":"Duration in seconds (max 5 min per scene)"}},"type":"object","required":["videoUrl","durationInSeconds"],"title":"SpiderVideoScene","description":"Individual scene in video stitching job"},"SpiderVideoUpload":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Upload to SpiderMedia (client bucket)","default":false}},"type":"object","title":"SpiderVideoUpload","description":"Upload options for rendered video"},"StreamOptions":{"properties":{"include_usage":{"type":"boolean","title":"Include Usage","default":false}},"type":"object","title":"StreamOptions","description":"Streaming options."},"SubdomainCreate":{"properties":{"label":{"anyOf":[{"type":"string","maxLength":63,"minLength":1},{"type":"null"}],"title":"Label","description":"DNS label, e.g. 'acme' → acme.sites.spideriq.ai. Omit to use the client's default platform subdomain."},"homepage_slug":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Homepage Slug"}},"type":"object","title":"SubdomainCreate","description":"Create a free `{label}.sites.spideriq.ai` subdomain.\n\n`label` is optional — when omitted the backend mints the client's\ndeterministic platform subdomain (the `cli_`-stripped slug). When\nsupplied it must be a single valid DNS label.","example":{"label":"acme"}},"SubscribeRequest":{"properties":{"tariff_version_id":{"type":"integer","minimum":1.0,"title":"Tariff Version Id"},"billing_interval":{"type":"string","enum":["month","year"],"title":"Billing Interval","default":"month"},"success_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Success Url"},"cancel_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Cancel Url"}},"additionalProperties":false,"type":"object","required":["tariff_version_id","success_url","cancel_url"],"title":"SubscribeRequest"},"SubscribeResponse":{"properties":{"checkout_url":{"type":"string","minLength":1,"title":"Checkout Url"},"session_id":{"type":"string","minLength":1,"title":"Session Id"}},"type":"object","required":["checkout_url","session_id"],"title":"SubscribeResponse"},"SubscriptionStateResponse":{"properties":{"status":{"type":"string","enum":["active","trialing","past_due","cancelled","paused"],"title":"Status"},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End"},"cancel_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancel At"}},"type":"object","required":["status","cancel_at_period_end"],"title":"SubscriptionStateResponse","description":"The caller's subscription lifecycle state after a /cancel or /resume.\n\n``cancel_at`` is the ISO ``current_period_end`` the access runs through\nwhen ``cancel_at_period_end`` is true; ``None`` once resumed. The customer\nstays ``active`` until the period rolls — cancellation is scheduled, not\nimmediate."},"SubscriptionStatus":{"type":"string","enum":["inactive","active","trialing","past_due","canceled"],"title":"SubscriptionStatus"},"SuggestEnvelope":{"properties":{"asset_type":{"type":"string","enum":["bg_video","component","site_template"],"title":"Asset Type"},"slug":{"type":"string","maxLength":128,"minLength":1,"title":"Slug"},"proposed_universal_axes":{"type":"object","title":"Proposed Universal Axes","description":"mood/palette/brand_fit_tags/scene_type — validated against Pydantic enums"},"proposed_agent_meta":{"type":"object","title":"Proposed Agent Meta","description":"Per-asset-type agent_meta — validated against the matching Pydantic model"},"confidence_per_key":{"items":{"$ref":"#/components/schemas/SuggestKeyConfidence"},"type":"array","title":"Confidence Per Key","description":"One record per proposed key (universal + agent_meta) for UI rendering"},"dropped_keys":{"items":{"type":"string"},"type":"array","title":"Dropped Keys","description":"Keys the LLM proposed but the validator rejected (off-vocab)"},"reasoning":{"type":"string","maxLength":2000,"title":"Reasoning","default":""},"usage":{"$ref":"#/components/schemas/SuggestUsage"}},"type":"object","required":["asset_type","slug","usage"],"title":"SuggestEnvelope","description":"Suggester response — what `marketplace_inference_service.suggest()`\nreturns, what `POST /dashboard/content/marketplace/{type}/{slug}/suggest-agent-meta`\nreturns, and what slices 4 + 6 hand off to the apply path.\n\nCritical contract: ``proposed_universal_axes`` and ``proposed_agent_meta``\ncontain ONLY values that passed the Pydantic enum validation against\n``Mood`` / ``SceneType`` / ``BrandFit`` / ``BgVideoAgentMeta`` /\n``ComponentAgentMeta`` / ``SiteTemplateAgentMeta``. Off-vocab values\nare dropped + listed in ``dropped_keys`` so callers can audit\nsuggestions that didn't make the cut."},"SuggestKeyConfidence":{"properties":{"key":{"type":"string","maxLength":64,"minLength":1,"title":"Key"},"value":{"title":"Value"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence"},"action":{"type":"string","enum":["auto_apply","review","drop"],"title":"Action","description":"auto_apply (>=0.75 + vocab-match), review (>=0.55), or drop"}},"type":"object","required":["key","value","confidence","action"],"title":"SuggestKeyConfidence","description":"Per-axis confidence record. Drives the dashboard's confidence\nbadges and the slice 4 bulk-apply gating logic."},"SuggestUsage":{"properties":{"model":{"type":"string","maxLength":128,"minLength":1,"title":"Model"},"input_tokens":{"type":"integer","minimum":0.0,"title":"Input Tokens"},"output_tokens":{"type":"integer","minimum":0.0,"title":"Output Tokens"},"cost_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Cost Usd","description":"Best-effort cost from litellm's completion_cost helper. None when the model doesn't have a price entry."}},"type":"object","required":["model","input_tokens","output_tokens"],"title":"SuggestUsage","description":"Token + cost accounting for the call. Used by the slice 4 bulk\nrunner to enforce the $50 cumulative cap."},"SyncResult":{"properties":{"connection_id":{"type":"integer","title":"Connection Id"},"senders_found":{"type":"integer","title":"Senders Found"},"senders_inserted":{"type":"integer","title":"Senders Inserted"},"senders_updated":{"type":"integer","title":"Senders Updated"},"mailbox_matches":{"type":"integer","title":"Mailbox Matches"},"errors":{"items":{"type":"string"},"type":"array","title":"Errors","default":[]}},"type":"object","required":["connection_id","senders_found","senders_inserted","senders_updated","mailbox_matches"],"title":"SyncResult"},"TagCreate":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"slug":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Slug"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["name"],"title":"TagCreate","description":"Create a new tag."},"TagListResponse":{"properties":{"tags":{"items":{"$ref":"#/components/schemas/TagResponse"},"type":"array","title":"Tags"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["tags","total"],"title":"TagListResponse","description":"List of tags."},"TagResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"post_count":{"type":"integer","title":"Post Count","default":0},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","client_id","name","slug","created_at"],"title":"TagResponse","description":"Tag response."},"TagUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"slug":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Slug"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"TagUpdate","description":"Update tag fields."},"TaskAssigneeRef":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"type":{"type":"string","title":"Type","default":"user"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"}},"type":"object","required":["id","name"],"title":"TaskAssigneeRef"},"TaskBoardRef":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"board_type":{"type":"string","title":"Board Type"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"}},"type":"object","required":["id","name","board_type"],"title":"TaskBoardRef"},"TaskColumnRef":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"}},"type":"object","required":["id","name"],"title":"TaskColumnRef"},"TaskCreate":{"properties":{"column_id":{"type":"string","format":"uuid","title":"Column Id"},"title":{"type":"string","maxLength":500,"minLength":1,"title":"Title"},"description":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Description"},"priority":{"anyOf":[{"type":"string","pattern":"^(critical|high|medium|low)$"},{"type":"null"}],"title":"Priority"},"labels":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Labels"},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"},"source":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Source"},"external_ref":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"External Ref"}},"type":"object","required":["column_id","title"],"title":"TaskCreate"},"TaskGroup":{"properties":{"key":{"type":"string","title":"Key"},"label":{"type":"string","title":"Label"},"task_count":{"type":"integer","title":"Task Count","default":0},"board_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Board Color"},"tasks":{"items":{"$ref":"#/components/schemas/MasterTaskItem"},"type":"array","title":"Tasks"}},"type":"object","required":["key","label"],"title":"TaskGroup"},"TaskMove":{"properties":{"column_id":{"type":"string","format":"uuid","title":"Column Id"},"position":{"type":"integer","minimum":0.0,"title":"Position"}},"type":"object","required":["column_id","position"],"title":"TaskMove"},"TaskResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"type":"string","format":"uuid","title":"Column Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"priority":{"type":"string","title":"Priority"},"position":{"type":"integer","title":"Position"},"labels":{"items":{"type":"string"},"type":"array","title":"Labels"},"custom_fields":{"type":"object","title":"Custom Fields"},"due_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due Date"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"external_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Ref"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"profile":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Profile"}},"type":"object","required":["id","client_id","board_id","column_id","title","status","priority","position","created_at","updated_at"],"title":"TaskResponse"},"TaskUpdate":{"properties":{"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Description"},"status":{"anyOf":[{"type":"string","pattern":"^(pending|assigned|in_progress|blocked|review|done|cancelled|failed)$"},{"type":"null"}],"title":"Status"},"priority":{"anyOf":[{"type":"string","pattern":"^(critical|high|medium|low)$"},{"type":"null"}],"title":"Priority"},"position":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Position"},"due_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due Date"},"labels":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Labels"},"custom_fields":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Fields"}},"type":"object","title":"TaskUpdate"},"TemplateConfigResponse":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"theme":{"type":"string","title":"Theme","default":"default"},"routes":{"additionalProperties":{"type":"string"},"type":"object","title":"Routes","default":{}},"settings":{"type":"object","title":"Settings","default":{}},"data_sources":{"items":{"type":"object"},"type":"array","title":"Data Sources","default":[]},"salespersons":{"type":"object","title":"Salespersons","default":{}},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","title":"TemplateConfigResponse"},"TemplateConfigUpdate":{"properties":{"theme":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Theme"},"routes":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Routes"},"settings":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Settings"},"data_sources":{"anyOf":[{"items":{"$ref":"#/components/schemas/DataSourceConfig"},"type":"array"},{"type":"null"}],"title":"Data Sources"},"salespersons":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Salespersons"}},"type":"object","title":"TemplateConfigUpdate"},"TemplateListResponse":{"properties":{"templates":{"items":{"$ref":"#/components/schemas/TemplateResponse"},"type":"array","title":"Templates"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["templates","total"],"title":"TemplateListResponse"},"TemplatePreviewRequest":{"properties":{"template_path":{"type":"string","minLength":1,"title":"Template Path"},"data":{"type":"object","title":"Data"}},"type":"object","required":["template_path"],"title":"TemplatePreviewRequest"},"TemplatePreviewResponse":{"properties":{"html":{"type":"string","title":"Html"},"template_path":{"type":"string","title":"Template Path"}},"type":"object","required":["html","template_path"],"title":"TemplatePreviewResponse"},"TemplatePreviewUploadResponse":{"properties":{"key":{"type":"string","title":"Key"},"url":{"type":"string","title":"Url"},"size_bytes":{"type":"integer","title":"Size Bytes"},"content_type":{"type":"string","title":"Content Type"}},"type":"object","required":["key","url","size_bytes","content_type"],"title":"TemplatePreviewUploadResponse","description":"Returned by POST /dashboard/site-templates/{slug}/upload-preview."},"TemplateResponse":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"path":{"type":"string","title":"Path"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"theme":{"type":"string","title":"Theme","default":"default"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"}},"type":"object","required":["path"],"title":"TemplateResponse"},"TemplateUpsert":{"properties":{"content":{"type":"string","minLength":1,"title":"Content","description":"Liquid template source code"},"theme":{"type":"string","maxLength":100,"title":"Theme","default":"default"}},"type":"object","required":["content"],"title":"TemplateUpsert"},"ThemeDetailResponse":{"properties":{"name":{"type":"string","title":"Name"},"display_name":{"type":"string","title":"Display Name"},"description":{"type":"string","title":"Description","default":""},"version":{"type":"string","title":"Version","default":"1.0.0"},"files":{"additionalProperties":{"type":"string"},"type":"object","title":"Files"}},"type":"object","required":["name","display_name","files"],"title":"ThemeDetailResponse"},"ThemeInfo":{"properties":{"name":{"type":"string","title":"Name"},"display_name":{"type":"string","title":"Display Name"},"description":{"type":"string","title":"Description","default":""},"version":{"type":"string","title":"Version","default":"1.0.0"},"file_count":{"type":"integer","title":"File Count","default":0}},"type":"object","required":["name","display_name"],"title":"ThemeInfo"},"ThemeListResponse":{"properties":{"themes":{"items":{"$ref":"#/components/schemas/ThemeInfo"},"type":"array","title":"Themes"}},"type":"object","required":["themes"],"title":"ThemeListResponse"},"TimeSeriesBucket":{"properties":{"date":{"type":"string","title":"Date"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["date","count"],"title":"TimeSeriesBucket","description":"Time series data point."},"TokenPlanListItem":{"properties":{"token_id":{"type":"string","format":"uuid","title":"Token Id"},"token_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Label","description":"agent_tokens.name — human label for the PAT."},"token_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Prefix","description":"agent_tokens.token_prefix — disambiguates tokens in the UI."},"is_active":{"type":"boolean","title":"Is Active","default":true},"service_type":{"type":"string","maxLength":64,"minLength":1,"title":"Service Type"},"csp_caps":{"type":"object","title":"Csp Caps","description":"The client-plan caps for this service_type (the ceiling). Keys: enabled + the six TSP_CLAMPED_CAP_FIELDS. NULL value = unlimited."},"tsp_overrides":{"anyOf":[{"$ref":"#/components/schemas/TokenServicePlanRow"},{"type":"null"}],"description":"The per-token override row, or None when this token has no override for this service_type yet."}},"additionalProperties":false,"type":"object","required":["token_id","service_type"],"title":"TokenPlanListItem","description":"One (token × service_type) cell of the management grid. Carries the\ntoken's identity, the CSP caps it is carving under (the ceiling), and the\nTSP override row if one exists yet (None = token inherits CSP wholesale)."},"TokenPlanListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/TokenPlanListItem"},"type":"array","title":"Items"},"total":{"type":"integer","minimum":0.0,"title":"Total"}},"additionalProperties":false,"type":"object","required":["items","total"],"title":"TokenPlanListResponse"},"TokenRevokeResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"revoked_count":{"type":"integer","title":"Revoked Count"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","revoked_count","message"],"title":"TokenRevokeResponse","description":"Response after revoking token(s)."},"TokenServicePlanClientUpsert":{"properties":{"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"jobs_per_hour":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Jobs Per Hour"},"jobs_per_24h":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Jobs Per 24H"},"monthly_cap_calendar":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Monthly Cap Calendar"},"monthly_cap_period":{"anyOf":[{"type":"string","pattern":"^(calendar|rolling_30d)$"},{"type":"null"}],"title":"Monthly Cap Period"},"cost_ceiling_usd_per_24h":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Cost Ceiling Usd Per 24H"},"monthly_spend_cap_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Monthly Spend Cap Usd"},"max_inflight_jobs":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Inflight Jobs"},"time_window_start":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Time Window Start"},"time_window_end":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Time Window End"},"time_window_tz":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Time Window Tz"},"priority_tier":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Priority Tier"}},"additionalProperties":false,"type":"object","title":"TokenServicePlanClientUpsert","description":"Client self-serve PUT body. Same as the admin schema MINUS the three\nadmin-only columns (custom_rules, cost_per_request_usd,\ncost_per_request_currency) — `extra=\"forbid\"` means a client that sends any\nof them gets a 422 before the clamp even runs. The six budget-cap fields are\nadditionally clamped server-side to the client's CSP caps. priority_tier and\ntime_window_* are intra-client (a token only reorders / narrows its OWNER's\nown work), so they are writable without a clamp."},"TokenServicePlanRow":{"properties":{"token_id":{"type":"string","format":"uuid","title":"Token Id"},"service_type":{"type":"string","maxLength":64,"minLength":1,"title":"Service Type"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"jobs_per_hour":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Jobs Per Hour"},"jobs_per_24h":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Jobs Per 24H"},"monthly_cap_calendar":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Monthly Cap Calendar"},"monthly_cap_period":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Monthly Cap Period"},"cost_ceiling_usd_per_24h":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Cost Ceiling Usd Per 24H"},"monthly_spend_cap_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Monthly Spend Cap Usd"},"max_inflight_jobs":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Inflight Jobs"},"time_window_start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Window Start"},"time_window_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Window End"},"time_window_tz":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Window Tz"},"priority_tier":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Priority Tier"},"custom_rules":{"type":"object","title":"Custom Rules"},"cost_per_request_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Cost Per Request Usd"},"cost_per_request_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cost Per Request Currency"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["token_id","service_type","created_at","updated_at"],"title":"TokenServicePlanRow","description":"A `token_service_plans` row as returned by the read endpoints. The PK\npair + the 15 overridable columns + timestamps. Any override column being\nNone means \"inherit the client plan's value for this axis\"."},"TokenUsage":{"properties":{"prompt_tokens":{"type":"integer","title":"Prompt Tokens","description":"Tokens in the prompt"},"completion_tokens":{"type":"integer","title":"Completion Tokens","description":"Tokens in the completion"},"total_tokens":{"type":"integer","title":"Total Tokens","description":"Total tokens used"},"prompt_tokens_details":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Prompt Tokens Details"},"completion_tokens_details":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Completion Tokens Details"}},"type":"object","required":["prompt_tokens","completion_tokens","total_tokens"],"title":"TokenUsage","description":"Token usage statistics."},"ToolCall":{"properties":{"id":{"type":"string","title":"Id","description":"Unique identifier for this tool call"},"type":{"const":"function","title":"Type","default":"function"},"function":{"$ref":"#/components/schemas/FunctionCall"}},"type":"object","required":["id","function"],"title":"ToolCall","description":"Tool call made by the assistant."},"ToolChoice":{"properties":{"type":{"const":"function","title":"Type","default":"function"},"function":{"additionalProperties":{"type":"string"},"type":"object","title":"Function","description":"{'name': 'function_name'}"}},"type":"object","required":["function"],"title":"ToolChoice","description":"Specific tool choice."},"ToolDefinition":{"properties":{"type":{"const":"function","title":"Type","default":"function"},"function":{"$ref":"#/components/schemas/FunctionDefinition"}},"type":"object","required":["function"],"title":"ToolDefinition","description":"Tool definition for tool calling."},"TopLogprob":{"properties":{"token":{"type":"string","title":"Token"},"logprob":{"type":"number","title":"Logprob"},"bytes":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Bytes"}},"type":"object","required":["token","logprob"],"title":"TopLogprob","description":"Top log probability entry."},"TriggerResetResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message"],"title":"TriggerResetResponse","description":"Response for password reset trigger."},"UnbanRequest":{"properties":{"role":{"type":"string","maxLength":64,"minLength":1,"title":"Role","default":"member"}},"type":"object","title":"UnbanRequest"},"UnifiedJobResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Whether the job completed successfully"},"job_id":{"type":"string","title":"Job Id","description":"Unique job identifier (UUID)"},"type":{"type":"string","title":"Type","description":"Job type: spiderSite, spiderMaps, spiderVerify, spiderPeople, or spiderPhone"},"status":{"type":"string","title":"Status","description":"Job status: completed, failed, processing, queued, cancelled"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Additional context about job state (e.g., 'Job is being processed')"},"processing_time_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Processing Time Seconds","description":"Time taken to process the job"},"worker_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Worker Id","description":"Worker that processed the job"},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At","description":"Completion timestamp (ISO 8601)"},"data":{"anyOf":[{"$ref":"#/components/schemas/SpiderSiteData"},{"$ref":"#/components/schemas/SpiderMapsData"},{"$ref":"#/components/schemas/SpiderVerifyData"},{"$ref":"#/components/schemas/SpiderPeopleData"},{"$ref":"#/components/schemas/SpiderPhoneData"},{"type":"object"},{"type":"null"}],"title":"Data","description":"Job results data"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Error message if job failed"}},"type":"object","required":["success","job_id","type","status"],"title":"UnifiedJobResponse","description":"Unified API response for SpiderSite, SpiderMaps, SpiderVerify, SpiderPeople, and SpiderPhone jobs\n\nFLAT STRUCTURE - Industry standard (similar to Firecrawl/Outscraper)\nMax nesting: 2-3 levels vs old 5 levels","examples":[{"name":"SpiderSite Success","value":{"completed_at":"2025-10-27T11:42:20Z","data":{"crawl_status":"success","emails":["contact@example.com"],"facebook":"https://facebook.com/example","linkedin":"https://linkedin.com/company/example","metadata":{"browser_rendering_available":true,"spa_enabled":true},"pages_crawled":10,"phones":["+1-555-123-4567"],"url":"https://example.com"},"job_id":"974ceeda-84fe-4634-bdcd-adc895c6bc75","processing_time_seconds":19.77,"status":"completed","success":true,"type":"spiderSite","worker_id":"spider-site-main-1"}},{"name":"SpiderMaps Success","value":{"completed_at":"2025-10-27T12:00:00Z","data":{"businesses":[],"query":"restaurants in Manhattan","results_count":20},"job_id":"abc123...","processing_time_seconds":5.2,"status":"completed","success":true,"type":"spiderMaps","worker_id":"spider-maps-main-1"}},{"name":"Failed Job","value":{"error_message":"Connection timeout","job_id":"xyz789...","status":"failed","success":false,"type":"spiderSite"}}]},"UnpublishFlowRequest":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run","default":false},"confirm_token":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirm Token"}},"type":"object","title":"UnpublishFlowRequest"},"UnreadCountResponse":{"properties":{"brand_id":{"type":"integer","minimum":1.0,"title":"Brand Id"},"unread":{"type":"integer","minimum":0.0,"title":"Unread"}},"type":"object","required":["brand_id","unread"],"title":"UnreadCountResponse"},"UpgradePreview":{"properties":{"from_tariff_id":{"type":"string","title":"From Tariff Id"},"to_tariff_id":{"type":"string","title":"To Tariff Id"},"immediate_charge_usd":{"type":"number","title":"Immediate Charge Usd"},"proration_credit_usd":{"type":"number","title":"Proration Credit Usd"},"next_period_charge_usd":{"type":"number","title":"Next Period Charge Usd"},"next_period_start":{"type":"string","title":"Next Period Start"}},"type":"object","required":["from_tariff_id","to_tariff_id","immediate_charge_usd","proration_credit_usd","next_period_charge_usd","next_period_start"],"title":"UpgradePreview"},"UpgradePreviewRequest":{"properties":{"from_tariff_version_id":{"type":"integer","minimum":1.0,"title":"From Tariff Version Id"},"to_tariff_version_id":{"type":"integer","minimum":1.0,"title":"To Tariff Version Id"},"billing_interval":{"type":"string","enum":["month","year"],"title":"Billing Interval","default":"month"}},"additionalProperties":false,"type":"object","required":["from_tariff_version_id","to_tariff_version_id"],"title":"UpgradePreviewRequest"},"UploadAvatarResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Public R2 URL for the uploaded avatar"}},"type":"object","required":["url"],"title":"UploadAvatarResponse"},"UploadPresignRequest":{"properties":{"field_id":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[a-z][a-z0-9_]*$","title":"Field Id","description":"Id of the file_upload field on the flow."},"content_type":{"type":"string","maxLength":255,"minLength":3,"title":"Content Type","description":"MIME type of the file the visitor selected."},"file_size_bytes":{"type":"integer","maximum":52428800.0,"minimum":1.0,"title":"File Size Bytes","description":"Size of the selected file in bytes."},"filename":{"type":"string","maxLength":255,"minLength":1,"title":"Filename","description":"Original filename. Sanitized server-side before storage."},"session_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Session Id","description":"Optional form-fill session id. When supplied, re-presigning the same field_id + filename within that session returns the same file_url (idempotency). When omitted, a per-visitor token is derived from IP + user-agent."}},"additionalProperties":false,"type":"object","required":["field_id","content_type","file_size_bytes","filename"],"title":"UploadPresignRequest","description":"``POST /booking/{flow_id}/upload-presign`` payload."},"UploadPresignResponse":{"properties":{"upload_url":{"type":"string","title":"Upload Url","description":"Short-lived signed URL. PUT the file bytes here."},"upload_headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Upload Headers","description":"Headers the PUT request MUST include (signed Content-Type)."},"file_url":{"type":"string","title":"File Url","description":"Permanent public URL — reference this in the form submission body."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"UTC instant after which upload_url stops working."}},"additionalProperties":false,"type":"object","required":["upload_url","upload_headers","file_url","expires_at"],"title":"UploadPresignResponse","description":"Successful presign — the signed URL plus the eventual permanent URL."},"Usage":{"properties":{"prompt_tokens":{"type":"integer","title":"Prompt Tokens"},"completion_tokens":{"type":"integer","title":"Completion Tokens"},"total_tokens":{"type":"integer","title":"Total Tokens"}},"type":"object","required":["prompt_tokens","completion_tokens","total_tokens"],"title":"Usage","description":"Token usage information."},"UsageDay":{"properties":{"date":{"type":"string","title":"Date"},"jobs":{"type":"integer","title":"Jobs"},"leads":{"type":"integer","title":"Leads"},"costUsd":{"type":"number","title":"Costusd"}},"type":"object","required":["date","jobs","leads","costUsd"],"title":"UsageDay"},"UsageResponse":{"properties":{"daily":{"items":{"$ref":"#/components/schemas/UsageDay"},"type":"array","title":"Daily"},"totals":{"$ref":"#/components/schemas/UsageTotals"},"billingCycle":{"$ref":"#/components/schemas/BillingCycle"}},"type":"object","required":["totals","billingCycle"],"title":"UsageResponse"},"UsageTotals":{"properties":{"jobs":{"type":"integer","title":"Jobs"},"leads":{"type":"integer","title":"Leads"},"costUsd":{"type":"number","title":"Costusd"}},"type":"object","required":["jobs","leads","costUsd"],"title":"UsageTotals"},"UserMeResponse":{"properties":{"id":{"type":"integer","title":"Id"},"email":{"type":"string","title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"firstname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Firstname"},"lastname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lastname"},"mobile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mobile"},"photo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photo"},"user_role":{"type":"string","title":"User Role"},"brands":{"items":{"$ref":"#/components/schemas/api__v1__auth_profile__UserBrandMembership"},"type":"array","title":"Brands","default":[]}},"type":"object","required":["id","email","user_role"],"title":"UserMeResponse","description":"Full user profile response matching OPVS structure."},"UserPreferencesUpdate":{"properties":{"event_sounds_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Event Sounds Enabled","description":"Toggle Live Theater audio cues on /dashboard/live."}},"type":"object","title":"UserPreferencesUpdate","description":"Patchable per-user UI preferences. Only fields present are updated."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VayaPinCard":{"properties":{"vayapin":{"type":"string","title":"Vayapin","description":"Full pin id, e.g. 'BB:CHAMPERS'"},"pin_name":{"type":"string","title":"Pin Name","description":"Pin slug, e.g. 'CHAMPERS'"},"country":{"type":"string","title":"Country","description":"Uppercase 2-letter namespace, e.g. 'BB'"},"url":{"type":"string","title":"Url","description":"Public profile URL, e.g. 'https://vayapin.com/BB:CHAMPERS'"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Business name / title"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle","description":"Subtitle tagline"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Longer description"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"Settlement / city"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Best-available image (photo → logo)"},"photo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photo Url","description":"Processed pin photo URL"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url","description":"Processed pin logo URL"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories","description":"Human-readable category names"}},"type":"object","required":["vayapin","pin_name","country","url"],"title":"VayaPinCard","description":"One VayaPin profile, ready to render as a card."},"VayaPinCardsResponse":{"properties":{"cards":{"items":{"$ref":"#/components/schemas/VayaPinCard"},"type":"array","title":"Cards"},"count":{"type":"integer","minimum":0.0,"title":"Count","description":"Number of cards returned"},"mode":{"type":"string","title":"Mode","description":"'pinned' (explicit pin names) or 'query' (search)"},"unresolved":{"items":{"type":"string"},"type":"array","title":"Unresolved"}},"type":"object","required":["count","mode"],"title":"VayaPinCardsResponse","description":"Envelope for GET /content/vayapin/cards."},"VayapinCampaignItem":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Name"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["campaign_id","count"],"title":"VayapinCampaignItem","description":"Campaign (flow) option for the dashboard campaign filter dropdown."},"VayapinCountryItem":{"properties":{"country_code":{"type":"string","title":"Country Code"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["country_code","count"],"title":"VayapinCountryItem","description":"Country distribution item for chart."},"VayapinDashboardStats":{"properties":{"total_pins":{"type":"integer","title":"Total Pins","default":0},"pins_with_seo":{"type":"integer","title":"Pins With Seo","default":0},"pins_base_only":{"type":"integer","title":"Pins Base Only","default":0},"pins_failed":{"type":"integer","title":"Pins Failed","default":0},"pins_skipped":{"type":"integer","title":"Pins Skipped","default":0},"countries":{"type":"integer","title":"Countries","default":0},"country_list":{"items":{"type":"string"},"type":"array","title":"Country List","default":[]},"success_rate":{"type":"number","title":"Success Rate","default":0.0},"total_ai_prompt_tokens":{"type":"integer","title":"Total Ai Prompt Tokens","default":0},"total_ai_completion_tokens":{"type":"integer","title":"Total Ai Completion Tokens","default":0}},"type":"object","title":"VayapinDashboardStats","description":"KPI numbers for the VayaPin dashboard."},"VayapinPinDetail":{"properties":{"cwj_id":{"type":"integer","title":"Cwj Id"},"business_name":{"type":"string","title":"Business Name"},"pin_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pin Name"},"vayapin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vayapin Url"},"country_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Code"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"has_seo":{"type":"boolean","title":"Has Seo","default":false},"skip_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skip Reason"},"exported_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exported At"},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Name"},"original_website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Website"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Id"},"pin_subscription_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pin Subscription Id"},"pin_data_set_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pin Data Set Id"},"seo_content":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Seo Content"},"api_log":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Api Log"}},"type":"object","required":["cwj_id","business_name"],"title":"VayapinPinDetail","description":"Full PIN detail with SEO content and API audit log."},"VayapinPinList":{"properties":{"pins":{"items":{"$ref":"#/components/schemas/VayapinPinListItem"},"type":"array","title":"Pins"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"total_pages":{"type":"integer","title":"Total Pages"}},"type":"object","required":["pins","total","page","page_size","total_pages"],"title":"VayapinPinList","description":"Paginated PIN list response."},"VayapinPinListItem":{"properties":{"cwj_id":{"type":"integer","title":"Cwj Id"},"business_name":{"type":"string","title":"Business Name"},"pin_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pin Name"},"vayapin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vayapin Url"},"country_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Code"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"has_seo":{"type":"boolean","title":"Has Seo","default":false},"skip_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skip Reason"},"exported_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exported At"},"campaign_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Name"},"original_website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Website"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["cwj_id","business_name"],"title":"VayapinPinListItem","description":"Single PIN in the paginated list."},"VayapinTimelineItem":{"properties":{"date":{"type":"string","title":"Date"},"count":{"type":"integer","title":"Count"},"with_seo":{"type":"integer","title":"With Seo"},"base_only":{"type":"integer","title":"Base Only"}},"type":"object","required":["date","count","with_seo","base_only"],"title":"VayapinTimelineItem","description":"Daily export count for timeline chart."},"VerifiedEmail":{"properties":{"email":{"type":"string","title":"Email","description":"Email address"},"status":{"type":"string","title":"Status","description":"Verification status"},"is_deliverable":{"type":"boolean","title":"Is Deliverable","description":"Whether email is deliverable"}},"type":"object","required":["email","status","is_deliverable"],"title":"VerifiedEmail","description":"Verified email from SpiderVerify"},"VerifyConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Verify extracted emails via SMTP","default":true},"max_emails":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Max Emails","description":"Max emails to verify","default":50}},"type":"object","title":"VerifyConfig"},"VideoImportRequest":{"properties":{"url":{"type":"string","maxLength":2048,"minLength":1,"title":"Url","description":"Remote video URL to import (YouTube/TikTok/direct)"},"title":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Title","description":"Optional video title"},"description":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"title":"Description","description":"Optional video description"},"privacy":{"type":"integer","maximum":3.0,"minimum":1.0,"title":"Privacy","description":"PeerTube privacy: 1=public, 2=unlisted (default), 3=private","default":2},"use_proxy":{"type":"boolean","title":"Use Proxy","description":"Force SpiderProxy for the source download (YouTube/TikTok auto-detected regardless)","default":false}},"type":"object","required":["url"],"title":"VideoImportRequest","description":"Body for ``POST /import-video`` — import a remote video into PeerTube."},"ViewColumn":{"properties":{"key":{"type":"string","maxLength":50,"minLength":1,"title":"Key"},"width":{"anyOf":[{"type":"integer","maximum":2000.0,"minimum":20.0},{"type":"null"}],"title":"Width"},"visible":{"type":"boolean","title":"Visible","default":true}},"type":"object","required":["key"],"title":"ViewColumn"},"ViewFilterConfig":{"properties":{"mailboxes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Mailboxes","description":"Email addresses to include. None = all client mailboxes."},"unread_only":{"type":"boolean","title":"Unread Only","default":false},"starred_only":{"type":"boolean","title":"Starred Only","default":false},"has_attachments":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Attachments"},"from_contains":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"From Contains"},"subject_contains":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Subject Contains"},"labels":{"items":{"type":"string"},"type":"array","title":"Labels"},"received_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Received After","description":"ISO timestamp; messages with date >= this are included."},"received_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Received Before","description":"ISO timestamp; messages with date <  this are included."},"outreach_classification":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Outreach Classification"},"direction":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Direction"}},"type":"object","title":"ViewFilterConfig","description":"Saved filter shape. Stored as JSONB in mail_views.filter_config."},"VisitorGeoResponse":{"properties":{"country":{"anyOf":[{"type":"string","maxLength":4},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country code, e.g. 'DE'. Null when CF-IPCountry is missing or 'XX'/'T1' (Tor / unknown)."},"city":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"City"},"region":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Region","description":"Subdivision name (e.g. 'Berlin', 'California'). From CF-Region."},"timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Timezone","description":"IANA timezone, e.g. 'Europe/Berlin'. From CF-Timezone."},"source":{"type":"string","enum":["cloudflare","none"],"title":"Source","description":"`cloudflare` when at least country was resolved; `none` when no enrichment headers were present."}},"type":"object","required":["source"],"title":"VisitorGeoResponse","description":"Response payload for GET /api/v1/content/visitor-geo.\n\nReflects Cloudflare edge headers — `CF-IPCountry`, `CF-IPCity`, `CF-Region`,\n`CF-Timezone`. NO database lookup, NO IP storage. All fields nullable\nbecause not every CF zone enrichment plan exposes city/region (free zone\noften only has country). Components MUST tolerate nulls."},"VisualCheckAssertions":{"properties":{"expected_text_found":{"items":{"type":"string"},"type":"array","title":"Expected Text Found"},"expected_text_missing":{"items":{"type":"string"},"type":"array","title":"Expected Text Missing"},"forbidden_text_found":{"items":{"type":"string"},"type":"array","title":"Forbidden Text Found"}},"type":"object","required":["expected_text_found","expected_text_missing","forbidden_text_found"],"title":"VisualCheckAssertions"},"VisualCheckDom":{"properties":{"iframe_count":{"type":"integer","title":"Iframe Count"},"iframe_srcs":{"items":{"type":"string"},"type":"array","title":"Iframe Srcs"},"shadow_hosts":{"items":{"type":"string"},"type":"array","title":"Shadow Hosts"}},"type":"object","required":["iframe_count","iframe_srcs","shadow_hosts"],"title":"VisualCheckDom"},"VisualCheckFailedRequest":{"properties":{"url":{"type":"string","title":"Url"},"error":{"type":"string","title":"Error"}},"type":"object","required":["url","error"],"title":"VisualCheckFailedRequest"},"VisualCheckRequest":{"properties":{"page_url":{"type":"string","maxLength":2048,"minLength":8,"title":"Page Url","description":"Full URL to check. Must be in the allowlist (spideriq.ai subdomains or a configured tenant primary domain)."},"viewport":{"type":"string","enum":["desktop","mobile"],"title":"Viewport","description":"Viewport preset. desktop=1440x900, mobile=375x667.","default":"desktop"},"wait_for_text":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Wait For Text","description":"Optional. Wait until this string appears in the DOM before snapshotting."},"expected_text":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Expected Text","description":"Strings that MUST appear in body_text_preview. Missing strings are flagged in assertions.expected_text_missing."},"expected_no_text":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Expected No Text","description":"Strings that MUST NOT appear (e.g. \"couldn't load this booking\"). Found strings are flagged in assertions.forbidden_text_found."},"timeout_ms":{"type":"integer","maximum":60000.0,"minimum":1000.0,"title":"Timeout Ms","description":"Total navigation + wait timeout, in milliseconds. Hard cap 60s.","default":30000},"tenant_id":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Tenant Id","description":"Optional tenant identifier. Allows the URL allowlist to expand to that tenant's verified primary domain."}},"additionalProperties":false,"type":"object","required":["page_url"],"title":"VisualCheckRequest","description":"Request body for ``POST /api/v1/mcp/visual-check``."},"VisualCheckResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"screenshot_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Screenshot Url"},"final_url":{"type":"string","title":"Final Url"},"status_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Status Code"},"body_text_preview":{"type":"string","title":"Body Text Preview"},"dom":{"$ref":"#/components/schemas/VisualCheckDom"},"console_errors":{"items":{"type":"string"},"type":"array","title":"Console Errors"},"failed_requests":{"items":{"$ref":"#/components/schemas/VisualCheckFailedRequest"},"type":"array","title":"Failed Requests"},"assertions":{"$ref":"#/components/schemas/VisualCheckAssertions"},"failed_assertions":{"items":{"type":"string"},"type":"array","title":"Failed Assertions"}},"type":"object","required":["success","final_url","body_text_preview","dom","console_errors","failed_requests","assertions","failed_assertions"],"title":"VisualCheckResponse","description":"Response body. Mirrors the sidecar's ``VisualCheckResponse`` exactly."},"WorkerInfo":{"properties":{"queue_name":{"type":"string","title":"Queue Name","description":"RabbitMQ/PostgreSQL queue name"},"total_workers":{"type":"integer","title":"Total Workers","description":"Total workers deployed","default":0},"supports_test_queue":{"type":"boolean","title":"Supports Test Queue","description":"Whether test queue routing is supported","default":true},"vps_distribution":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vps Distribution","description":"VPS distribution strategy"}},"type":"object","required":["queue_name"],"title":"WorkerInfo","description":"Information about the worker fleet for a skill"},"WorkflowActiveRun":{"properties":{"run_id":{"type":"integer","title":"Run Id"},"location_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Location Id"},"location_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location Name"},"current_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Step","description":"spidermaps, spidersite, spiderverify, complete, failed"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"duration_so_far_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration So Far Ms"},"inngest_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inngest Run Id"}},"type":"object","required":["run_id"],"title":"WorkflowActiveRun","description":"Information about a currently active workflow run."},"WorkflowBusinessData":{"properties":{"business_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Name"},"business_website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Website"},"business_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Address"},"latitude":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latitude","description":"Latitude coordinate from Google Maps"},"longitude":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Longitude","description":"Longitude coordinate from Google Maps"},"business_phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Phone"},"business_categories":{"items":{"type":"string"},"type":"array","title":"Business Categories"},"business_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Business Rating"},"business_reviews_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Business Reviews Count"},"logo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo","description":"Logo URL if found"},"emails_found":{"items":{"type":"string"},"type":"array","title":"Emails Found"},"emails_verified":{"items":{"$ref":"#/components/schemas/WorkflowBusinessEmailResult"},"type":"array","title":"Emails Verified"},"phones_found":{"items":{"type":"string"},"type":"array","title":"Phones Found"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Facebook","description":"Facebook page URL"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram","description":"Instagram profile URL"},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin","description":"LinkedIn profile/company URL"},"twitter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter","description":"Twitter/X profile URL"},"youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube","description":"YouTube channel URL"},"tiktok":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tiktok","description":"TikTok profile URL"},"pinterest":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pinterest","description":"Pinterest profile URL"},"snapchat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Snapchat","description":"Snapchat profile URL"},"telegram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram","description":"Telegram contact/channel URL"},"whatsapp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Whatsapp","description":"WhatsApp contact/business URL"},"discord":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Discord","description":"Discord server invite URL"},"reddit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reddit","description":"Reddit profile/subreddit URL"},"medium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Medium","description":"Medium profile URL"},"github":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Github","description":"GitHub organization/user URL"},"google_place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Place Id","description":"Full Google place ID"},"gmaps_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Link","description":"Google Maps link"},"gmaps_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Description","description":"Google Maps business description"},"gmaps_amenities":{"items":{"type":"string"},"type":"array","title":"Gmaps Amenities","description":"Amenities: Delivery, Dine-in, Takeaway, etc."},"gmaps_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Image Url","description":"First image uploaded to SeaweedFS"},"gmaps_images":{"items":{"type":"string"},"type":"array","title":"Gmaps Images","description":"Array of Google Maps photos"},"gmaps_phone_e164":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Phone E164","description":"Validated phone in E.164 format"},"gmaps_phone_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Phone Type","description":"Phone type: MOBILE, FIXED_LINE, etc."},"gmaps_phone_valid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Gmaps Phone Valid","description":"Phone validation status"},"company_info":{"allOf":[{"$ref":"#/components/schemas/CompanyInfo-Output"}],"description":"Company information extracted by AI"},"team_members":{"items":{"type":"object"},"type":"array","title":"Team Members"},"personalization_hooks":{"allOf":[{"$ref":"#/components/schemas/PersonalizationHooks"}],"description":"AI-generated personalization data for outreach"},"lead_scoring":{"allOf":[{"$ref":"#/components/schemas/LeadScoring-Output"}],"description":"CHAMP analysis with icp_fit_grade, engagement_score, lead_priority, etc."},"custom_analysis":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Analysis","description":"Custom AI prompt analysis results (v2.9.0)"},"compendium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compendium","description":"Deprecated: Full compendium not included in campaign results. Use compendium_metadata.download_url to fetch content."},"crawl_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Crawl Status","description":"Crawl status: success, partial, failed"},"pages_crawled":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pages Crawled"},"domain_filtered":{"type":"boolean","title":"Domain Filtered","default":false},"filter_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filter Reason"},"spidersite_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spidersite Status"},"spidersite_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spidersite Error"},"spiderverify_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spiderverify Status"},"spiderverify_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spiderverify Error"},"logo_metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Logo Metadata","description":"Full logo metadata: url, original_url, source, confidence, type"},"compendium_metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Compendium Metadata","description":"Compendium stats: chars, available, cleanup_level, storage_location, deduplication_stats"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata","description":"Crawl metadata: browser_rendering_available, spa_enabled, crawl_strategy, pages_by_type"},"addresses":{"items":{"type":"string"},"type":"array","title":"Addresses","description":"Physical addresses found on website"},"team_members_found":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Team Members Found","description":"Number of team members found on website"},"total_emails_found":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Emails Found","description":"Total emails found before deduplication"},"total_phones_found":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Phones Found","description":"Total phone numbers found before deduplication"},"valid_emails_count":{"type":"integer","title":"Valid Emails Count","default":0},"valid_phones_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Valid Phones Count","description":"Number of valid phone numbers after verification"},"ai_usage":{"allOf":[{"$ref":"#/components/schemas/AiUsage"}],"description":"AI usage stats: model_used, total_cost, total_tokens"}},"type":"object","title":"WorkflowBusinessData","description":"Data for a single business from the workflow (v2.16.0, reordered v2.27.3).\n\nUsed in the blocking job results endpoint to return complete\ndata for each business found by SpiderMaps.\n\nField order: Core Business → Contact → Social Media → Google Maps → AI/Enrichment → Crawl Metadata"},"WorkflowBusinessEmailResult":{"properties":{"email":{"type":"string","title":"Email"},"status":{"type":"string","enum":["valid","invalid","risky","unknown"],"title":"Status"},"score":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Score","default":0},"is_deliverable":{"type":"boolean","title":"Is Deliverable","default":false},"is_free_email":{"type":"boolean","title":"Is Free Email","default":false},"is_disposable":{"type":"boolean","title":"Is Disposable","default":false},"is_role_account":{"type":"boolean","title":"Is Role Account","default":false},"is_catch_all":{"type":"boolean","title":"Is Catch All","description":"Domain catches all emails","default":false},"has_gravatar":{"type":"boolean","title":"Has Gravatar","description":"Email has Gravatar profile image","default":false},"domain_info":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Domain Info","description":"Domain info: name, mx_found, mx_records, smtp_provider"},"quality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality","description":"Email quality: good, bad, unknown"},"sub_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Status","description":"Detailed status reason"}},"type":"object","required":["email","status"],"title":"WorkflowBusinessEmailResult","description":"Email verification result for a single email."},"WorkflowBusinessResult":{"properties":{"business_name":{"type":"string","title":"Business Name"},"business_place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Place Id"},"business_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Address"},"business_phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Phone"},"business_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Business Rating"},"business_reviews_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Business Reviews Count"},"business_categories":{"items":{"type":"string"},"type":"array","title":"Business Categories"},"gmaps_images":{"items":{"type":"string"},"type":"array","title":"Gmaps Images","description":"Array of Google Maps photos"},"gmaps_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Image Url","description":"First image uploaded to SeaweedFS"},"gmaps_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Description","description":"Google Maps business description"},"gmaps_amenities":{"items":{"type":"string"},"type":"array","title":"Gmaps Amenities","description":"Amenities: Delivery, Dine-in, Takeaway, etc."},"gmaps_coordinates":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Gmaps Coordinates","description":"latitude/longitude coordinates"},"google_place_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Place Id","description":"Full Google place ID"},"gmaps_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Link","description":"Google Maps link"},"gmaps_phone_e164":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Phone E164","description":"Validated phone in E.164 format"},"gmaps_phone_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gmaps Phone Type","description":"Phone type: MOBILE, FIXED_LINE, etc."},"gmaps_phone_valid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Gmaps Phone Valid","description":"Phone validation status"},"original_website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Website"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"domain_filtered":{"type":"boolean","title":"Domain Filtered","description":"True if domain was filtered out","default":false},"filter_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filter Reason"},"spidersite_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spidersite Status"},"spidersite_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spidersite Error"},"pages_crawled":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pages Crawled"},"emails_found":{"items":{"type":"string"},"type":"array","title":"Emails Found"},"phones_found":{"items":{"type":"string"},"type":"array","title":"Phones Found"},"social_media":{"additionalProperties":{"type":"string"},"type":"object","title":"Social Media","description":"All social media URLs found (linkedin, twitter, facebook, instagram, youtube, tiktok, github, pinterest, etc.)"},"company_info":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Company Info"},"team_members":{"items":{"type":"object"},"type":"array","title":"Team Members"},"lead_scoring":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Lead Scoring","description":"CHAMP analysis with icp_fit_grade, engagement_score, lead_priority, champ_breakdown, etc."},"logo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo","description":"Logo URL if found"},"compendium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compendium","description":"Deprecated: Full compendium not included in campaign results. Use compendium_metadata.download_url to fetch content."},"linkedin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin","description":"LinkedIn profile/company URL"},"twitter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Twitter","description":"Twitter/X profile URL"},"facebook":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Facebook","description":"Facebook page URL"},"instagram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instagram","description":"Instagram profile URL"},"youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube","description":"YouTube channel URL"},"tiktok":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tiktok","description":"TikTok profile URL"},"github":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Github","description":"GitHub organization/user URL"},"pinterest":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pinterest","description":"Pinterest profile URL"},"snapchat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Snapchat","description":"Snapchat profile URL"},"reddit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reddit","description":"Reddit profile/subreddit URL"},"medium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Medium","description":"Medium profile URL"},"discord":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Discord","description":"Discord server invite URL"},"whatsapp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Whatsapp","description":"WhatsApp contact/business URL"},"telegram":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram","description":"Telegram contact/channel URL"},"logo_metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Logo Metadata","description":"Full logo metadata: url, original_url, source, confidence, type"},"compendium_metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Compendium Metadata","description":"Compendium stats: chars, available, cleanup_level, storage_location, deduplication_stats"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata","description":"Crawl metadata: browser_rendering_available, spa_enabled, crawl_strategy, pages_by_type"},"personalization_hooks":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Personalization Hooks","description":"AI-generated personalization data for outreach"},"custom_analysis":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Custom Analysis","description":"Custom AI prompt analysis results (v2.9.0)"},"addresses":{"items":{"type":"string"},"type":"array","title":"Addresses","description":"Physical addresses found on website"},"crawl_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Crawl Status","description":"Crawl status: success, partial, failed"},"spiderverify_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spiderverify Status"},"spiderverify_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spiderverify Error"},"emails_verified":{"items":{"$ref":"#/components/schemas/WorkflowBusinessEmailResult"},"type":"array","title":"Emails Verified"},"valid_emails_count":{"type":"integer","title":"Valid Emails Count","default":0},"workflow_stage":{"type":"string","title":"Workflow Stage","default":"maps"}},"type":"object","required":["business_name"],"title":"WorkflowBusinessResult","description":"Complete result for a single business through the entire workflow.\n\nContains SpiderMaps data + SpiderSite data + SpiderVerify data."},"WorkflowCompendiumConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Generate markdown compendium","default":true},"cleanup_level":{"type":"string","enum":["raw","fit","citations","minimal"],"title":"Cleanup Level","description":"Cleanup level: raw, fit, citations, minimal","default":"fit"},"max_chars":{"type":"integer","maximum":1000000.0,"minimum":1000.0,"title":"Max Chars","default":100000},"include_in_response":{"type":"boolean","title":"Include In Response","default":true},"remove_duplicates":{"type":"boolean","title":"Remove Duplicates","default":true}},"type":"object","title":"WorkflowCompendiumConfig","description":"Compendium configuration for workflow SpiderSite jobs."},"WorkflowConfig":{"properties":{"spidersite":{"allOf":[{"$ref":"#/components/schemas/WorkflowSpiderSiteConfig"}],"description":"SpiderSite configuration (website scraping)"},"spiderverify":{"allOf":[{"$ref":"#/components/schemas/WorkflowSpiderVerifyConfig"}],"description":"SpiderVerify configuration (email verification)"},"vayapin":{"allOf":[{"$ref":"#/components/schemas/WorkflowVayaPinConfig"}],"description":"VayaPin configuration (directory profile creation)"},"filter_social_media":{"type":"boolean","title":"Filter Social Media","description":"Filter out social media URLs (facebook, instagram, etc.)","default":true},"filter_review_sites":{"type":"boolean","title":"Filter Review Sites","description":"Filter out review sites (yelp, tripadvisor, etc.)","default":true},"filter_directories":{"type":"boolean","title":"Filter Directories","description":"Filter out business directories (yellowpages, etc.)","default":true},"filter_maps":{"type":"boolean","title":"Filter Maps","description":"Filter out map links (google maps, waze, etc.)","default":true}},"type":"object","title":"WorkflowConfig","description":"Complete workflow configuration for campaign orchestration.\n\nEnables automatic job chaining:\nSpiderMaps → (filter domains) → SpiderSite → (extract emails) → SpiderVerify → VayaPin"},"WorkflowHealthMetrics":{"properties":{"stuck_locations_count":{"type":"integer","title":"Stuck Locations Count","description":"Locations stuck in submitted status >2 hours","default":0},"stuck_inngest_runs_count":{"type":"integer","title":"Stuck Inngest Runs Count","description":"Inngest runs stuck in queued/running","default":0},"avg_queue_duration_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Queue Duration Ms","description":"Average time in queue"},"avg_processing_duration_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Processing Duration Ms","description":"Average processing time"},"recovery_events_24h":{"type":"integer","title":"Recovery Events 24H","description":"Number of auto-recovery events in last 24h","default":0}},"type":"object","title":"WorkflowHealthMetrics","description":"Health metrics for campaign workflow monitoring."},"WorkflowJobProgress":{"properties":{"current_stage":{"type":"string","title":"Current Stage"},"stages":{"items":{"$ref":"#/components/schemas/WorkflowStageInfo"},"type":"array","title":"Stages"},"elapsed_seconds":{"type":"number","title":"Elapsed Seconds"},"spidersite_timeout_remaining_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spidersite Timeout Remaining Seconds"},"spiderverify_timeout_remaining_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spiderverify Timeout Remaining Seconds"}},"type":"object","required":["current_stage","elapsed_seconds"],"title":"WorkflowJobProgress","description":"Progress info while waiting for blocking endpoint."},"WorkflowJobResultsResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"True if the job processed successfully (no errors). A job with 0 results is still successful."},"has_results":{"type":"boolean","title":"Has Results","description":"True if businesses_total > 0. Use this for automation filtering.","default":false},"job_id":{"type":"string","title":"Job Id"},"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","enum":["queued","processing","completed","completed_with_errors","failed","cancelled","partial"],"title":"Status","description":"completed=all OK, completed_with_errors=some failures but has data, failed=no useful data"},"progress":{"anyOf":[{"$ref":"#/components/schemas/WorkflowJobProgress"},{"type":"null"}]},"businesses_total":{"type":"integer","title":"Businesses Total","default":0},"businesses_with_domains":{"type":"integer","title":"Businesses With Domains","default":0},"businesses_filtered":{"type":"integer","title":"Businesses Filtered","default":0},"spidersite_completed":{"type":"integer","title":"Spidersite Completed","default":0},"spidersite_failed":{"type":"integer","title":"Spidersite Failed","default":0},"spiderverify_completed":{"type":"integer","title":"Spiderverify Completed","default":0},"spiderverify_failed":{"type":"integer","title":"Spiderverify Failed","default":0},"total_emails_found":{"type":"integer","title":"Total Emails Found","default":0},"total_valid_emails":{"type":"integer","title":"Total Valid Emails","default":0},"businesses":{"items":{"$ref":"#/components/schemas/WorkflowBusinessData"},"type":"array","title":"Businesses"},"processing_time_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Processing Time Seconds","description":"Time spent waiting for this API call (blocking time)"},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds","description":"Total workflow orchestration time from SpiderMaps start to last job completion"},"spidersite_timed_out":{"type":"boolean","title":"Spidersite Timed Out","default":false},"spiderverify_timed_out":{"type":"boolean","title":"Spiderverify Timed Out","default":false},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["success","job_id","campaign_id","status"],"title":"WorkflowJobResultsResponse","description":"Response for blocking workflow job results endpoint (v2.16.0, updated v2.27.4).\n\nReturns ALL businesses from a specific SpiderMaps job with their\ncomplete workflow data (SpiderSite + SpiderVerify results).\n\nEndpoint: GET /api/v1/jobs/spiderMaps/campaigns/{campaign_id}/jobs/{job_id}/results\n\nv2.27.4 Changes:\n- Added `has_results` boolean for easy automation filtering\n- Fixed 0-results jobs returning status=\"processing\" instead of \"completed\"\n\nv2.27.2 Changes:\n- Added `completed_with_errors` status for partial success scenarios\n- Added `duration_seconds` for total workflow orchestration time\n- `success=true` when businesses_total > 0 and processing complete"},"WorkflowLocationResult":{"properties":{"location_id":{"type":"integer","title":"Location Id"},"search_string":{"type":"string","title":"Search String"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"status":{"type":"string","title":"Status"},"businesses_count":{"type":"integer","title":"Businesses Count","default":0},"businesses":{"items":{"$ref":"#/components/schemas/WorkflowBusinessResult"},"type":"array","title":"Businesses"}},"type":"object","required":["location_id","search_string","status"],"title":"WorkflowLocationResult","description":"Results for a single location in the workflow."},"WorkflowProgress":{"properties":{"businesses_total":{"type":"integer","title":"Businesses Total","description":"Total businesses with valid domains","default":0},"sites_queued":{"type":"integer","title":"Sites Queued","description":"SpiderSite jobs queued","default":0},"sites_completed":{"type":"integer","title":"Sites Completed","description":"SpiderSite jobs completed","default":0},"sites_failed":{"type":"integer","title":"Sites Failed","description":"SpiderSite jobs failed","default":0},"verifies_queued":{"type":"integer","title":"Verifies Queued","description":"SpiderVerify jobs queued","default":0},"verifies_completed":{"type":"integer","title":"Verifies Completed","description":"SpiderVerify jobs completed","default":0},"verifies_failed":{"type":"integer","title":"Verifies Failed","description":"SpiderVerify jobs failed","default":0},"emails_found":{"type":"integer","title":"Emails Found","description":"Total emails extracted","default":0},"emails_verified":{"type":"integer","title":"Emails Verified","description":"Total emails verified","default":0},"spidermaps_businesses":{"type":"integer","title":"Spidermaps Businesses","description":"Total businesses found by SpiderMaps","default":0},"spidermaps_with_domain":{"type":"integer","title":"Spidermaps With Domain","description":"Businesses with website/domain","default":0},"spidermaps_with_phone":{"type":"integer","title":"Spidermaps With Phone","description":"Businesses with phone number","default":0},"spidermaps_with_email":{"type":"integer","title":"Spidermaps With Email","description":"Businesses with email (from Google)","default":0},"spidermaps_with_social":{"type":"integer","title":"Spidermaps With Social","description":"Businesses with social media links","default":0},"spidermaps_avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spidermaps Avg Rating","description":"Average rating"},"spidermaps_total_reviews":{"type":"integer","title":"Spidermaps Total Reviews","description":"Total review count","default":0},"extraction_sites_with_emails":{"type":"integer","title":"Extraction Sites With Emails","description":"Sites with emails extracted","default":0},"extraction_sites_with_phones":{"type":"integer","title":"Extraction Sites With Phones","description":"Sites with phones extracted","default":0},"extraction_sites_with_team":{"type":"integer","title":"Extraction Sites With Team","description":"Sites with team members extracted","default":0},"extraction_total_emails":{"type":"integer","title":"Extraction Total Emails","description":"Total emails extracted from sites","default":0},"extraction_total_phones":{"type":"integer","title":"Extraction Total Phones","description":"Total phones extracted from sites","default":0},"extraction_total_team_members":{"type":"integer","title":"Extraction Total Team Members","description":"Total team members extracted","default":0},"vayapins_exported":{"type":"integer","title":"Vayapins Exported","description":"VayaPin profiles created successfully","default":0},"vayapins_skipped":{"type":"integer","title":"Vayapins Skipped","description":"Businesses skipped (no email or validation failed)","default":0},"vayapins_with_seo":{"type":"integer","title":"Vayapins With Seo","description":"Profiles with SEO enrichment (had markdown content)","default":0}},"type":"object","title":"WorkflowProgress","description":"Workflow orchestration progress statistics (v2.15.0, enhanced v2.54.4)."},"WorkflowProgressResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"campaign_status":{"type":"string","title":"Campaign Status"},"active_runs":{"type":"integer","title":"Active Runs","description":"Currently running workflows","default":0},"completed_runs":{"type":"integer","title":"Completed Runs","description":"Successfully completed workflows","default":0},"failed_runs":{"type":"integer","title":"Failed Runs","description":"Failed workflows","default":0},"queued_runs":{"type":"integer","title":"Queued Runs","description":"Runs waiting to start","default":0},"current_runs":{"items":{"$ref":"#/components/schemas/WorkflowActiveRun"},"type":"array","title":"Current Runs","description":"Details of active runs"},"locations_total":{"type":"integer","title":"Locations Total","default":0},"locations_completed":{"type":"integer","title":"Locations Completed","default":0},"locations_pending":{"type":"integer","title":"Locations Pending","default":0},"locations_submitted":{"type":"integer","title":"Locations Submitted","default":0},"avg_workflow_duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Avg Workflow Duration Ms"},"fastest_workflow_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fastest Workflow Ms"},"slowest_workflow_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Slowest Workflow Ms"},"health":{"$ref":"#/components/schemas/WorkflowHealthMetrics"}},"type":"object","required":["campaign_id","campaign_status"],"title":"WorkflowProgressResponse","description":"Response for workflow progress monitoring endpoint (v2.60.0).\n\nProvides real-time visibility into campaign workflow execution:\n- Active runs with current step\n- Completion statistics\n- Health metrics for stuck detection"},"WorkflowResultResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","title":"Status"},"result":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["job_id","status"],"title":"WorkflowResultResponse","description":"Response for workflow job result."},"WorkflowResultsResponse":{"properties":{"campaign_id":{"type":"string","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"query":{"type":"string","title":"Query"},"country_code":{"type":"string","title":"Country Code"},"progress":{"$ref":"#/components/schemas/CampaignProgress"},"workflow_progress":{"$ref":"#/components/schemas/WorkflowProgress"},"total_locations":{"type":"integer","title":"Total Locations","default":0},"total_businesses":{"type":"integer","title":"Total Businesses","default":0},"total_with_domains":{"type":"integer","title":"Total With Domains","default":0},"total_domains_filtered":{"type":"integer","title":"Total Domains Filtered","default":0},"total_emails_found":{"type":"integer","title":"Total Emails Found","default":0},"total_emails_verified":{"type":"integer","title":"Total Emails Verified","default":0},"total_valid_emails":{"type":"integer","title":"Total Valid Emails","default":0},"locations":{"items":{"$ref":"#/components/schemas/WorkflowLocationResult"},"type":"array","title":"Locations"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["campaign_id","status","query","country_code","progress","workflow_progress","created_at","updated_at"],"title":"WorkflowResultsResponse","description":"Aggregated workflow results for a campaign.\n\nContains all data from SpiderMaps → SpiderSite → SpiderVerify chain."},"WorkflowSpiderSiteConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable automatic SpiderSite job submission for businesses with valid domains","default":false},"mode":{"anyOf":[{"type":"string","enum":["contacts","compendium","leads","full"]},{"type":"null"}],"title":"Mode","description":"SpiderSite mode preset: contacts (5p), compendium (5p), leads (25p), full (50p)"},"max_pages":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Max Pages","description":"Maximum pages to crawl per website (mode overrides this)","default":25},"crawl_strategy":{"type":"string","enum":["bestfirst","bfs","dfs"],"title":"Crawl Strategy","description":"Crawling strategy","default":"bestfirst"},"target_pages":{"items":{"type":"string"},"type":"array","title":"Target Pages","description":"Page types to prioritize","default":["contact","about","team","news","blog"]},"enable_spa":{"type":"boolean","title":"Enable Spa","description":"Enable SPA detection and Playwright rendering","default":true},"spa_timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Spa Timeout","default":30},"extract_team":{"type":"boolean","title":"Extract Team","description":"Extract team members using AI","default":false},"extract_company_info":{"type":"boolean","title":"Extract Company Info","description":"Extract company info using AI","default":false},"extract_pain_points":{"type":"boolean","title":"Extract Pain Points","description":"Analyze business challenges using AI","default":false},"product_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Description","description":"Your product description for CHAMP scoring"},"icp_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icp Description","description":"Your ideal customer profile for CHAMP scoring"},"compendium":{"allOf":[{"$ref":"#/components/schemas/WorkflowCompendiumConfig"}],"description":"Markdown compendium configuration"},"timeout":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Timeout","default":30}},"type":"object","title":"WorkflowSpiderSiteConfig","description":"SpiderSite configuration for workflow orchestration.\n\nContains all SpiderSite options that can be configured when\nenabling the SpiderMaps → SpiderSite chain.\n\nv3.1.0: Added mode field for preset configurations:\n- contacts: 5 pages, no AI (quick emails/phones)\n- compendium: 5 pages, markdown output (AI context doc)\n- leads: 25 pages, team+company extraction (recommended)\n- full: 50 pages, all AI features (deep extract)"},"WorkflowSpiderVerifyConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable automatic email verification for extracted emails","default":false},"check_gravatar":{"type":"boolean","title":"Check Gravatar","description":"Check if emails have Gravatar images","default":false},"check_dnsbl":{"type":"boolean","title":"Check Dnsbl","description":"Check email domains against spam blacklists","default":false},"smtp_timeout_secs":{"type":"integer","maximum":120.0,"minimum":10.0,"title":"Smtp Timeout Secs","description":"SMTP verification timeout","default":45},"max_emails_per_business":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Max Emails Per Business","description":"Maximum emails to verify per business (prioritizes contact@, info@, etc.)","default":10}},"type":"object","title":"WorkflowSpiderVerifyConfig","description":"SpiderVerify configuration for workflow orchestration.\n\nContains SpiderVerify options for automatic email verification\nafter SpiderSite extracts emails."},"WorkflowStageInfo":{"properties":{"stage":{"type":"string","enum":["maps","site","verify"],"title":"Stage"},"status":{"type":"string","title":"Status"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["stage","status"],"title":"WorkflowStageInfo","description":"Info for a single workflow stage in the blocking endpoint."},"WorkflowStatusResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","title":"Status","description":"Job status: queued, running, completed, failed"},"flow_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow Path"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"current_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Step"},"steps_completed":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Steps Completed"}},"type":"object","required":["job_id","status"],"title":"WorkflowStatusResponse","description":"Response for workflow job status."},"WorkflowTypeInfo":{"properties":{"id":{"type":"string","title":"Id","description":"Workflow type identifier"},"name":{"type":"string","title":"Name","description":"Human-readable name"},"description":{"type":"string","title":"Description","description":"Workflow description"},"flow_path":{"type":"string","title":"Flow Path","description":"WindMill flow path"},"modules":{"type":"integer","title":"Modules","description":"Number of workflow modules"},"stages":{"items":{"type":"string"},"type":"array","title":"Stages","description":"Pipeline stages"}},"type":"object","required":["id","name","description","flow_path","modules","stages"],"title":"WorkflowTypeInfo","description":"Information about a workflow type."},"WorkflowTypesResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"workflows":{"items":{"$ref":"#/components/schemas/WorkflowTypeInfo"},"type":"array","title":"Workflows"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["workflows","total"],"title":"WorkflowTypesResponse","description":"Response for listing workflow types."},"WorkflowVayaPinConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Enable automatic VayaPin profile creation for businesses with email. Opt-in (default OFF).","default":false}},"type":"object","title":"WorkflowVayaPinConfig","description":"VayaPin configuration for automatic directory profile creation.\n\nWhen enabled, businesses with email are automatically exported to VayaPin\nafter the campaign pipeline completes. Creates real, irreversible profiles\non vayapin.com. If markdown content is available (from SpiderSite), SEO\nenrichment with multilingual content is generated; otherwise a basic PIN\nprofile is created.\n\nflowstest-1.2-r2: default flipped from True to False. Previously a\nstandalone SpiderMaps job submitting only ``workflow.spidersite`` would\nsilently end up with ``workflow.vayapin.enabled=True`` injected (because\nPydantic constructed the default WorkflowVayaPinConfig). Clients had no\nway to opt out of irreversible VayaPin profile creation. Now opt-in only."},"WorkspaceInfo":{"properties":{"project_id":{"type":"string","title":"Project Id"},"client_uuid":{"type":"string","title":"Client Uuid"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"brand_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand Id"},"brand_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Name"},"brand_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Slug"},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"},"is_active":{"type":"boolean","title":"Is Active","default":true}},"type":"object","required":["project_id","client_uuid"],"title":"WorkspaceInfo","description":"A project/workspace the authenticated caller can operate on."},"WorkspacesResponse":{"properties":{"workspaces":{"items":{"$ref":"#/components/schemas/WorkspaceInfo"},"type":"array","title":"Workspaces"},"scoped_by_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scoped By Token"},"total":{"type":"integer","title":"Total"}},"additionalProperties":true,"type":"object","required":["workspaces","total"],"title":"WorkspacesResponse"},"_RenderBusiness":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","maxLength":500,"minLength":1,"title":"Name"},"phone":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Phone"},"timezone":{"type":"string","maxLength":64,"minLength":1,"title":"Timezone"}},"additionalProperties":false,"type":"object","required":["id","name","timezone"],"title":"_RenderBusiness","description":"Minimal business identity the booking component needs.\n\nMirrors `apps/booking-component/src/types.ts → BusinessInfo`. Phone and\ntimezone power the fatal-error fallback (call us) and the slot-time\nformatter."},"_RenderTheme":{"properties":{"primary_color":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Primary Color"},"button_label":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Button Label"},"preset":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Preset"},"tokens":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Tokens"}},"additionalProperties":true,"type":"object","title":"_RenderTheme","description":"Theme payload surfaced on ``GET /booking/{id}/render``.\n\nThe original B.3 shape was flat — top-level ``primary_color`` /\n``button_label`` keys with ``extra=\"allow\"`` letting tenant\n``content_settings`` tokens pass through. SpiderFlow P1.W13.2 adds the\nnested-shape fields ``preset`` (slug) and ``tokens`` (explicit\n``--*`` overrides) so the customer-facing renderer can drive its\nDesign-panel work without remapping. Both new fields are optional and\n``extra=\"allow\"`` stays so a flow that pre-dates W13.2 (flat shape)\nstill surfaces unchanged."},"api__v1__auth_profile__UserBrandMembership":{"properties":{"brand_id":{"type":"integer","title":"Brand Id"},"brand_name":{"type":"string","title":"Brand Name"},"role":{"type":"string","title":"Role"},"subscription_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subscription Status"}},"type":"object","required":["brand_id","brand_name","role"],"title":"UserBrandMembership","description":"Brand membership info returned with user profile."},"api__v1__gate__contribute__OAuthCallbackRequest":{"properties":{"code":{"type":"string","title":"Code","description":"Authorization code from provider redirect"},"state":{"type":"string","title":"State","description":"State token returned from provider redirect"},"contributor_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contributor Name","description":"Optional display name"},"primary_calendar_external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Calendar External Id","description":"For calendar providers only. The calendar id the invitee picked as their primary booking calendar. Defaults to 'primary' for Google / 'default' for Microsoft if not provided."}},"type":"object","required":["code","state"],"title":"OAuthCallbackRequest"},"api__v1__gate__contribute__OAuthCallbackResponse":{"properties":{"status":{"type":"string","title":"Status","description":"'success' on completion"},"integration_id":{"type":"integer","title":"Integration Id","description":"api_integrations row id"},"calcom_user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Calcom User Id","description":"Provisioned cal.com user id (calendar only)"},"calcom_credential_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Calcom Credential Id","description":"cal.com Credential id (calendar only)"},"message":{"type":"string","title":"Message"}},"type":"object","required":["status","integration_id","message"],"title":"OAuthCallbackResponse"},"api__v1__gate__reauth__OAuthCallbackRequest":{"properties":{"code":{"type":"string","minLength":1,"title":"Code"},"state":{"type":"string","minLength":1,"title":"State"}},"type":"object","required":["code","state"],"title":"OAuthCallbackRequest"},"api__v1__gate__reauth__OAuthCallbackResponse":{"properties":{"status":{"type":"string","title":"Status"},"credential_id":{"type":"integer","title":"Credential Id"},"token_status":{"type":"string","title":"Token Status"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"message":{"type":"string","title":"Message"}},"type":"object","required":["status","credential_id","token_status","message"],"title":"OAuthCallbackResponse"},"api__v1__gate__schemas__ChatCompletionChoice":{"properties":{"index":{"type":"integer","title":"Index","description":"Choice index"},"message":{"allOf":[{"$ref":"#/components/schemas/api__v1__gate__schemas__ChatMessage-Output"}],"description":"The generated message"},"finish_reason":{"anyOf":[{"$ref":"#/components/schemas/FinishReason"},{"type":"null"}],"description":"Why generation stopped"},"logprobs":{"anyOf":[{"$ref":"#/components/schemas/ChoiceLogprobs"},{"type":"null"}]}},"type":"object","required":["index","message"],"title":"ChatCompletionChoice","description":"A single completion choice."},"api__v1__gate__schemas__ChatCompletionRequest":{"properties":{"model":{"type":"string","title":"Model","description":"Model ID to use"},"messages":{"items":{"$ref":"#/components/schemas/api__v1__gate__schemas__ChatMessage-Input"},"type":"array","minItems":1,"title":"Messages","description":"Conversation messages"},"temperature":{"anyOf":[{"type":"number","maximum":2.0,"minimum":0.0},{"type":"null"}],"title":"Temperature","description":"Sampling temperature","default":1.0},"top_p":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Top P","description":"Nucleus sampling","default":1.0},"n":{"anyOf":[{"type":"integer","maximum":128.0,"minimum":1.0},{"type":"null"}],"title":"N","description":"Number of completions","default":1},"max_tokens":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Tokens","description":"Maximum tokens to generate"},"max_completion_tokens":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Completion Tokens","description":"Alias for max_tokens (newer API)"},"stream":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Stream","description":"Stream responses","default":false},"stream_options":{"anyOf":[{"$ref":"#/components/schemas/StreamOptions"},{"type":"null"}]},"stop":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Stop","description":"Stop sequences"},"presence_penalty":{"anyOf":[{"type":"number","maximum":2.0,"minimum":-2.0},{"type":"null"}],"title":"Presence Penalty","description":"Presence penalty","default":0.0},"frequency_penalty":{"anyOf":[{"type":"number","maximum":2.0,"minimum":-2.0},{"type":"null"}],"title":"Frequency Penalty","description":"Frequency penalty","default":0.0},"logit_bias":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Logit Bias","description":"Token logit biases"},"logprobs":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Logprobs","description":"Return log probabilities","default":false},"top_logprobs":{"anyOf":[{"type":"integer","maximum":20.0,"minimum":0.0},{"type":"null"}],"title":"Top Logprobs","description":"Number of top logprobs to return"},"tools":{"anyOf":[{"items":{"$ref":"#/components/schemas/ToolDefinition"},"type":"array"},{"type":"null"}],"title":"Tools","description":"Available tools"},"tool_choice":{"anyOf":[{"type":"string","enum":["none","auto","required"]},{"$ref":"#/components/schemas/ToolChoice"},{"type":"null"}],"title":"Tool Choice","description":"Tool choice mode"},"parallel_tool_calls":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Parallel Tool Calls","description":"Allow parallel tool calls","default":true},"response_format":{"anyOf":[{"$ref":"#/components/schemas/ResponseFormatSpec"},{"type":"null"}],"description":"Response format"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed","description":"Random seed for reproducibility"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User","description":"End-user identifier"},"functions":{"anyOf":[{"items":{"$ref":"#/components/schemas/FunctionDefinition"},"type":"array"},{"type":"null"}],"title":"Functions"},"function_call":{"anyOf":[{"type":"string","enum":["none","auto"]},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Function Call"},"spidergate_options":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Spidergate Options","description":"SpiderGate-specific options: fallback_models, timeout, cache_ttl, etc."}},"type":"object","required":["model","messages"],"title":"ChatCompletionRequest","description":"OpenAI-compatible chat completion request.\n\nThis is the primary input format for the /v1/chat/completions endpoint."},"api__v1__gate__schemas__ChatCompletionResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Unique completion ID"},"object":{"const":"chat.completion","title":"Object","default":"chat.completion"},"created":{"type":"integer","title":"Created","description":"Unix timestamp"},"model":{"type":"string","title":"Model","description":"Model used"},"choices":{"items":{"$ref":"#/components/schemas/api__v1__gate__schemas__ChatCompletionChoice"},"type":"array","title":"Choices","description":"Generated completions"},"usage":{"anyOf":[{"$ref":"#/components/schemas/TokenUsage"},{"type":"null"}],"description":"Token usage"},"system_fingerprint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System Fingerprint","description":"System fingerprint"},"spidergate_metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Spidergate Metadata","description":"SpiderGate metadata: provider, latency_ms, cache_hit, cost_usd, etc."}},"type":"object","required":["id","created","model","choices"],"title":"ChatCompletionResponse","description":"OpenAI-compatible chat completion response.\n\nThis is the response format for non-streaming /v1/chat/completions requests."},"api__v1__gate__schemas__ChatMessage-Input":{"properties":{"role":{"$ref":"#/components/schemas/MessageRole"},"content":{"anyOf":[{"type":"string"},{"items":{"$ref":"#/components/schemas/ContentPart"},"type":"array"},{"type":"null"}],"title":"Content"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Name for the participant"},"tool_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/ToolCall"},"type":"array"},{"type":"null"}],"title":"Tool Calls"},"tool_call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tool Call Id","description":"For tool role messages"},"function_call":{"anyOf":[{"$ref":"#/components/schemas/FunctionCall"},{"type":"null"}]}},"type":"object","required":["role"],"title":"ChatMessage","description":"A message in a chat conversation."},"api__v1__gate__schemas__ChatMessage-Output":{"properties":{"role":{"$ref":"#/components/schemas/MessageRole"},"content":{"anyOf":[{"type":"string"},{"items":{"$ref":"#/components/schemas/ContentPart"},"type":"array"},{"type":"null"}],"title":"Content"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Name for the participant"},"tool_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/ToolCall"},"type":"array"},{"type":"null"}],"title":"Tool Calls"},"tool_call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tool Call Id","description":"For tool role messages"},"function_call":{"anyOf":[{"$ref":"#/components/schemas/FunctionCall"},{"type":"null"}]}},"type":"object","required":["role"],"title":"ChatMessage","description":"A message in a chat conversation."},"api__v1__gate__schemas__ErrorResponse":{"properties":{"error":{"$ref":"#/components/schemas/ErrorDetail"}},"type":"object","required":["error"],"title":"ErrorResponse","description":"OpenAI-compatible error response."},"api__v1__llm__ChatCompletionChoice":{"properties":{"index":{"type":"integer","title":"Index"},"message":{"$ref":"#/components/schemas/api__v1__llm__ChatMessage"},"finish_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Finish Reason"}},"type":"object","required":["index","message"],"title":"ChatCompletionChoice","description":"Completion choice."},"api__v1__llm__ChatCompletionRequest":{"properties":{"model":{"type":"string","title":"Model"},"messages":{"items":{"$ref":"#/components/schemas/api__v1__llm__ChatMessage"},"type":"array","title":"Messages"},"temperature":{"anyOf":[{"type":"number","maximum":2.0,"minimum":0.0},{"type":"null"}],"title":"Temperature"},"top_p":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Top P"},"max_tokens":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Max Tokens"},"stream":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Stream","default":false},"stop":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Stop"},"presence_penalty":{"anyOf":[{"type":"number","maximum":2.0,"minimum":-2.0},{"type":"null"}],"title":"Presence Penalty"},"frequency_penalty":{"anyOf":[{"type":"number","maximum":2.0,"minimum":-2.0},{"type":"null"}],"title":"Frequency Penalty"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Associated job ID for tracing"},"job_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Type","description":"Job type (spiderSite, spiderMaps, etc.)"}},"type":"object","required":["model","messages"],"title":"ChatCompletionRequest","description":"OpenAI-compatible chat completion request."},"api__v1__llm__ChatCompletionResponse":{"properties":{"id":{"type":"string","title":"Id"},"object":{"type":"string","title":"Object","default":"chat.completion"},"created":{"type":"integer","title":"Created"},"model":{"type":"string","title":"Model"},"choices":{"items":{"$ref":"#/components/schemas/api__v1__llm__ChatCompletionChoice"},"type":"array","title":"Choices"},"usage":{"anyOf":[{"$ref":"#/components/schemas/Usage"},{"type":"null"}]}},"type":"object","required":["id","created","model","choices"],"title":"ChatCompletionResponse","description":"OpenAI-compatible chat completion response."},"api__v1__llm__ChatMessage":{"properties":{"role":{"type":"string","title":"Role"},"content":{"type":"string","title":"Content"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["role","content"],"title":"ChatMessage","description":"OpenAI-compatible message format."},"schemas__dashboard__UserBrandMembership":{"properties":{"brand_id":{"type":"integer","title":"Brand Id"},"brand_name":{"type":"string","title":"Brand Name"},"role":{"type":"string","title":"Role"}},"type":"object","required":["brand_id","brand_name","role"],"title":"UserBrandMembership","description":"Brand membership info for a user."},"schemas__marketplace__ErrorResponse":{"properties":{"detail":{"type":"string","maxLength":2000,"minLength":1,"title":"Detail"}},"type":"object","required":["detail"],"title":"ErrorResponse","description":"Generic error envelope.\n\nMirrors FastAPI's HTTPException default — ``detail`` is the only public\nfield. Internal exceptions never leak via this shape (handlers wrap them\nwith detail=\"Internal server error\" + log the traceback). Per FastAPI\nrule #5 in CLAUDE.md."}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"},"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-Admin-Key"}}}}