{"openapi":"3.1.0","info":{"title":"Maison API","description":"AI-powered hotel concierge platform API. Exposes REST, A2A (Agent-to-Agent), and MCP (Model Context Protocol) interfaces over the same business logic.","version":"1.0.0","contact":{"name":"Maison Labs","url":"https://maison-labs.com"}},"servers":[{"url":"https://maison-labs.com","description":"Production"}],"tags":[{"name":"REST API","description":"Chat sessions and client config"},{"name":"Client API","description":"Client-specific message translations and field configuration"},{"name":"Auth","description":"Session authentication"},{"name":"API Keys","description":"API key lifecycle management"},{"name":"Clients","description":"Client (tenant) CRUD management"},{"name":"A2A","description":"Agent-to-Agent protocol (JSON-RPC 2.0)"},{"name":"MCP","description":"Model Context Protocol (Streamable HTTP)"},{"name":"Tools","description":"Website and integration diagnostic tools"},{"name":"Users","description":"User account management"},{"name":"Roles","description":"Role-based access control management"},{"name":"Features","description":"Feature flag and permission feature management"},{"name":"User Groups","description":"User group management and membership control"},{"name":"Settings","description":"Client-scoped settings: regions, departments, and tags"},{"name":"Legacy","description":"Temporary backward-compatibility shims for legacy/business-console, legacy/llm-service, and legacy/pipeline-tasks. SCHEDULED FOR REMOVAL — do not build new clients against these endpoints. See docs/legacy-endpoints.md."}],"paths":{"/api/translations":{"get":{"tags":["Client API"],"operationId":"getTranslations","summary":"Get client translations","description":"Fetch UI translations for a client. Keys and locales can be specified via headers or query params. Supports JSONP via `tcb` query param.","parameters":[{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string"},"description":"Client (tenant) UUID"},{"name":"x-translation-keys","in":"header","schema":{"type":"string"},"description":"Comma-separated translation keys, or `*` for all"},{"name":"x-translation-locales","in":"header","schema":{"type":"string"},"description":"Comma-separated locales, or `*` for all"},{"name":"clientid","in":"query","schema":{"type":"string"}},{"name":"keys","in":"query","schema":{"type":"string"}},{"name":"locales","in":"query","schema":{"type":"string"}},{"name":"tcb","in":"query","schema":{"type":"string"},"description":"JSONP callback name"}],"responses":{"200":{"description":"Translations object (JSON or JSONP)","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Missing client ID or translation keys","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/client/fields":{"get":{"tags":["Client API"],"operationId":"getClientFields","summary":"Get client config fields","description":"Fetch specific fields from a client's configuration.","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"},"description":"Client (tenant) UUID"},{"name":"x-client-fields","in":"header","required":true,"schema":{"type":"string"},"description":"Comma-separated field paths (e.g. `hotelConfig.name,clientAssets.icon`)"},{"name":"x-client-locale","in":"header","schema":{"type":"string"},"description":"Locale override"}],"responses":{"200":{"description":"Requested fields","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Missing client ID or fields","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/me":{"get":{"tags":["Auth"],"operationId":"getMe","summary":"Get current user profile and permissions","description":"Returns the authenticated user's userId, email, and flattened permission set. Accepts session cookie or Authorization: Bearer header.","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Authenticated user profile","content":{"application/json":{"schema":{"type":"object","required":["userId","email","permissions"],"properties":{"userId":{"type":"string"},"email":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"},"description":"Flattened permission strings: FEATURE:PERM or FEATURE:PERM:clientId"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to fetch user profile","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/message":{"get":{"tags":["REST API"],"operationId":"getMessageSession","summary":"Get or create chat session","description":"Returns the current session (or creates one) for the given client.","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"},"description":"Client (tenant) UUID"}],"responses":{"200":{"description":"Session with message history","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}}}}}}},"400":{"description":"Invalid client ID or session","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Session does not exist","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["REST API"],"operationId":"postMessage","summary":"Send a chat message","description":"Submit a user query. Normally returns 202 Accepted immediately and delivers the assistant reply via WebSocket (AppSync Events). Requests marked with the `x-test-session: true` header (the prompt-test / golden-FAQ worker, a backend job that is not a WebSocket subscriber) instead receive a synchronous 200 with the assistant reply in the body.","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"},"description":"Client (tenant) UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryBody"}}}},"responses":{"200":{"description":"Synchronous assistant reply (test-session requests only — see the `x-test-session` header).","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"datetime":{"type":"number"},"type":{"type":"string"},"context":{"type":"array","items":{"type":"string"}}}}}}},"202":{"description":"Query accepted for async processing","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ack"]},"sessionId":{"type":"string"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/auth/":{"get":{"tags":["Auth"],"operationId":"checkAuth","summary":"Check session authentication","description":"Returns 200 if the caller has a valid session; 401 otherwise. Returns user email for authenticated sessions.","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Authenticated — returns user info","content":{"application/json":{"schema":{"type":"object","properties":{"authenticated":{"type":"boolean"},"email":{"type":"string","format":"email"},"userId":{"type":"string"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Auth"],"operationId":"login","summary":"Authenticate with credentials","description":"Authenticate with email and password. Auth payload must be base64(email:password) to avoid exposing the password in transit. On success, sets a session cookie scoped to .maison-labs.com.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username","auth"],"properties":{"username":{"type":"string","format":"email","description":"User email address"},"auth":{"type":"string","description":"base64(email:password)"}}}}}},"responses":{"200":{"description":"Authenticated — session cookie set"},"400":{"description":"Missing username or auth","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"User must reset their password before logging in (admin-flagged force_reset_password); no session is issued.","content":{"application/json":{"schema":{"type":"object","required":["forceReset","email"],"properties":{"error":{"type":"string"},"forceReset":{"type":"boolean"},"email":{"type":"string","format":"email"}}}}}}}}},"/auth/logout":{"post":{"tags":["Auth"],"operationId":"logout","summary":"Log out and revoke session","description":"Clears the session cookie and revokes the Firebase refresh token. Always returns 200, even if token revocation fails.","responses":{"200":{"description":"Logged out — session cookie cleared"}}}},"/auth/resetpasswordcode":{"post":{"tags":["Auth"],"operationId":"requestResetPasswordCode","summary":"Request a TOTP password reset code via email","description":"Generates a stateless TOTP reset code and sends it to the user's registered email address. Always returns 200 regardless of whether the account exists (no account enumeration). Resend is throttled to one code per 30 seconds per account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nonce"],"properties":{"email":{"type":"string","format":"email","description":"Email address of the account to reset. Mutually exclusive with userId."},"userId":{"type":"string","format":"uuid","description":"UUID of the user to reset. Mutually exclusive with email."},"nonce":{"type":"string","minLength":16,"description":"Caller-generated random string (≥16 chars). Must be re-sent verbatim to /auth/resetpassword. Ties the code to this specific reset session."}}}}}},"responses":{"200":{"description":"Code sent (or silently skipped when account does not match any registration)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid request — e.g. both email and userId supplied","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"No identifier supplied and no active session","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/auth/resetpassword":{"post":{"tags":["Auth"],"operationId":"resetPasswordWithCode","summary":"Verify TOTP reset code and set a new password","description":"Verifies the 6-digit TOTP code (from /auth/resetpasswordcode) against the supplied nonce and sets the new password via Firebase Admin SDK. Verify attempts are rate-limited to 5 per minute per account+IP to prevent brute-force.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nonce","code","newPassword"],"properties":{"email":{"type":"string","format":"email","description":"Email address of the account. Mutually exclusive with userId."},"userId":{"type":"string","format":"uuid","description":"UUID of the user. Mutually exclusive with email."},"nonce":{"type":"string","minLength":16,"description":"The same nonce sent to /auth/resetpasswordcode."},"code":{"type":"string","pattern":"^\\d{6}$","description":"6-digit TOTP code received via email."},"newPassword":{"type":"string","minLength":8,"description":"New password to set (minimum 8 characters)."}}}}}},"responses":{"200":{"description":"Password updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid or expired code, or malformed request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"429":{"description":"Too many verify attempts — try again later","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/auth/users/{id}/invite/accept":{"post":{"tags":["Users","Auth"],"operationId":"acceptInvite","summary":"Accept invitation and create user account","description":"Verifies the invitation for the given user ID, sets the user's password via Firebase, and returns a session cookie. The invite link is single-use; subsequent calls return 400.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"UUID of the invited user"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password","first_name","last_name"],"properties":{"email":{"type":"string","format":"email","description":"User email address (must match the invitation)"},"password":{"type":"string","description":"Password to set for the new account"},"first_name":{"type":"string","description":"User's first name"},"last_name":{"type":"string","description":"User's last name"}}}}}},"responses":{"200":{"description":"Invitation accepted — session cookie set (HttpOnly, Secure, SameSite=Lax)","content":{"application/json":{"schema":{"type":"object","required":["session","email"],"properties":{"session":{"type":"string","description":"Session token"},"email":{"type":"string","format":"email"}}}}}},"400":{"description":"Invalid or already-accepted invitation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Invited user not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Account already exists for this email","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/user/apikey":{"put":{"tags":["API Keys"],"operationId":"createApiKey","summary":"Create API key","description":"Generate a new HMAC-signed API key. The full key is returned once and cannot be recovered. Requires session auth (API key auth is rejected).","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-user-id","in":"header","schema":{"type":"string"},"description":"Target user UUID. If omitted, creates for self. Requires ApiKeys WRITE permission for other users."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateBody"}}}},"responses":{"201":{"description":"API key created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"API key auth used, or insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Target user not found or disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/user/apikey/{id}":{"delete":{"tags":["API Keys"],"operationId":"deleteApiKey","summary":"Disable API key","description":"Disable an API key by its database ID. Requires session auth. Deleting another user's key requires ApiKeys WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"API key database UUID"}],"responses":{"200":{"description":"API key disabled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"API key auth used, or insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"API key not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/.well-known/agent.json":{"get":{"tags":["A2A"],"operationId":"getAgentCard","summary":"A2A agent card","description":"Discovery endpoint for the A2A protocol. Returns capabilities, skills, and auth requirements.","responses":{"200":{"description":"Agent card","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCard"}}}}}}},"/a2a":{"post":{"tags":["A2A"],"operationId":"a2aDispatch","summary":"A2A JSON-RPC endpoint","description":"JSON-RPC 2.0 endpoint for A2A protocol. Methods: `tasks/send`, `tasks/get`, `tasks/cancel`. Requires API key + client ID.","security":[{"apiKey":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"},"description":"Client (tenant) UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC response (success or error)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}}}}},"/mcp":{"post":{"tags":["MCP"],"operationId":"mcpPost","summary":"MCP request handler","description":"Streamable HTTP transport for MCP. Handles initialize, tools/list, tools/call, and other JSON-RPC messages. Include `mcp-session-id` header for established sessions.","security":[{"apiKey":[]}],"parameters":[{"name":"mcp-session-id","in":"header","schema":{"type":"string"},"description":"Session ID returned from initialize. Omit for the initial request."}],"requestBody":{"required":true,"description":"JSON-RPC 2.0 message. For `tools/call`, the `params.arguments` object conforms to one of the MCP tool schemas: McpQueryHotelParams, McpGetSessionParams, McpCreateSessionParams, McpGetClientInfoParams, or McpListTranslationsParams.","content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","id","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"oneOf":[{"type":"string"},{"type":"number"}]},"method":{"type":"string","description":"MCP method (e.g. initialize, tools/list, tools/call)"},"params":{"type":"object","properties":{"name":{"type":"string","description":"Tool name (for tools/call)","enum":["query_hotel","get_session","create_session","get_client_info","list_translations"]},"arguments":{"oneOf":[{"$ref":"#/components/schemas/McpQueryHotelParams"},{"$ref":"#/components/schemas/McpGetSessionParams"},{"$ref":"#/components/schemas/McpCreateSessionParams"},{"$ref":"#/components/schemas/McpGetClientInfoParams"},{"$ref":"#/components/schemas/McpListTranslationsParams"}]}}}}}}}},"responses":{"200":{"description":"JSON-RPC response or SSE stream","content":{"application/json":{"schema":{"type":"object"}},"text/event-stream":{"schema":{"type":"string"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"get":{"tags":["MCP"],"operationId":"mcpSse","summary":"MCP SSE stream","description":"Server-Sent Events stream for MCP server notifications. Requires established session.","security":[{"apiKey":[]}],"parameters":[{"name":"mcp-session-id","in":"header","required":true,"schema":{"type":"string"},"description":"Established session ID"}],"responses":{"200":{"description":"SSE stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["MCP"],"operationId":"mcpClose","summary":"Close MCP session","description":"End an MCP session and release resources.","security":[{"apiKey":[]}],"parameters":[{"name":"mcp-session-id","in":"header","required":true,"schema":{"type":"string"},"description":"Session ID to close"}],"responses":{"200":{"description":"Session closed"},"404":{"description":"Session not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients":{"get":{"tags":["Clients"],"operationId":"listClients","summary":"List clients","description":"Return a paginated, searchable, sortable list of active clients. Requires session auth and clients:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of clients to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against client name, description, and website domain"},{"name":"status","in":"query","schema":{"type":"string","enum":["active","disabled","all"]},"description":"Filter by lifecycle status. `active` (default) excludes disabled clients, `disabled` shows only disabled, `all` returns both."},{"name":"order_by","in":"query","schema":{"type":"string","enum":["name","created_date","website_id","demo_client","last_scraped_date","ai_visibility_schedule"],"default":"created_date"},"description":"Column to sort by"},{"name":"sort","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"}],"responses":{"200":{"description":"Paginated list of clients","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientListResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Clients"],"operationId":"createClient","summary":"Create client","description":"Create a new client record. Requires session auth and clients:WRITE permission.","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClientBody"}}}},"responses":{"201":{"description":"Client created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/summary":{"get":{"tags":["Clients"],"operationId":"getClientsSummary","summary":"Get clients summary","description":"Return aggregate counts across all active clients. Requires session auth and clients:READ permission.","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Aggregate client counts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientsSummaryResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}/metrics":{"get":{"tags":["Clients"],"operationId":"getClientMetrics","summary":"Get client Overview metrics","description":"Returns metrics used by the Client Overview tab. Today this is a stub that returns an empty metrics object; shape will stay stable while fields are added in later features.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"responses":{"200":{"description":"Client metrics (empty for now)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientMetricsResponse"}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}/website/rescrape":{"post":{"tags":["Clients"],"operationId":"rescrapeClientWebsite","summary":"Trigger a fresh website scrape","description":"Creates a new SCRAPE-type website_scrape_sessions row and pushes a task to the pipeline SQS queue. When PIPELINE_REQUEST_TASK_QUEUE_URL is unset (local dev), the session is recorded but the SQS push is skipped and the response reports enqueued=false.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescrapeClientBody"}}}},"responses":{"202":{"description":"Scrape session recorded and (optionally) enqueued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescrapeClientResponse"}}}},"400":{"description":"Invalid client id or body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Client has no website to rescrape or is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}/demosite":{"post":{"tags":["Clients"],"operationId":"createClientDemoSite","summary":"Create or recreate the demo preview site","description":"Copies the preview-site template assets in S3 to a per-client folder, rewrites the generated index.html to embed the client's website in an iframe and insert the client id, and records the deploy in preview_websites. Returns 503 when PREVIEW_SITE_BUCKET or PREVIEW_SITE_DOMAIN is unset.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSiteBody"}}}},"responses":{"201":{"description":"Demo site created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSiteResponse"}}}},"400":{"description":"Invalid client id or body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"503":{"description":"Preview-site not configured in this environment","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}":{"get":{"tags":["Clients"],"operationId":"getClient","summary":"Get client by ID","description":"Fetch a single client record by UUID. Requires session auth and clients:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"responses":{"200":{"description":"Client record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Clients"],"operationId":"updateClient","summary":"Update client","description":"Partially update a client record. All body fields are optional. Requires session auth and clients:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClientBody"}}}},"responses":{"200":{"description":"Updated client record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Invalid client id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Client is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Clients"],"operationId":"deleteClient","summary":"Disable client","description":"Soft-disable a client by setting disabled_date and disabled_by. The record is retained for audit purposes. Requires session auth and clients:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"responses":{"200":{"description":"Client disabled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Client is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}/enable":{"post":{"tags":["Clients"],"operationId":"enableClient","summary":"Re-enable a soft-disabled client","description":"Clears `disabled_date` and `disabled_by` so the client becomes active again. Requires clients:WRITE.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"responses":{"200":{"description":"Client enabled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Client is already active","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}/features":{"get":{"tags":["Clients"],"operationId":"listClientFeatures","summary":"List client feature entitlements","description":"Return the feature names a client is currently entitled to (layer-1 entitlement, Maison-admin/business-plan tier). Requires global clients:READ (Maison-admin only).","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"responses":{"200":{"description":"Entitled feature list","content":{"application/json":{"schema":{"type":"object","required":["features"],"properties":{"features":{"type":"array","items":{"type":"string"},"description":"Feature names the client is entitled to"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Clients"],"operationId":"grantClientFeature","summary":"Grant a feature entitlement to a client","description":"Grants a feature to a client (Maison-admin/sales business-plan operation). Idempotent: re-granting a previously revoked feature un-soft-deletes the link. Dual-store: SQL system-of-record first, then projects the full entitled-feature list into DynamoDB (ADR 0009). Requires global clients:WRITE (Maison-admin only).","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["feature"],"properties":{"feature":{"type":"string","minLength":1,"maxLength":200,"description":"Feature name to grant (must exist in the features catalog)"}}}}}},"responses":{"201":{"description":"Feature granted","content":{"application/json":{"schema":{"type":"object","required":["feature"],"properties":{"feature":{"type":"string","description":"Granted feature name"}}}}}},"400":{"description":"Invalid client id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Feature not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{id}/features/{feature}":{"delete":{"tags":["Clients"],"operationId":"revokeClientFeature","summary":"Revoke a feature entitlement from a client","description":"Soft-deletes the client_feature_link row, removing the feature entitlement. No-op if the client is not entitled to the feature. Dual-store: SQL first, then re-projects the full entitled-feature list into DynamoDB (ADR 0009). Requires global clients:WRITE (Maison-admin only).","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"feature","in":"path","required":true,"schema":{"type":"string"},"description":"Feature name to revoke"}],"responses":{"200":{"description":"Feature revoked","content":{"application/json":{"schema":{"type":"object","required":["feature"],"properties":{"feature":{"type":"string","description":"Revoked feature name"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Feature not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users":{"get":{"tags":["Users"],"operationId":"listUsers","summary":"List users","description":"Return a paginated, searchable, sortable list of users. Requires session auth and users:READ permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of users to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against name and email"},{"name":"status","in":"query","schema":{"type":"string","enum":["active","disabled","invited","expired"]},"description":"Filter by computed user status"},{"name":"order_by","in":"query","schema":{"type":"string","enum":["email","first_name","last_name","created_date","last_login_date","status"],"default":"created_date"},"description":"Column to sort by"},{"name":"sort","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"}],"responses":{"200":{"description":"Paginated list of users","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users/invite":{"post":{"tags":["Users"],"operationId":"inviteUser","summary":"Invite a new user","description":"Create a user record and send an invitation email. The user must accept the invite to activate their account. Requires session auth and users:WRITE permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteUserBody"}}}},"responses":{"201":{"description":"User invited — invitation email sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteUserResponse"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"A user with this email already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process invitation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users/{id}/reinvite":{"post":{"tags":["Users"],"operationId":"reinviteUser","summary":"Reinvite a user","description":"Resend an invitation to a user whose status is 'invited' or 'expired'. Resets the invite expiry date. Returns 409 if the user has already accepted their invite or is disabled. Requires session auth and users:WRITE permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReinviteUserBody"}}}},"responses":{"200":{"description":"Invite resent — updated expiry date returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteUserResponse"}}}},"400":{"description":"Invalid user id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"User has already accepted their invite or is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process reinvite","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users/{id}/forcereset":{"post":{"tags":["Users"],"operationId":"forceResetUser","summary":"Force password reset for a user","description":"Sets force_reset_password = true and sends a password reset email via Firebase. The user must be active (accepted invite, not disabled). Returns 409 if the user has not accepted their invite or is disabled. If email sending fails the flag is still set and 200 is returned. Requires session auth and users:WRITE permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"responses":{"200":{"description":"Password reset email sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceResetResponse"}}}},"400":{"description":"Invalid user id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"User has not accepted their invite yet or is disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users/{id}/setpassword":{"post":{"tags":["Users"],"operationId":"setUserPassword","summary":"Directly set a user's password (admin only)","description":"Directly sets the user's Firebase password to the supplied value. Requires session auth and users:WRITE permission. NOTE: This endpoint is intentionally REST-only and is NOT exposed via A2A or MCP. Direct admin password-set is a sensitive privileged operation — force-reset (POST /api/users/{id}/forcereset) is the agent-safe path that sends a password reset email instead.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["password"],"properties":{"password":{"type":"string","minLength":8}}}}}},"responses":{"200":{"description":"Password updated","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid user id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users/{id}/activity":{"get":{"tags":["Users"],"operationId":"getUserActivity","summary":"Get user activity timeline","description":"Return a paginated activity timeline for a user. Events are assembled from role assignments, group membership changes, API key operations, invitations, and login history. Ordered by event_date DESC. Requires session auth and users:READ permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Maximum number of events to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"}],"responses":{"200":{"description":"Paginated activity timeline","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserActivityResponse"}}}},"400":{"description":"Invalid user id or query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/users/{id}":{"get":{"tags":["Users"],"operationId":"getUser","summary":"Get user by ID","description":"Fetch a single user record by UUID. Requires session auth and users:READ permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"responses":{"200":{"description":"User record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListItem"}}}},"400":{"description":"Invalid user id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Users"],"operationId":"updateUser","summary":"Update user","description":"Partially update a user record. All body fields are optional. Set `enable: false` to disable or `enable: true` to re-enable. Requires session auth and users:WRITE permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserBody"}}}},"responses":{"200":{"description":"Updated user record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListItem"}}}},"400":{"description":"Invalid user id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"User is already in the requested state","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Users"],"operationId":"disableUser","summary":"Disable user","description":"Soft-disable a user by setting disabled_date and disabled_by. The record is retained for audit purposes. Requires session auth and users:WRITE permission.","security":[{"sessionAuth":[]},{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"responses":{"200":{"description":"User disabled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid user id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"User is already disabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles":{"get":{"tags":["Roles"],"operationId":"listRoles","summary":"List roles","description":"Return a paginated, searchable, sortable list of roles. Requires session auth and roles:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of roles to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against role name and description"},{"name":"order_by","in":"query","schema":{"type":"string","enum":["name","created_date","policy_count","user_count"],"default":"created_date"},"description":"Column to sort by"},{"name":"sort","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"}],"responses":{"200":{"description":"Paginated list of roles","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Roles"],"operationId":"createRole","summary":"Create role","description":"Create a new role. Requires session auth and roles:WRITE permission.","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoleBody"}}}},"responses":{"201":{"description":"Role created","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles/{id}":{"get":{"tags":["Roles"],"operationId":"getRole","summary":"Get role by ID","description":"Fetch a single role with its policies. Requires session auth and roles:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"}],"responses":{"200":{"description":"Role detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetail"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Role not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Roles"],"operationId":"updateRole","summary":"Update role","description":"Partially update a role. All body fields are optional; at least one must be provided. Requires session auth and roles:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleBody"}}}},"responses":{"200":{"description":"Updated role detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleDetail"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Role not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Roles"],"operationId":"disableRole","summary":"Disable role","description":"Soft-disable a role by setting disabled_date and disabled_by. The record is retained for audit purposes. Requires session auth and roles:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"}],"responses":{"200":{"description":"Role disabled","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Role not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles/{id}/policies":{"post":{"tags":["Roles"],"operationId":"addPolicy","summary":"Add policy to role","description":"Attach a new policy to a role. Requires session auth and roles:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPolicyBody"}}}},"responses":{"201":{"description":"Policy added","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Role not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles/{id}/policies/{policyId}":{"delete":{"tags":["Roles"],"operationId":"removePolicy","summary":"Remove policy from role","description":"Remove a policy from a role. Requires session auth and roles:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"},{"name":"policyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Policy UUID"}],"responses":{"200":{"description":"Policy removed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Role or policy not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles/{id}/users/{userId}":{"delete":{"tags":["Roles"],"operationId":"removeRoleUser","summary":"Remove a user's direct role link","description":"Soft-deletes every active direct user_role_link for the given (role, user). Does NOT touch group-inherited access. Idempotent — returns { removed } count.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User UUID"}],"responses":{"200":{"description":"Remove operation completed","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"integer"}}}}}},"400":{"description":"Invalid role or user id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles/{id}/users":{"post":{"tags":["Roles"],"operationId":"addRoleUser","summary":"Attach a user to a role","description":"Create a direct user_role_link between this role and the given user. Optional client_id scopes the assignment to a single client; omit for a global link. Reviving a soft-deleted link is idempotent.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id"],"properties":{"user_id":{"type":"string","format":"uuid"},"client_id":{"type":"string","format":"uuid","nullable":true,"description":"Optional client scope; null/omitted creates a global link."}}}}}},"responses":{"201":{"description":"User attached to role","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"get":{"tags":["Roles"],"operationId":"listRoleUsers","summary":"List users assigned to a role","description":"Return a paginated list of users assigned to this role. Requires session auth and roles:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of users to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"}],"responses":{"200":{"description":"Paginated list of users assigned to this role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleUsersResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/roles/{id}/groups":{"post":{"tags":["Roles"],"operationId":"addRoleGroup","summary":"Attach a user group to a role","description":"Create a user_group_role_link between this role and the given user group. Optional client_id scopes the assignment to a single client. Reviving a soft-deleted link is idempotent.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_group_id"],"properties":{"user_group_id":{"type":"string","format":"uuid"},"client_id":{"type":"string","format":"uuid","nullable":true,"description":"Optional client scope; null/omitted creates a global link."}}}}}},"responses":{"201":{"description":"Group attached to role","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"get":{"tags":["Roles"],"operationId":"listRoleGroups","summary":"List groups assigned to a role","description":"Return a paginated list of groups assigned to this role. Requires session auth and roles:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Role UUID"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of groups to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"}],"responses":{"200":{"description":"Paginated list of groups assigned to this role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleGroupsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/features":{"get":{"tags":["Features"],"operationId":"listFeatures","summary":"List features","description":"Return a paginated, searchable list of features. Requires session auth and features:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of features to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against feature name and description"}],"responses":{"200":{"description":"Paginated list of features","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Features"],"operationId":"createFeature","summary":"Create feature","description":"Create a new feature. Requires session auth and features:WRITE permission.","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFeatureBody"}}}},"responses":{"201":{"description":"Feature created","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/features/{id}":{"put":{"tags":["Features"],"operationId":"updateFeature","summary":"Update feature","description":"Partially update a feature. All body fields are optional. Requires session auth and features:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Feature ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFeatureBody"}}}},"responses":{"200":{"description":"Updated feature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureItem"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Feature not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Features"],"operationId":"deprecateFeature","summary":"Deprecate feature","description":"Mark a feature as deprecated. The record is retained. Requires session auth and features:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Feature ID"}],"responses":{"200":{"description":"Feature deprecated","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Feature not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/usergroups":{"get":{"tags":["User Groups"],"operationId":"listUserGroups","summary":"List user groups","description":"Return a paginated, searchable, sortable list of user groups. Requires session auth and user-groups:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of groups to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against group name and description"},{"name":"order_by","in":"query","schema":{"type":"string","enum":["name","member_count","created_date"],"default":"created_date"},"description":"Column to sort by"},{"name":"sort","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"}],"responses":{"200":{"description":"Paginated list of user groups","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserGroupListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["User Groups"],"operationId":"createUserGroup","summary":"Create user group","description":"Create a new user group. Requires session auth and user-groups:WRITE permission.","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserGroupBody"}}}},"responses":{"201":{"description":"User group created","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/usergroups/{id}":{"get":{"tags":["User Groups"],"operationId":"getUserGroup","summary":"Get user group detail","description":"Fetch a single user group with its members and roles. Requires session auth and user-groups:READ permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User group UUID"}],"responses":{"200":{"description":"User group detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserGroupDetail"}}}},"400":{"description":"Invalid group id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["User Groups"],"operationId":"updateUserGroup","summary":"Update user group","description":"Partially update a user group. All body fields are optional. Requires session auth and user-groups:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User group UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserGroupBody"}}}},"responses":{"200":{"description":"Updated user group detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserGroupDetail"}}}},"400":{"description":"Invalid group id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["User Groups"],"operationId":"deleteUserGroup","summary":"Delete user group","description":"Soft-delete a user group by setting deleted_date. The record is retained for audit purposes. Requires session auth and user-groups:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User group UUID"}],"responses":{"200":{"description":"Group deleted","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"400":{"description":"Invalid group id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/usergroups/{id}/members":{"put":{"tags":["User Groups"],"operationId":"updateGroupMembers","summary":"Add/remove group members","description":"Add and/or remove members from a user group in a single request. Requires session auth and user-groups:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User group UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupMembersBody"}}}},"responses":{"200":{"description":"Members updated — counts of added and removed records","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupMembersResponse"}}}},"400":{"description":"Invalid group id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/usergroups/{id}/roles":{"put":{"tags":["User Groups"],"operationId":"updateGroupRoles","summary":"Assign/remove group roles","description":"Assign and/or remove roles from a user group in a single request. Requires session auth and user-groups:WRITE permission.","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"User group UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupRolesBody"}}}},"responses":{"200":{"description":"Roles updated — counts of added and removed records","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupRolesResponse"}}}},"400":{"description":"Invalid group id or request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/users":{"get":{"tags":["Clients"],"operationId":"listClientUsers","summary":"List users with access to a client","description":"Returns every user who has a non-deleted user_role_link OR is a member of a user_group with a non-deleted user_group_role_link scoped to this client. Aggregated per user with role + permission summary.","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"responses":{"200":{"description":"List of users with access","content":{"application/json":{"schema":{"type":"object","required":["items","total","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}},"total":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Clients"],"operationId":"addClientUser","summary":"Add a user to a client (invite new or attach existing)","description":"Auto-detects by email. If the user exists, inserts a user_role_link for (user, client, role). If not, provisions a Firebase user + sends an invite with the role attached. role_id must be assignable for this client (not Maison-only, not disabled).","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","role_id"],"properties":{"email":{"type":"string","format":"email"},"role_id":{"type":"string","format":"uuid"},"first_name":{"type":"string"},"last_name":{"type":"string"}}}}}},"responses":{"200":{"description":"Existing user attached","content":{"application/json":{"schema":{"type":"object","required":["kind","user_id"],"properties":{"kind":{"type":"string","enum":["attached"]},"user_id":{"type":"string","format":"uuid"}}}}}},"201":{"description":"New user invited","content":{"application/json":{"schema":{"type":"object","required":["kind","user_id"],"properties":{"kind":{"type":"string","enum":["invited"]},"user_id":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Role is not assignable for this client","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Email already registered in Firebase","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/users/{userId}":{"delete":{"tags":["Clients"],"operationId":"removeClientUser","summary":"Remove all direct role-links for (user, client)","description":"Soft-deletes every active user_role_link for this user-client pair. Does NOT touch user_group memberships. Idempotent.","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Role links removed","content":{"application/json":{"schema":{"type":"object","required":["removed"],"properties":{"removed":{"type":"integer"}}}}}},"400":{"description":"Invalid client or user id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/usergroups":{"get":{"tags":["Clients"],"operationId":"listClientUserGroups","summary":"List user-groups with role links on this client","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"List of user-groups with role links","content":{"application/json":{"schema":{"type":"object","required":["items","total","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}},"total":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Clients"],"operationId":"addClientGroup","summary":"Attach an existing user group to this client with a role","description":"Adds a role link for (group, client). Group must already exist (this endpoint does not create groups). Role must be assignable for this client.","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["group_id","role_id"],"properties":{"group_id":{"type":"string","format":"uuid"},"role_id":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Group attached","content":{"application/json":{"schema":{"type":"object","required":["kind","group_id"],"properties":{"kind":{"type":"string","enum":["attached"]},"group_id":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Role is not assignable for this client","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User group not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/usergroups/{groupId}":{"delete":{"tags":["Clients"],"operationId":"removeClientUserGroup","summary":"Remove all role-links for (group, client)","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"groupId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Role links removed","content":{"application/json":{"schema":{"type":"object","required":["removed"],"properties":{"removed":{"type":"integer"}}}}}},"400":{"description":"Invalid client or group id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/roles":{"get":{"tags":["Clients"],"operationId":"listClientAssignableRoles","summary":"List roles that can be assigned to users/groups for this client","description":"Excludes disabled roles and roles marked visibility_maison_only=true (Maison-admin-only). Returns both global roles and client-scoped roles belonging to this client.","security":[{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Assignable roles","content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/regions":{"get":{"tags":["Settings"],"operationId":"listRegions","summary":"List regions","description":"Return a paginated, searchable list of regions for a client. Requires session auth and settings:READ permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of regions to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against region name"}],"responses":{"200":{"description":"Paginated list of regions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegionListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Settings"],"operationId":"createRegion","summary":"Create region","description":"Create a new region for a client. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRegionBody"}}}},"responses":{"201":{"description":"Region created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/regions/{id}":{"put":{"tags":["Settings"],"operationId":"updateRegion","summary":"Update region","description":"Partially update a region. All body fields are optional. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Region UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRegionBody"}}}},"responses":{"200":{"description":"Updated region","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegionListItem"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Region not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Settings"],"operationId":"deleteRegion","summary":"Delete region","description":"Soft-delete a region by setting deleted_date. The record is retained for audit purposes. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Region UUID"}],"responses":{"200":{"description":"Region deleted","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Region not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/departments":{"get":{"tags":["Settings"],"operationId":"listDepartments","summary":"List departments","description":"Return a paginated, searchable list of departments for a client. Requires session auth and settings:READ permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of departments to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against department name"}],"responses":{"200":{"description":"Paginated list of departments","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Settings"],"operationId":"createDepartment","summary":"Create department","description":"Create a new department for a client. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDepartmentBody"}}}},"responses":{"201":{"description":"Department created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/departments/{id}":{"put":{"tags":["Settings"],"operationId":"updateDepartment","summary":"Update department","description":"Partially update a department. All body fields are optional. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Department UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDepartmentBody"}}}},"responses":{"200":{"description":"Updated department","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentListItem"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Department not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Settings"],"operationId":"deleteDepartment","summary":"Delete department","description":"Soft-delete a department by setting deleted_date. The record is retained for audit purposes. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Department UUID"}],"responses":{"200":{"description":"Department deleted","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Department not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/tags":{"get":{"tags":["Settings"],"operationId":"listTags","summary":"List tags","description":"Return a paginated, searchable list of tags for a client. Requires session auth and settings:READ permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Maximum number of tags to return (1-100)"},{"name":"after","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned in the previous response as `nextCursor`"},{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Case-insensitive search string matched against tag name"}],"responses":{"200":{"description":"Paginated list of tags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Settings"],"operationId":"createTag","summary":"Create tag","description":"Create a new tag for a client. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagBody"}}}},"responses":{"201":{"description":"Tag created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/clients/{clientId}/tags/{id}":{"put":{"tags":["Settings"],"operationId":"updateTag","summary":"Update tag","description":"Partially update a tag. All body fields are optional. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Tag UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTagBody"}}}},"responses":{"200":{"description":"Updated tag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagListItem"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Tag not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Settings"],"operationId":"deleteTag","summary":"Delete tag","description":"Soft-delete a tag by setting deleted_date. The record is retained for audit purposes. Requires session auth and settings:WRITE permission.","security":[{"apiKey":[]},{"sessionAuth":[]}],"parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Client UUID"},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Tag UUID"}],"responses":{"200":{"description":"Tag deleted","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Tag not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/auth/":{"post":{"tags":["Legacy"],"operationId":"legacyBcAuthLogin","summary":"DEPRECATED: Legacy login","description":"DEPRECATED: Accepts legacy Basic-in-body credentials and issues a session cookie. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string"},"auth":{"type":"string"}}}}}},"responses":{"200":{"description":"Login successful"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/auth/logout":{"post":{"tags":["Legacy"],"operationId":"legacyBcAuthLogout","summary":"DEPRECATED: Legacy logout","description":"DEPRECATED: Revokes the session cookie. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","responses":{"200":{"description":"Logged out"},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/client":{"post":{"tags":["Legacy"],"operationId":"legacyBcClientCreate","summary":"DEPRECATED: Create client","description":"DEPRECATED: Creates a new client with an associated website and hotel config, and deploys the demo preview site (best-effort; matches the legacy agent-server which deployed it on create). Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","website"],"properties":{"name":{"type":"string"},"demoClient":{"type":"boolean"},"website":{"type":"string","format":"uri"},"previewSiteConfiguration":{"type":"object"},"hotelConfig":{"type":"object"},"scrapeLanguage":{"type":"string"}}}}}},"responses":{"201":{"description":"Created — { client: { id } }"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"get":{"tags":["Legacy"],"operationId":"legacyBcClientGet","summary":"DEPRECATED: Get client","description":"DEPRECATED: Returns the client identified by x-client-id header. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Client record"},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Legacy"],"operationId":"legacyBcClientUpdate","summary":"DEPRECATED: Update client","description":"DEPRECATED: Updates the client identified by x-client-id header. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Updated client"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/client/list":{"get":{"tags":["Legacy"],"operationId":"legacyBcClientList","summary":"DEPRECATED: List clients","description":"DEPRECATED: Returns all active clients as an array. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Array of clients"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/client/summary":{"get":{"tags":["Legacy"],"operationId":"legacyBcClientSummary","summary":"DEPRECATED: Get client summary","description":"DEPRECATED: Returns minimal client summary. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Client summary"},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/hotelconfiguration":{"put":{"tags":["Legacy"],"operationId":"legacyBcHotelConfiguration","summary":"DEPRECATED: Update hotel configuration","description":"DEPRECATED: No-op shim — hotel_config column not in current schema. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"204":{"description":"Accepted (no-op)"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/website":{"put":{"tags":["Legacy"],"operationId":"legacyBcWebsiteUpdate","summary":"DEPRECATED: Update website","description":"DEPRECATED: No-op shim — website updates use v2 endpoints. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"204":{"description":"Accepted (no-op)"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/client/source/list":{"get":{"tags":["Legacy"],"operationId":"legacyBcClientSourceList","summary":"DEPRECATED: List client knowledge sources","description":"DEPRECATED: Returns knowledge source links for the client. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Array of source links"},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/website/{id}/sync":{"put":{"tags":["Legacy"],"operationId":"legacyBcWebsiteSync","summary":"DEPRECATED: Trigger website sync","description":"DEPRECATED: Returns 202 Accepted; actual sync is pipeline-side. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Sync queued"},"400":{"description":"Invalid website id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/qa/counts":{"get":{"tags":["Legacy"],"operationId":"legacyQaCounts","summary":"DEPRECATED: QA pair counts","description":"DEPRECATED: Returns active/deleted/total counts for a client's QA namespace (proxies to llm-service). Consumer: legacy/qa-editor.","deprecated":true,"x-legacy-consumer":"qa-editor","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"QA pair counts","content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"integer"},"deleted":{"type":"integer"},"total":{"type":"integer"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/qa/deduplicate":{"post":{"tags":["Legacy"],"operationId":"legacyBcQaDeduplicate","summary":"DEPRECATED: Deduplicate QA","description":"DEPRECATED: Enqueues a deduplicate-kb pipeline task. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"similarityThreshold":{"type":"number"},"batchSize":{"type":"integer"}}}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"},"messageId":{"type":"string","nullable":true}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/qa/consistencycheck":{"post":{"tags":["Legacy"],"operationId":"legacyBcQaConsistencyCheck","summary":"DEPRECATED: QA consistency check","description":"DEPRECATED: Enqueues a check-consistency pipeline task. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"}}}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"},"messageId":{"type":"string","nullable":true}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/document":{"get":{"tags":["Legacy"],"operationId":"legacyBcDocumentList","summary":"DEPRECATED: List documents","description":"DEPRECATED: Returns documents linked to the client. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Array of documents"},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/client/asset":{"post":{"tags":["Legacy"],"operationId":"legacyBcClientAsset","summary":"DEPRECATED: Upload client asset","description":"DEPRECATED: Uploads an image asset to S3 (CLIENT_ASSETS_BUCKET) and writes the resolved path into DynamoDB clientAssets. Named visual-branding uploads (assetType=icon|hero|heroDark) use a deterministic S3 key and update clientAssets.<field>; library uploads (no assetType) append to clientAssets.list. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"Raster image to upload. The type is validated from the file's magic bytes (PNG, JPEG, GIF, or WEBP only); SVG and other/spoofed types are rejected with 400. The stored extension is derived from the detected type, not the filename."},"assetType":{"type":"string","enum":["icon","hero","heroDark"],"description":"Visual-branding asset type. Omit for general library upload."},"description":{"type":"string","description":"Optional description (library uploads only)"},"published":{"type":"boolean","description":"Published flag (library uploads only)"}}}}}},"responses":{"200":{"description":"Asset uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"asset_url":{"type":"string","description":"S3-relative URL of the stored asset"}}}}}},"400":{"description":"Invalid client id / Missing file / Unknown asset type","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"503":{"description":"Asset storage not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Legacy"],"operationId":"legacyBcClientAssetDelete","summary":"DEPRECATED: Delete client asset","description":"DEPRECATED: Deletes a named visual-branding asset (?assetType=icon|hero|heroDark) or a general library asset (?assetType=<id>) from S3 and removes the corresponding clientAssets entry from DynamoDB. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"assetType","in":"query","required":true,"schema":{"type":"string"},"description":"Visual-branding type (icon|hero|heroDark) or library asset id"}],"responses":{"200":{"description":"Asset deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"Invalid client id / Missing asset type","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/translations":{"put":{"tags":["Legacy"],"operationId":"legacyBcTranslationsUpdate","summary":"DEPRECATED: Update translations","description":"DEPRECATED: Translations stored in DynamoDB via agent-core; returns 204. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"204":{"description":"Accepted"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/analytics/chatlogs":{"post":{"tags":["Legacy"],"operationId":"legacyBcAnalyticsChatlogs","summary":"DEPRECATED: Trigger chat-logs analytics","description":"DEPRECATED: Fetches chat logs from DynamoDB, calls llm-service for analysis, uploads HTML report to S3, and dispatches email. Custom mode when recipients.to supplied; scheduled mode otherwise. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string","description":"Client UUID (falls back to x-client-id header)"},"startDate":{"type":"string","format":"date-time","description":"Start of date range (ISO 8601). Defaults to 8 days ago."},"endDate":{"type":"string","format":"date-time","description":"End of date range (ISO 8601). Defaults to 24 hours ago."},"recipients":{"type":"object","description":"Custom recipients. When present triggers custom mode (no unsubscribe). to is required.","required":["to"],"properties":{"to":{"type":"array","items":{"type":"string","format":"email"}},"cc":{"type":"array","items":{"type":"string","format":"email"}},"bcc":{"type":"array","items":{"type":"string","format":"email"}}}}}}}}},"responses":{"202":{"description":"Accepted — analytics run complete","content":{"application/json":{"schema":{"type":"object","properties":{"analyticsId":{"type":"string"},"queued":{"type":"boolean"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/logs/chat":{"get":{"tags":["Legacy"],"operationId":"legacyBcLogsChat","summary":"DEPRECATED: Download chat logs as XLSX","description":"DEPRECATED: Streams an XLSX download of raw chat-log messages for a client in the given date window. Returns 204 when no logs match. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"startDate","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"ISO 8601 window start"},{"name":"endDate","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"ISO 8601 window end"},{"name":"skipFresh","in":"query","required":false,"schema":{"type":"string"},"description":"true (default) omits first-visit sessions"},{"name":"skipAdmin","in":"query","required":false,"schema":{"type":"string"},"description":"true (default) omits admin sessions"},{"name":"skipTestSessions","in":"query","required":false,"schema":{"type":"string"},"description":"true (default) omits test sessions"}],"responses":{"200":{"description":"XLSX workbook download","headers":{"Content-Disposition":{"description":"attachment; filename=\"<slug>-chatlogs-<start>-<end>.xlsx\"","schema":{"type":"string"}}},"content":{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}}}},"204":{"description":"No chat logs match the date range"},"400":{"description":"Invalid request (client id / date range)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Client not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/analytics/aivisibility":{"post":{"tags":["Legacy"],"operationId":"legacyBcAnalyticsAiVisibility","summary":"DEPRECATED: Trigger AI visibility analytics","description":"DEPRECATED: Enqueues a run-ai-visibility pipeline task. sessionId generated server-side if absent. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"sessionId":{"type":"string","description":"Session ID; generated server-side if absent"}}}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"},"messageId":{"type":"string","nullable":true},"sessionId":{"type":"string"}}}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/test/session/list":{"get":{"tags":["Legacy"],"operationId":"legacyBcTestSessionList","summary":"DEPRECATED: List test sessions","description":"DEPRECATED: Returns test sessions from the testing schema. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Array of test sessions"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/test/session/report":{"get":{"tags":["Legacy"],"operationId":"legacyBcTestSessionReport","summary":"DEPRECATED: Get test session report","description":"DEPRECATED: Returns the test session report object { id, description, tests[] } for a given session_id (accepts ?id= or ?session_id=). Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"session_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Test session report ({ id, description, tests[] })"},"400":{"description":"Missing session_id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Test session not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/test/session/gfaq":{"post":{"tags":["Legacy"],"operationId":"legacyBcTestSessionGfaq","summary":"DEPRECATED: Generate FAQ from test session","description":"DEPRECATED: Returns 202 Accepted; FAQ generation is pipeline-side. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"202":{"description":"Queued"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/subscription":{"post":{"tags":["Subscriptions"],"operationId":"subscriptionCreate","summary":"Create email subscribers (bulk)","description":"Inserts one or more rows into email_subscribers. Resolves known users by email (case-insensitive). Requires session auth.","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subscribers"],"properties":{"subscribers":{"type":"array","minItems":1,"items":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"emailType":{"type":"string"},"email_type":{"type":"string"},"clientId":{"type":"string"},"client_id":{"type":"string"}}}}}}}}},"responses":{"201":{"description":"Subscribers created"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Subscriptions"],"operationId":"subscriptionDelete","summary":"Soft-delete subscribers by id","description":"Soft-deletes email_subscriber rows. Pass comma-separated UUIDs in the ?ids= query param. Requires session auth.","security":[{"sessionAuth":[]}],"parameters":[{"in":"query","name":"ids","required":true,"schema":{"type":"string"},"description":"Comma-separated subscriber UUIDs"}],"responses":{"200":{"description":"Subscribers deleted"},"400":{"description":"Missing ids query parameter","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/subscription/list":{"get":{"tags":["Subscriptions"],"operationId":"subscriptionList","summary":"List active subscribers for a client","description":"Returns active (non-deleted, non-unsubscribed) email_subscribers for the x-client-id client. Requires session auth.","security":[{"sessionAuth":[]}],"parameters":[{"in":"header","name":"x-client-id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Array of subscribers"},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/subscription/unsubscribe":{"post":{"tags":["Subscriptions"],"operationId":"subscriptionUnsubscribe","summary":"Token-based email unsubscribe","description":"Public endpoint — no auth required. Sets unsubscribed_date on the email_subscribers row identified by the ?id= UUID.","parameters":[{"in":"query","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Unsubscribed or already unsubscribed"},"400":{"description":"Invalid subscription id"},"404":{"description":"Subscription not found"},"500":{"description":"Server error"}}}},"/api/legacy/user/":{"get":{"tags":["Legacy"],"operationId":"legacyBcUserGet","summary":"DEPRECATED: Get current user","description":"DEPRECATED: Returns the authenticated user record. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"User record"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/user/create":{"post":{"tags":["Legacy"],"operationId":"legacyBcUserCreate","summary":"DEPRECATED: Create user","description":"DEPRECATED: Inserts a minimal user record. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}}}}}},"responses":{"201":{"description":"User created"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"409":{"description":"Duplicate email","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/user/adminsetpassword":{"put":{"tags":["Legacy"],"operationId":"legacyBcUserAdminSetPassword","summary":"DEPRECATED: Admin set password","description":"DEPRECATED: Flags force_reset_password on the user record. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string"},"new_password":{"type":"string"}}}}}},"responses":{"200":{"description":"Password flagged for reset"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/user/addroles":{"post":{"tags":["Legacy"],"operationId":"legacyBcUserAddRoles","summary":"DEPRECATED: Add roles to user","description":"DEPRECATED: Inserts user_role_links. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string"},"role_ids":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Roles added"},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/user/list":{"get":{"tags":["Legacy"],"operationId":"legacyBcUserList","summary":"DEPRECATED: List users","description":"DEPRECATED: Returns up to 100 users. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Array of users"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/user/role/list":{"get":{"tags":["Legacy"],"operationId":"legacyBcUserRoleList","summary":"DEPRECATED: List roles","description":"DEPRECATED: Returns all active roles for dropdown population. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"responses":{"200":{"description":"Array of roles"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to process request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/salesagent":{"get":{"tags":["Legacy"],"operationId":"legacySalesAgentGet","summary":"Get sales agent config","description":"DEPRECATED: Returns the DynamoDB salesAgent config (enabled flag + opportunities). Consumer: legacy/business-console, legacy/qa-editor.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Sales agent config","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"opportunities":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to read sales agent config","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Legacy"],"operationId":"legacySalesAgentPut","summary":"Update sales agent config","description":"DEPRECATED: Updates salesAgent DynamoDB config. Disabling wipes opportunities. Consumer: legacy/business-console, legacy/qa-editor.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"opportunities":{"type":"array","items":{"type":"object"}}}}}}},"responses":{"200":{"description":"Updated sales agent config"},"400":{"description":"No fields to update / Invalid enabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to update sales agent config","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/salesagent/whitelist":{"get":{"tags":["Legacy"],"operationId":"legacySalesAgentWhitelistGet","summary":"Get sales agent email whitelist","description":"DEPRECATED: Returns active SALES_AGENT_LEAD whitelist entries. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Active whitelist","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to read sales agent whitelist","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"post":{"tags":["Legacy"],"operationId":"legacySalesAgentWhitelistPost","summary":"Add emails to sales agent whitelist","description":"DEPRECATED: Inserts new email rows (deduplication enforced). Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["emails"],"properties":{"emails":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Updated whitelist with added count"},"400":{"description":"No valid emails to add","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to add whitelist entries","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["Legacy"],"operationId":"legacySalesAgentWhitelistDelete","summary":"Soft-delete whitelist entries","description":"DEPRECATED: Stamps deleted_date/deleted_by on the specified rows. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Remaining active whitelist"},"400":{"description":"Provide one or more ids","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to delete whitelist entries","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/salesagent/recipients":{"get":{"tags":["Legacy"],"operationId":"legacySalesAgentRecipients","summary":"Get sales agent email recipients","description":"DEPRECATED: Returns {email} list from active SALES_AGENT_LEAD whitelist. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Recipient list","content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"}}}}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to read sales agent recipients","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/branding":{"get":{"tags":["Legacy"],"operationId":"legacyBrandingGet","summary":"Get branding config","description":"DEPRECATED: Returns the DynamoDB branding.enabled flag. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Branding config","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to read branding config","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Legacy"],"operationId":"legacyBrandingPut","summary":"Update branding config","description":"DEPRECATED: Writes branding.enabled to DynamoDB. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["enabled"],"properties":{"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated"},"400":{"description":"Invalid enabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to update branding config","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/documents/config":{"get":{"tags":["Legacy"],"operationId":"legacyDocumentsConfigGet","summary":"Get documents config","description":"DEPRECATED: Returns the DynamoDB documents.enabled flag. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Documents config","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to read documents config","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Legacy"],"operationId":"legacyDocumentsConfigPut","summary":"Update documents config","description":"DEPRECATED: Writes documents.enabled to DynamoDB. Consumer: legacy/business-console.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["enabled"],"properties":{"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated"},"400":{"description":"Invalid enabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to update documents config","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/legacy/client/chatstyles":{"get":{"tags":["Legacy"],"operationId":"legacyChatStylesGet","summary":"Get chat styles","description":"DEPRECATED: Returns the DynamoDB chatStyles map. Consumer: legacy/business-console, legacy/qa-editor.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Chat styles map","content":{"application/json":{"schema":{"type":"object","properties":{"chatStyles":{"type":"object"}}}}}},"400":{"description":"Invalid client id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to read chat styles","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"put":{"tags":["Legacy"],"operationId":"legacyChatStylesPut","summary":"Update chat styles","description":"DEPRECATED: Writes chatStyles map to DynamoDB. Consumer: legacy/business-console, legacy/qa-editor.","deprecated":true,"x-legacy-consumer":"business-console","security":[{"sessionAuth":[]}],"parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chatStyles"],"properties":{"chatStyles":{"type":"object"}}}}}},"responses":{"200":{"description":"Updated chat styles"},"400":{"description":"Invalid chatStyles","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Unable to update chat styles","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/tools/sitecheck":{"post":{"tags":["Tools"],"operationId":"siteCheck","summary":"Analyze a website for Maison embed status","description":"Fetches the given URL, detects the framework, and checks whether the Maison chat widget is properly embedded.","security":[{"apiKey":[]},{"sessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteCheckBody"}}}},"responses":{"200":{"description":"Analysis result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteCheckResponse"}}}},"400":{"description":"Invalid URL or private IP blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"URL could not be fetched","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"TextPart":{"type":"object","required":["type","text"],"properties":{"type":{"type":"string","enum":["text"]},"text":{"type":"string"}}},"Message":{"type":"object","required":["role","parts"],"properties":{"role":{"type":"string","enum":["user","agent"]},"parts":{"type":"array","items":{"$ref":"#/components/schemas/TextPart"},"minItems":1}}},"Artifact":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"parts":{"type":"array","items":{"$ref":"#/components/schemas/TextPart"},"minItems":1},"index":{"type":"integer","minimum":0,"default":0}},"required":["parts"]},"TaskStatus":{"type":"object","required":["state","timestamp"],"properties":{"state":{"type":"string","enum":["submitted","working","completed","failed","canceled"]},"message":{"$ref":"#/components/schemas/Message"},"timestamp":{"type":"string"}}},"Task":{"type":"object","required":["id","clientId","status"],"properties":{"id":{"type":"string"},"clientId":{"type":"string"},"status":{"$ref":"#/components/schemas/TaskStatus"},"artifacts":{"type":"array","items":{"$ref":"#/components/schemas/Artifact"}},"history":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"metadata":{"type":"object","additionalProperties":true},"expiresAt":{"type":"integer"}}},"JsonRpcRequest":{"type":"object","required":["jsonrpc","id","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"oneOf":[{"type":"string"},{"type":"number"}]},"method":{"type":"string"},"params":{"type":"object","additionalProperties":true}}},"JsonRpcResponse":{"type":"object","required":["jsonrpc","id"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"oneOf":[{"type":"string"},{"type":"number"}]},"result":{},"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"data":{}},"required":["code","message"]}}},"ChatMessage":{"type":"object","required":["role","message","datetime"],"properties":{"role":{"type":"string","enum":["assistant","user","system"]},"message":{"type":"string"},"datetime":{"oneOf":[{"type":"string"},{"type":"number"}]},"isOpeningMessage":{"type":"boolean"},"isAdmin":{"type":"boolean"},"context":{"type":"array","items":{"type":"string"}},"type":{"type":"string"},"locale":{"type":"string"},"isTestSession":{"type":"boolean"}}},"QueryBody":{"type":"object","required":["id","query","messages"],"properties":{"id":{"type":"string","description":"Session ID"},"query":{"type":"string","description":"User's query text"},"localTime":{"type":"string"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}},"pmsConfig":{"type":"object","nullable":true,"properties":{"pmsProvider":{"type":"string"},"propertyConfig":{"type":"array","items":{"type":"object"}},"serviceConfig":{"type":"array","items":{"type":"object"}},"rateConfig":{"type":"array","items":{"type":"object"}}}},"messageToken":{"type":"string"}}},"ApiKeyCreateBody":{"type":"object","properties":{"description":{"type":"string","default":"API key"}}},"ApiKeyCreateResponse":{"type":"object","required":["key","masked","description","userId"],"properties":{"key":{"type":"string","description":"Full API key — shown once, store securely"},"masked":{"type":"string","description":"Masked key for display"},"description":{"type":"string"},"userId":{"type":"string"}}},"McpQueryHotelParams":{"type":"object","required":["clientId","query"],"properties":{"clientId":{"type":"string","description":"Client/tenant ID"},"query":{"type":"string","description":"The question to ask"},"sessionId":{"type":"string","description":"Existing session ID to continue a conversation"},"locale":{"type":"string","description":"Locale code (e.g. en, fr, de)"}}},"McpGetSessionParams":{"type":"object","required":["clientId","sessionId"],"properties":{"clientId":{"type":"string","description":"Client/tenant ID"},"sessionId":{"type":"string","description":"Session ID to retrieve"}}},"McpCreateSessionParams":{"type":"object","required":["clientId"],"properties":{"clientId":{"type":"string","description":"Client/tenant ID"},"locale":{"type":"string","description":"Locale code"}}},"McpGetClientInfoParams":{"type":"object","required":["clientId","fields"],"properties":{"clientId":{"type":"string","description":"Client/tenant ID"},"fields":{"type":"string","description":"Comma-separated field paths (e.g. hotelConfig.name,clientAssets.icon)"},"locale":{"type":"string","description":"Locale code for translation fields"}}},"McpListTranslationsParams":{"type":"object","required":["clientId","keys"],"properties":{"clientId":{"type":"string","description":"Client/tenant ID"},"keys":{"type":"string","description":"Comma-separated translation key names, or '*' for all"},"locales":{"type":"string","description":"Comma-separated locale codes, or '*' for all"}}},"SiteCheckBody":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"The website URL to analyze"}}},"SiteCheckResponse":{"type":"object","properties":{"url":{"type":"string"},"fetchable":{"type":"boolean"},"framework":{"type":"string"},"hasAgentScript":{"type":"boolean"},"scriptPlacement":{"type":"string","nullable":true},"hasInitCall":{"type":"boolean"},"initCorrect":{"type":"boolean"},"clientIdPresent":{"type":"boolean"},"bestPlacement":{"type":"string"},"diagnosis":{"type":"string"},"suggestedPlatformGuide":{"type":"string"}}},"ScheduleEnum":{"type":"string","enum":["NONE","DAILY","WEEKLY","BI_WEEKLY","MONTHLY"],"description":"Scheduling frequency for analytics or AI visibility runs"},"Client":{"type":"object","required":["id","name","website_id","demo_client","created_date"],"properties":{"id":{"type":"string","format":"uuid","description":"Client UUID"},"name":{"type":"string","description":"Client display name"},"website_id":{"type":"string","format":"uuid","description":"Primary website UUID"},"demo_client":{"type":"boolean","description":"Whether this is a demo client"},"description":{"type":"string","nullable":true,"description":"Optional description of the client"},"scrape_language":{"type":"string","nullable":true,"description":"BCP-47 language code for website scraping (e.g. en, fr)"},"chat_logs_analytics_schedule":{"$ref":"#/components/schemas/ScheduleEnum","description":"Frequency of chat log analytics runs"},"ai_visibility_schedule":{"$ref":"#/components/schemas/ScheduleEnum","description":"Frequency of AI visibility runs"},"last_scraped_date":{"type":"string","format":"date-time","nullable":true,"description":"Timestamp of the most recent website scrape (from linked website)"},"created_date":{"type":"string","format":"date-time","description":"Timestamp when the client was created"},"created_by":{"type":"string","nullable":true,"description":"User ID of the creator"},"disabled_date":{"type":"string","format":"date-time","nullable":true,"description":"Timestamp when the client was disabled (null if active)"},"disabled_by":{"type":"string","nullable":true,"description":"User ID who disabled the client"},"_computed":{"type":"object","nullable":true,"description":"Server-computed display fields derived from the client row. Present on single-client responses only.","properties":{"initials":{"type":"string","description":"First two characters of the client name, uppercased (e.g. 'HO'). '?' when name is empty."},"status_label":{"type":"string","enum":["Active","Demo","Disabled"],"description":"Human-readable status. Disabled > Demo > Active precedence."},"status_variant":{"type":"string","enum":["success","warning","danger"],"description":"Badge variant matching status_label."},"website_url":{"type":"string","nullable":true,"description":"Resolved website URL. Null when not available."},"website_hidden":{"type":"boolean","description":"True when website_url is null (hides the website link in the UI)."},"demo_hidden":{"type":"boolean","description":"True when demo_client is false (hides the Demo badge in the UI)."},"is_disabled":{"type":"boolean","description":"True when disabled_date is non-null."},"primary_action_label":{"type":"string","description":"Label for the primary CTA button: 'Enable' when disabled, 'Edit' otherwise."},"disable_label":{"type":"string","description":"Label for the disable toggle action: 'Enable Client' when disabled, 'Disable Client' otherwise."},"created_date_formatted":{"type":"string","description":"created_date formatted for display (e.g. 'Jan 1, 2024')."}},"required":["initials","status_label","status_variant","website_url","website_hidden","demo_hidden","is_disabled","primary_action_label","disable_label","created_date_formatted"]}}},"ClientsSummaryResponse":{"type":"object","required":["totalActive","last90Days"],"properties":{"totalActive":{"type":"integer","description":"Number of active (non-disabled) clients"},"last90Days":{"type":"integer","description":"Number of clients created within the last 90 days (still active)"}}},"ClientMetricsResponse":{"type":"object","required":["clientId","metrics"],"properties":{"clientId":{"type":"string","format":"uuid"},"metrics":{"type":"object","description":"Placeholder for Overview-tab metrics. Empty today; populated in a later release."}}},"RescrapeClientBody":{"type":"object","properties":{"language":{"type":"string","minLength":1,"maxLength":50,"nullable":true,"description":"Optional override for the scrape language (e.g. 'English', 'French'). Defaults to the client's configured scrape_language, then 'English'."}}},"RescrapeClientResponse":{"type":"object","required":["scrapeSessionId","enqueued"],"properties":{"scrapeSessionId":{"type":"string","format":"uuid"},"enqueued":{"type":"boolean","description":"true when the task was pushed to the pipeline SQS queue. false when the queue url is unset (e.g. local dev) — the session is recorded but no worker will pick it up."}}},"DemoSiteBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Override name used to slugify the preview-site folder."},"website":{"type":"string","format":"uri","description":"Override client website rendered inside the preview iframe. Defaults to the client's configured website."}}},"DemoSiteResponse":{"type":"object","required":["previewUrl","folderName"],"properties":{"previewUrl":{"type":"string","format":"uri"},"folderName":{"type":"string"}}},"ClientListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Client"},"description":"Page of client records"},"total":{"type":"integer","description":"Total number of matching clients (across all pages)"},"limit":{"type":"integer","description":"Page size used for this request"},"nextCursor":{"type":"string","nullable":true,"description":"Opaque cursor for the next page. Null when there are no more pages."},"hasMore":{"type":"boolean","description":"Whether additional pages exist after this one"}}},"CreateClientBody":{"type":"object","required":["name","website_id"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Client name"},"website_id":{"type":"string","format":"uuid","description":"UUID of the primary website"},"demo_client":{"type":"boolean","default":false,"description":"Whether this client is a demo"},"description":{"type":"string","maxLength":1000,"nullable":true,"description":"Optional client description"},"scrape_language":{"type":"string","maxLength":10,"nullable":true,"description":"BCP-47 language code for scraping (e.g. en, fr)"},"chat_logs_analytics_schedule":{"$ref":"#/components/schemas/ScheduleEnum","default":"NONE"},"ai_visibility_schedule":{"$ref":"#/components/schemas/ScheduleEnum","default":"NONE"}}},"UserStatus":{"type":"string","enum":["active","disabled","invited","expired"],"description":"Computed user status derived from disabled_date, accepted_date, and invite_expiry_date"},"UserListItem":{"type":"object","required":["id","email","first_name","last_name","status","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"first_name":{"type":"string"},"last_name":{"type":"string"},"phone":{"type":"string","nullable":true},"status":{"$ref":"#/components/schemas/UserStatus"},"roles":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"client_name":{"type":"string","nullable":true}}}},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"api_key_count":{"type":"integer"},"last_login_date":{"type":"string","format":"date-time","nullable":true},"disabled_date":{"type":"string","format":"date-time","nullable":true},"created_date":{"type":"string","format":"date-time"},"invited_by_email":{"type":"string","nullable":true},"invite_expiry_date":{"type":"string","format":"date-time","nullable":true},"accepted_date":{"type":"string","format":"date-time","nullable":true}}},"UserListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/UserListItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}},"InviteUserBody":{"type":"object","required":["email","first_name","last_name"],"properties":{"email":{"type":"string","format":"email","maxLength":254},"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","minLength":1,"maxLength":100},"role_ids":{"type":"array","items":{"type":"object","properties":{"role_id":{"type":"string","format":"uuid"},"client_id":{"type":"string","format":"uuid","nullable":true}}}},"group_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"invite_expiry_days":{"type":"integer","minimum":1,"maximum":90,"default":7}}},"UpdateUserBody":{"type":"object","properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","minLength":1,"maxLength":100},"phone":{"type":"string","maxLength":30,"nullable":true},"enable":{"type":"boolean"}}},"InviteUserResponse":{"type":"object","required":["id","email","invite_expiry_date"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"invite_expiry_date":{"type":"string","format":"date-time"}}},"ReinviteUserBody":{"type":"object","properties":{"invite_expiry_days":{"type":"integer","minimum":1,"maximum":90,"default":7}}},"ForceResetResponse":{"type":"object","required":["message"],"properties":{"message":{"type":"string"}}},"ActivityEvent":{"type":"object","required":["event_date","action"],"properties":{"event_date":{"type":"string","format":"date-time","description":"ISO 8601 timestamp of when the event occurred"},"action":{"type":"string","enum":["login","role_assigned","role_removed","group_added","group_removed","api_key_created","api_key_disabled","invited","invite_accepted","disabled"],"description":"Type of activity event"},"actor_email":{"type":"string","nullable":true,"description":"Email of the user who performed the action (null if performed by the user themselves or unknown)"},"detail":{"type":"string","nullable":true,"description":"Additional context (e.g. role name, group name, API key description)"}}},"UserActivityResponse":{"type":"object","required":["items","hasMore","nextCursor"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ActivityEvent"},"description":"Page of activity events ordered by event_date DESC"},"hasMore":{"type":"boolean","description":"Whether additional pages exist after this one"},"nextCursor":{"type":"string","nullable":true,"description":"Opaque cursor for the next page. Null when there are no more pages."}}},"RoleListItem":{"type":"object","required":["id","name","description","visibility_maison_only","policy_count","user_count","group_count","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string","nullable":true},"client_name":{"type":"string","nullable":true},"visibility_maison_only":{"type":"boolean","nullable":true},"policy_count":{"type":"integer"},"user_count":{"type":"integer"},"group_count":{"type":"integer"},"created_date":{"type":"string","format":"date-time"},"disabled_date":{"type":"string","format":"date-time","nullable":true}}},"RoleDetail":{"type":"object","required":["id","name","description","visibility_maison_only","policy_count","user_count","group_count","created_date","policies"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string","nullable":true},"client_name":{"type":"string","nullable":true},"visibility_maison_only":{"type":"boolean","nullable":true},"policy_count":{"type":"integer"},"user_count":{"type":"integer"},"group_count":{"type":"integer"},"created_date":{"type":"string","format":"date-time"},"disabled_date":{"type":"string","format":"date-time","nullable":true},"created_by_email":{"type":"string","nullable":true},"disabled_by_email":{"type":"string","nullable":true},"policies":{"type":"array","items":{"type":"object","required":["id","permission","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"feature_id":{"type":"string","nullable":true},"feature_name":{"type":"string","nullable":true},"client_id":{"type":"string","nullable":true},"client_name":{"type":"string","nullable":true},"client_region_id":{"type":"string","nullable":true},"region_name":{"type":"string","nullable":true},"client_department_id":{"type":"string","nullable":true},"department_name":{"type":"string","nullable":true},"tag_id":{"type":"string","nullable":true},"tag_name":{"type":"string","nullable":true},"permission":{"type":"string","enum":["READ","WRITE","EXECUTE","DENY"]},"created_date":{"type":"string","format":"date-time"},"created_by_email":{"type":"string","nullable":true}}}}}},"RoleListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RoleListItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}},"CreateRoleBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string","nullable":true},"visibility_maison_only":{"type":"boolean"}}},"UpdateRoleBody":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"visibility_maison_only":{"type":"boolean"}}},"AddPolicyBody":{"type":"object","required":["permission"],"properties":{"feature_id":{"type":"string","nullable":true},"client_id":{"type":"string","nullable":true},"client_region_id":{"type":"string","nullable":true},"client_department_id":{"type":"string","nullable":true},"tag_id":{"type":"string","nullable":true},"permission":{"type":"string","enum":["READ","WRITE","EXECUTE","DENY"]}}},"RoleUsersResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","email","assigned_date"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"first_name":{"type":"string"},"last_name":{"type":"string"},"client_id":{"type":"string","nullable":true},"client_name":{"type":"string","nullable":true},"assigned_date":{"type":"string","format":"date-time"},"assigned_by_email":{"type":"string","nullable":true}}}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}},"RoleGroupsResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","name","assigned_date"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"client_id":{"type":"string","nullable":true},"client_name":{"type":"string","nullable":true},"assigned_date":{"type":"string","format":"date-time"},"assigned_by_email":{"type":"string","nullable":true}}}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}},"UserGroupMemberItem":{"type":"object","required":["user_id","email","first_name","last_name","status","joined_date"],"properties":{"user_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"first_name":{"type":"string"},"last_name":{"type":"string"},"status":{"type":"string","enum":["active","disabled","invited","expired"]},"joined_date":{"type":"string","format":"date-time"},"added_by_email":{"type":"string","nullable":true}}},"UserGroupRoleItem":{"type":"object","required":["role_id","role_name","assigned_date"],"properties":{"role_id":{"type":"string","format":"uuid"},"role_name":{"type":"string"},"client_id":{"type":"string","format":"uuid","nullable":true},"client_name":{"type":"string","nullable":true},"assigned_date":{"type":"string","format":"date-time"},"assigned_by_email":{"type":"string","nullable":true}}},"UserGroupListItem":{"type":"object","required":["id","name","member_count","role_count","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"client_id":{"type":"string","format":"uuid","nullable":true},"client_name":{"type":"string","nullable":true},"member_count":{"type":"integer"},"role_count":{"type":"integer"},"created_date":{"type":"string","format":"date-time"}}},"UserGroupDetail":{"type":"object","required":["id","name","created_date","members","roles","member_count","role_count"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"client_id":{"type":"string","format":"uuid","nullable":true},"client_name":{"type":"string","nullable":true},"created_date":{"type":"string","format":"date-time"},"created_by_email":{"type":"string","nullable":true},"deleted_date":{"type":"string","format":"date-time","nullable":true},"members":{"type":"array","items":{"$ref":"#/components/schemas/UserGroupMemberItem"}},"roles":{"type":"array","items":{"$ref":"#/components/schemas/UserGroupRoleItem"}},"member_count":{"type":"integer"},"role_count":{"type":"integer"}}},"UserGroupListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/UserGroupListItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}},"CreateUserGroupBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string","format":"uuid"}}},"UpdateUserGroupBody":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}},"UpdateGroupMembersBody":{"type":"object","properties":{"add":{"type":"array","items":{"type":"string","format":"uuid"}},"remove":{"type":"array","items":{"type":"string","format":"uuid"}}}},"UpdateGroupRolesBody":{"type":"object","properties":{"add":{"type":"array","items":{"type":"object","required":["role_id"],"properties":{"role_id":{"type":"string","format":"uuid"},"client_id":{"type":"string","format":"uuid"}}}},"remove":{"type":"array","items":{"type":"object","required":["role_id"],"properties":{"role_id":{"type":"string","format":"uuid"},"client_id":{"type":"string","format":"uuid"}}}}}},"UpdateGroupMembersResponse":{"type":"object","required":["added","removed"],"properties":{"added":{"type":"integer"},"removed":{"type":"integer"}}},"UpdateGroupRolesResponse":{"type":"object","required":["added","removed"],"properties":{"added":{"type":"integer"},"removed":{"type":"integer"}}},"FeatureItem":{"type":"object","required":["id","name","description","deprecated"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"deprecated":{"type":"boolean"}}},"FeatureListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/FeatureItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}}},"CreateFeatureBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"}}},"UpdateFeatureBody":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}},"RegionListItem":{"type":"object","required":["id","name","description","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"created_date":{"type":"string","format":"date-time"}}},"RegionListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RegionListItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":["string","null"]},"hasMore":{"type":"boolean"}}},"CreateRegionBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000}}},"UpdateRegionBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000}}},"DepartmentListItem":{"type":"object","required":["id","name","description","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"created_date":{"type":"string","format":"date-time"}}},"DepartmentListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentListItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":["string","null"]},"hasMore":{"type":"boolean"}}},"CreateDepartmentBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000}}},"UpdateDepartmentBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000}}},"TagListItem":{"type":"object","required":["id","name","description","created_date"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"created_date":{"type":"string","format":"date-time"}}},"TagListResponse":{"type":"object","required":["items","total","limit","nextCursor","hasMore"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/TagListItem"}},"total":{"type":"integer"},"limit":{"type":"integer"},"nextCursor":{"type":["string","null"]},"hasMore":{"type":"boolean"}}},"CreateTagBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000}}},"UpdateTagBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}},"AgentCard":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"url":{"type":"string"},"version":{"type":"string"},"capabilities":{"type":"object","properties":{"streaming":{"type":"boolean"},"pushNotifications":{"type":"boolean"}}},"authentication":{"type":"object","properties":{"schemes":{"type":"array","items":{"type":"string"}},"credentials":{"type":"string"}}},"defaultInputModes":{"type":"array","items":{"type":"string"}},"defaultOutputModes":{"type":"array","items":{"type":"string"}},"skills":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}}}}}},"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"HMAC-signed API key"},"sessionAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Maison session token (cookie or Authorization: Bearer header)"}}}}