{"openapi":"3.1.0","info":{"title":"SOP Engine API","description":"SOP Engine provides reliable, testable building blocks for agents and workflows:\n- Projects, files, chats, and hybrid search over your data\n- SOPs for versioned, auditable workflows\n- Agents that compile to SOPs with scheduling and run tracking\n- Background jobs, LLM routing, tools, and Assist/Structured Blocks APIs\n\nSee the individual tag groups below for detailed endpoint descriptions.","version":"1.12.2"},"paths":{"/api/v2/health":{"get":{"tags":["health"],"summary":"Health Check","description":"Check API health status and database connectivity.\n\nUnauthenticated endpoint for monitoring service health. Tests database\nconnection and returns overall status.\n\n**Prerequisites:**\n- None (public endpoint, no authentication required)\n\n**Query Parameters:**\n- None\n\n**Example Request:**\n```bash\ncurl -X GET https://api.taiso.ai/health\n```\n\n**Example Response (Healthy):**\n```json\n{\n  \"status\": \"ok\",\n  \"version\": \"1.0.0\",\n  \"database\": \"ok\"\n}\n```\n\n**Example Response (Degraded):**\n```json\n{\n  \"status\": \"degraded\",\n  \"version\": \"1.0.0\",\n  \"database\": \"error: connection timeout\"\n}\n```\n\n**Status Values:**\n- `ok`: All systems operational\n- `degraded`: Service running but some components unavailable\n\n**Related Endpoints:**\n- `GET /version` - Get API version only\n- `GET /whoami` - Test authentication (requires API key)","operationId":"health_check_api_v2_health_get","responses":{"200":{"description":"Service is healthy or degraded","content":{"application/json":{"schema":{}}}}}}},"/api/v2/version":{"get":{"tags":["health"],"summary":"Get Version","description":"Get API version information.\n\nUnauthenticated endpoint that returns the current API version.\nUseful for version checking and compatibility verification.\n\n**Prerequisites:**\n- None (public endpoint, no authentication required)\n\n**Query Parameters:**\n- None\n\n**Example Request:**\n```bash\ncurl -X GET https://api.taiso.ai/version\n```\n\n**Example Response:**\n```json\n{\n  \"version\": \"1.0.0\"\n}\n```\n\n**Related Endpoints:**\n- `GET /health` - Check service health and database status\n- `GET /whoami` - Test authentication","operationId":"get_version_api_v2_version_get","responses":{"200":{"description":"API version information","content":{"application/json":{"schema":{}}}}}}},"/api/v2/whoami":{"get":{"tags":["health"],"summary":"Whoami","description":"Test authentication and get current user information.\n\nReturns information about the authenticated user and their API key.\nUseful for testing authentication and verifying API key scopes.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Query Parameters:**\n- None\n\n**Example Request:**\n```bash\ncurl -X GET https://api.taiso.ai/whoami       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"user_id\": \"usr-1234567890abcdef\",\n  \"api_key_id\": \"key-abcdef1234567890\",\n  \"is_admin\": false,\n  \"scopes\": [\"projects:*\", \"llm:call\", \"blocks:*\"],\n  \"message\": \"Authentication successful!\"\n}\n```\n\n**Response Fields:**\n- `user_id`: Unique user identifier\n- `api_key_id`: Unique API key identifier\n- `is_admin`: Whether user has admin privileges\n- `scopes`: List of permissions granted to this API key\n- `message`: Confirmation message\n\n**Error Responses:**\n- `401 Unauthorized`: Invalid API key or missing Authorization header\n\n**Related Endpoints:**\n- `GET /health` - Check service health (no auth required)\n- `GET /version` - Get API version (no auth required)","operationId":"whoami_api_v2_whoami_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Authentication successful - returns user info","content":{"application/json":{"schema":{}}}},"401":{"description":"Invalid or missing API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/users/me":{"get":{"tags":["users","users"],"summary":"Get My Profile","description":"Get my user profile.\n\nReturns the authenticated user's profile information including email, name,\nadmin status, and account status.\n\n**Prerequisites:**\n- Valid API key required\n\n**Path Parameters:**\nNone\n\n**Example Request:**\n```bash\nGET /api/v2/users/me\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"user_id\": \"usr_9z8y7x6w5v\",\n    \"email\": \"user@example.com\",\n    \"name\": \"John Doe\",\n    \"phone_number\": \"+15551234567\",\n    \"business_id\": \"6b1e...\",\n    \"is_admin\": false,\n    \"status\": \"active\",\n    \"created_at\": \"2026-01-15T08:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: User not found (rare, indicates database inconsistency)\n\n**Related Endpoints:**\n- PATCH /users/me - Update my profile\n- GET /users/me/api-keys - List my API keys\n- POST /users/me/api-keys - Create new API key","operationId":"get_my_profile_api_v2_users_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"User not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["users","users"],"summary":"Update My Profile","description":"Update my profile.\n\nUpdates the authenticated user's name and/or email address. At least one field\nmust be provided. Email addresses must be unique across all users.\n\n**Prerequisites:**\n- Valid API key required\n- At least one field (name or email) must be provided\n\n**Path Parameters:**\nNone\n\n**Request Body:**\n- `name` (optional): New user name\n- `email` (optional): New email address (must be unique and valid format)\n\n**Example Request:**\n```json\n{\n    \"name\": \"Jane Smith\",\n    \"email\": \"jane.smith@example.com\"\n}\n```\n\n**Example Response (200):**\n```json\n{\n    \"user_id\": \"usr_9z8y7x6w5v\",\n    \"email\": \"jane.smith@example.com\",\n    \"name\": \"Jane Smith\",\n    \"is_admin\": false,\n    \"status\": \"active\",\n    \"created_at\": \"2026-01-15T08:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 400: No fields provided or email already in use by another user\n- 401: Missing or invalid API key\n- 404: User not found\n- 422: Invalid email format\n\n**Related Endpoints:**\n- GET /users/me - Get my profile\n- GET /users/me/api-keys - List my API keys","operationId":"update_my_profile_api_v2_users_me_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"400":{"description":"No fields to update or email already in use","content":{"application/json":{"examples":{"no_fields":{"value":{"detail":"Must provide name or email to update"}},"email_taken":{"value":{"detail":"Email already in use"}}}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"User not found"},"422":{"description":"Validation error - invalid email format"}}}},"/api/v2/users/me/api-keys":{"get":{"tags":["users","users"],"summary":"List My Api Keys","description":"List my API keys with pagination.\n\nReturns all API keys belonging to the authenticated user. For security, the\nfull key value is never returned (only the suffix). Revoked keys are excluded\nfrom results.\n\n**Prerequisites:**\n- Valid API key required\n\n**Query Parameters:**\n- `offset` (optional): Number of records to skip (default: 0)\n- `limit` (optional): Maximum records to return (default: 50, max: 100)\n\n**Example Request:**\n```bash\nGET /api/v2/users/me/api-keys?limit=10&offset=0\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"api_keys\": [\n        {\n            \"key_id\": \"key_7h8i9j0k1l\",\n            \"name\": \"Production API Key\",\n            \"key_suffix\": \"...xyz789\",\n            \"scopes\": [\"projects:*\", \"llm:call\", \"blocks:*\"],\n            \"status\": \"active\",\n            \"total_requests\": 1523,\n            \"total_tokens_used\": 45678,\n            \"total_cost_cents\": 234,\n            \"created_at\": \"2026-01-20T10:00:00Z\",\n            \"last_used_at\": \"2026-01-29T16:45:00Z\"\n        }\n    ],\n    \"pagination\": {\n        \"total\": 1,\n        \"offset\": 0,\n        \"limit\": 50,\n        \"has_more\": false\n    }\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n\n**Related Endpoints:**\n- POST /users/me/api-keys - Create new API key\n- DELETE /users/me/api-keys/{key_id} - Revoke API key\n- GET /users/me - Get my profile","operationId":"list_my_api_keys_api_v2_users_me_api_keys_get","parameters":[{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["users","users"],"summary":"Create My Api Key","description":"Create a new API key for myself.\n\nGenerates a new API key for the authenticated user. The full key is returned\nonly once - it cannot be retrieved later. Save it securely immediately.\n\n**Prerequisites:**\n- Valid API key required\n\n**Request Body:**\n- `name` (required): Descriptive name for the key (e.g., \"Production API\")\n- `scopes` (optional): Permission scopes (default: [\"projects:*\", \"llm:call\", \"blocks:*\"])\n- `max_cost_cents_per_month` (optional): Monthly spending limit in cents\n\n**Example Request:**\n```json\n{\n    \"name\": \"Production API Key\",\n    \"scopes\": [\"projects:*\", \"llm:call\", \"blocks:*\"],\n    \"max_cost_cents_per_month\": 10000\n}\n```\n\n**Example Response (201):**\n```json\n{\n    \"api_key\": \"sop_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z\",\n    \"key_id\": \"key_7h8i9j0k1l\",\n    \"key_suffix\": \"...y6z\",\n    \"warning\": \"Save this key now. It won't be shown again.\"\n}\n```\n\n**IMPORTANT:** The full `api_key` value is shown only once. Store it securely\nimmediately - you cannot retrieve it later.\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 422: Validation error - invalid scopes or missing name\n\n**Related Endpoints:**\n- GET /users/me/api-keys - List my API keys\n- DELETE /users/me/api-keys/{key_id} - Revoke API key","operationId":"create_my_api_key_api_v2_users_me_api_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAPIKeyRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAPIKeyResponse"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation error - invalid parameters"}}}},"/api/v2/users/me/api-keys/{key_id}":{"delete":{"tags":["users","users"],"summary":"Revoke My Api Key","description":"Revoke one of my API keys.\n\nPermanently revokes an API key, making it unusable for future requests.\nYou can only revoke your own API keys. This action cannot be undone.\n\n**Prerequisites:**\n- Valid API key required\n- Key must belong to the authenticated user\n\n**Path Parameters:**\n- `key_id` (required): API key ID to revoke\n\n**Example Request:**\n```bash\nDELETE /api/v2/users/me/api-keys/key_7h8i9j0k1l\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"success\": true,\n    \"message\": \"API key revoked\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: Attempting to revoke another user's key\n- 404: API key not found\n\n**Note:** You cannot revoke the API key you're currently using. Create a new\nkey first, then use it to revoke the old one.\n\n**Related Endpoints:**\n- GET /users/me/api-keys - List my API keys\n- POST /users/me/api-keys - Create new API key","operationId":"revoke_my_api_key_api_v2_users_me_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Cannot revoke another user's API key"},"404":{"description":"API key not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/llm/models":{"get":{"tags":["llm","llm"],"summary":"List Models","description":"List available LLM models from all configured providers.\n\nReturns a comprehensive catalog of available models from direct providers\n(OpenAI, Anthropic, Groq, Mistral) and OpenRouter (100+ models).\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Query Parameters:**\n- None\n\n**Example Request:**\n```bash\ncurl -X GET https://api.taiso.ai/api/v2/llm/models       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"providers\": [\"openai\", \"anthropic\", \"mistral\"],\n  \"models\": {\n    \"openai\": {\n      \"provider\": \"openai\",\n      \"access\": \"direct\",\n      \"models\": [\n        {\n          \"id\": \"openai/gpt-5.2\",\n          \"name\": \"GPT-5.2\",\n          \"context\": \"400k\",\n          \"description\": \"Flagship OpenAI model, 400k context\"\n        }\n      ]\n    }\n  },\n  \"routing_strategy\": \"smart\",\n  \"total_models\": 42,\n  \"info\": {\n    \"direct_providers\": [\"openai\"],\n    \"openrouter_providers\": [\"google\", \"meta\"],\n    \"has_openrouter\": true\n  }\n}\n```\n\n**Error Responses:**\n- `503 Service Unavailable`: No LLM providers configured (missing API keys)\n\n**Related Endpoints:**\n- `POST /api/v2/llm/chat` - Make LLM call with selected model\n- `POST /api/v2/llm/simple-json` - Extract JSON using selected model","operationId":"list_models_api_v2_llm_models_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of available models grouped by provider","content":{"application/json":{"schema":{}}}},"503":{"description":"No LLM providers configured"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/llm/pricing":{"get":{"tags":["llm","llm"],"summary":"Get Llm Pricing","description":"Read the daily snapshot of provider model catalogues and prices.\n\nBacked by `llm_provider_daily_pricing`, refreshed nightly at 01:00 UTC.\nOnly OpenRouter and Groq publish prices; rows from OpenAI, Anthropic,\nMistral and Ollama are catalogue-only, carrying `price_status=\"unavailable\"`\nand null prices.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Query Parameters:**\n- `date`: Snapshot day as `YYYY-MM-DD`. Defaults to the most recent day.\n- `provider`: Restrict to `openrouter`, `openai`, `anthropic`, `mistral`,\n  `groq` or `ollama`.\n- `model_id`: Restrict to a single model, as the provider publishes it.\n- `limit`: Page size, 1-1000 (default 100).\n- `offset`: Page offset (default 0).\n\n**Example Request:**\n```bash\ncurl -X GET \"https://api.taiso.ai/api/v2/llm/pricing?provider=openrouter&limit=2\"       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"snapshot_date\": \"2026-08-13\",\n  \"count\": 2,\n  \"items\": [\n    {\n      \"provider\": \"openrouter\",\n      \"model_id\": \"openai/gpt-5.2\",\n      \"price_status\": \"published\",\n      \"prompt_usd_per_token\": \"0.000000500000000000000000\",\n      \"completion_usd_per_token\": \"0.000002500000000000000000\"\n    }\n  ]\n}\n```\n\nArgs:\n    date: Optional snapshot day (`YYYY-MM-DD`).\n    provider: Optional provider filter.\n    model_id: Optional model filter.\n    limit: Page size.\n    offset: Page offset.\n    auth: Authenticated caller, injected.\n\nReturns:\n    A dict with `snapshot_date`, `count` and `items`. An empty `items` list\n    with HTTP 200 when no snapshot exists yet — not a 404.\n\nRaises:\n    HTTPException: 400 if `date` is malformed or pagination is out of range.\n\nSide Effects:\n    Reads from the database.","operationId":"get_llm_pricing_api_v2_llm_pricing_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"}},{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"}},{"name":"model_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Daily model pricing snapshot","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid date or pagination parameter"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/llm/pricing/status":{"get":{"tags":["llm","llm"],"summary":"Get Llm Pricing Status","description":"Report how fresh each provider's pricing snapshot is.\n\nDerived entirely from `llm_provider_daily_pricing`, so there is no separate\nrun-status state that could drift from reality. Use this to detect a\nsilently failing nightly ingest.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Query Parameters:**\n- None\n\n**Example Request:**\n```bash\ncurl -X GET https://api.taiso.ai/api/v2/llm/pricing/status       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"any_data\": true,\n  \"stale\": false,\n  \"overdue\": false,\n  \"providers\": [\n    {\n      \"provider\": \"openrouter\",\n      \"latest_snapshot\": \"2026-08-13\",\n      \"rows_today\": 411,\n      \"priced_today\": 406,\n      \"stale\": false\n    }\n  ]\n}\n```\n\nArgs:\n    auth: Authenticated caller, injected.\n\nReturns:\n    A dict with a per-provider breakdown plus `any_data` and an aggregate\n    `stale` flag. Returns 200 with an empty list before the first ingest.\n\nSide Effects:\n    Reads from the database.","operationId":"get_llm_pricing_status_api_v2_llm_pricing_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Per-provider ingest freshness","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/llm/chat":{"post":{"tags":["llm","llm"],"summary":"Llm Chat","description":"Send messages to an LLM with smart routing and automatic fallback.\n\nThis endpoint provides intelligent routing across multiple LLM providers with\nautomatic fallback, comprehensive usage tracking, and function calling support.\n\n**Prerequisites:**\n- Valid API key with `llm:call` scope\n- At least one LLM provider configured (OpenAI, Anthropic, Groq, Mistral, or OpenRouter)\n\n**Request Body:**\n- `messages` (required): Array of message objects with `role` and `content`\n- `model` (optional): Model ID (defaults to configured LLM_DEFAULT_MODEL)\n- `temperature` (optional): Sampling temperature 0.0-2.0 (default: 0.7)\n- `max_tokens` (optional): Maximum tokens to generate (default: 2000)\n- `tools` (optional): Array of tool/function definitions (OpenAI format)\n- `tool_choice` (optional): Control function invocation (\"auto\", \"required\", or specific function)\n- `project_id` (optional): Associate call with a project for tracking\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/llm/chat       -H \"Authorization: Bearer YOUR_API_KEY\"       -H \"Content-Type: application/json\"       -d '{\n    \"messages\": [\n      {\"role\": \"user\", \"content\": \"Explain quantum computing in simple terms\"}\n    ],\n    \"model\": \"openai/gpt-5.2\",\n    \"temperature\": 0.7,\n    \"max_tokens\": 500\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"message\": {\n    \"role\": \"assistant\",\n    \"content\": \"Quantum computing uses quantum mechanics principles...\"\n  },\n  \"usage\": {\n    \"prompt_tokens\": 15,\n    \"completion_tokens\": 87,\n    \"total_tokens\": 102\n  },\n  \"cost_cents\": 0.0051,\n  \"cost_usd\": 0.000051,\n  \"model\": \"openai/gpt-5.2\",\n  \"provider\": \"openai\",\n  \"provider_type\": \"direct\",\n  \"fallback_used\": false,\n  \"duration_ms\": 1247\n}\n```\n\n**Routing Behavior:**\n- Direct provider used if API key configured (faster, cheaper)\n- Automatic fallback to OpenRouter if direct provider unavailable or fails\n- Transparent routing - never shows \"API key not configured\" errors\n\n**Function Calling Example:**\n```json\n{\n  \"messages\": [{\"role\": \"user\", \"content\": \"What's the weather in SF?\"}],\n  \"model\": \"openai/gpt-5.2\",\n  \"tools\": [{\n    \"type\": \"function\",\n    \"function\": {\n      \"name\": \"get_weather\",\n      \"description\": \"Get current weather\",\n      \"parameters\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"location\": {\"type\": \"string\"}\n        }\n      }\n    }\n  }],\n  \"tool_choice\": \"auto\"\n}\n```\n\n**Error Responses:**\n- `400 Bad Request`: Invalid parameters or empty messages array\n- `500 Internal Server Error`: Model not supported or provider error\n- `503 Service Unavailable`: No LLM providers configured\n\n**Related Endpoints:**\n- `GET /api/v2/llm/models` - List available models\n- `POST /api/v2/llm/simple-json` - Simplified JSON extraction","operationId":"llm_chat_api_v2_llm_chat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/src__api__v2__llm__ChatRequest"}}}},"responses":{"200":{"description":"LLM response with usage and cost tracking","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request parameters or provider rejected request"},"404":{"description":"Model not found by provider"},"429":{"description":"LLM provider rate limit reached"},"502":{"description":"LLM provider unavailable or authentication error"},"503":{"description":"No LLM providers configured"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/llm/simple-json":{"post":{"tags":["llm","llm"],"summary":"Simple Json Extraction","description":"Extract structured JSON data from text using a natural language prompt.\n\nA simplified alternative to structured blocks for quick JSON extraction tasks.\nAutomatically infers schema from your prompt and returns valid JSON.\n\n**Prerequisites:**\n- Valid API key with `llm:call` scope\n- At least one LLM provider configured\n\n**Request Body:**\n- `prompt` (required): Natural language description of what to extract\n- `input_data` (required): The text to process and extract from\n- `model` (optional): Model ID (defaults to configured LLM_DEFAULT_MODEL)\n- `temperature` (optional): Sampling temperature (default: 0.0 for deterministic)\n- `max_tokens` (optional): Maximum tokens to generate (default: 1000)\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/llm/simple-json       -H \"Authorization: Bearer YOUR_API_KEY\"       -H \"Content-Type: application/json\"       -d '{\n    \"prompt\": \"Extract the patient name, age, and primary diagnosis\",\n    \"input_data\": \"Patient: Sarah Johnson, a 34-year-old female, presents with confirmed diagnosis of Type 2 Diabetes Mellitus.\",\n    \"model\": \"openai/gpt-5.2\",\n    \"temperature\": 0.0\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"data\": {\n    \"patient_name\": \"Sarah Johnson\",\n    \"age\": 34,\n    \"primary_diagnosis\": \"Type 2 Diabetes Mellitus\"\n  },\n  \"raw_response\": \"{\"patient_name\": \"Sarah Johnson\", \"age\": 34, ...}\",\n  \"usage\": {\n    \"prompt_tokens\": 67,\n    \"completion_tokens\": 23,\n    \"total_tokens\": 90\n  },\n  \"cost_cents\": 0.0045,\n  \"cost_usd\": 0.000045,\n  \"model\": \"openai/gpt-5.2\",\n  \"provider\": \"openai\",\n  \"provider_type\": \"direct\",\n  \"fallback_used\": false,\n  \"duration_ms\": 892\n}\n```\n\n**Common Use Cases:**\n- Resume parsing (extract name, skills, education)\n- Product catalog extraction (name, price, specs)\n- Document metadata extraction\n- Contact information parsing\n- Quick prototyping before building full SOPs\n\n**Error Responses:**\n- `400 Bad Request`: Invalid model or provider rejected request\n- `422 Unprocessable Entity`: LLM returned invalid JSON (includes raw response for debugging)\n- `500 Internal Server Error`: Model not supported or extraction failed\n- `503 Service Unavailable`: No LLM providers configured\n\n**Related Endpoints:**\n- `POST /api/v2/llm/chat` - Full conversational LLM interface\n- `POST /api/v2/structured-blocks/generate` - Complex extraction with schema validation","operationId":"simple_json_extraction_api_v2_llm_simple_json_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleJsonRequest"}}}},"responses":{"200":{"description":"Extracted JSON data with usage tracking","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request or provider rejected request"},"404":{"description":"Model not found by provider"},"422":{"description":"JSON parsing failed"},"429":{"description":"LLM provider rate limit reached"},"502":{"description":"LLM provider unavailable or authentication error"},"503":{"description":"No LLM providers configured"}}}},"/api/v2/llm/completions":{"post":{"tags":["llm","llm"],"summary":"Completions","description":"Chat completion using the `messages` / `choices` request and response shape.\n\nSame models, routing and pricing as `POST /api/v2/llm/chat` — this endpoint\nexists for code already written against that request/response shape, so you\nchange a URL instead of rewriting how you build requests and read responses.\n\nOptionally verifies the answer against documents you supply (see `lodestar`\nbelow). Verification is off unless you ask for it.\n\n**Prerequisites:**\n- Valid API key\n\n**Request fields:**\n- `model` (string) — model id; defaults to the configured default model\n- `messages` (array, required) — `{\"role\": ..., \"content\": ...}` objects\n- `temperature`, `max_tokens`, `tools`, `tool_choice`, `response_format` — optional, passed through\n- `stream` (bool) — SSE streaming. Cannot be combined with verification (see below)\n- `lodestar` (object) — optional answer verification, described below\n\nUnrecognised fields are ignored rather than rejected, so request bodies\nwritten for other services will not be refused for containing extras.\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/llm/completions \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"openai/gpt-5.2\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"How long do refunds take?\"}]\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"id\": \"chatcmpl-...\",\n  \"object\": \"chat.completion\",\n  \"model\": \"openai/gpt-5.2\",\n  \"choices\": [\n    {\"index\": 0, \"message\": {\"role\": \"assistant\", \"content\": \"...\"}, \"finish_reason\": \"stop\"}\n  ],\n  \"usage\": {\"prompt_tokens\": 12, \"completion_tokens\": 40, \"total_tokens\": 52}\n}\n```\n\n**Optional: verifying the answer**\n\nAdd a `lodestar` block to have Taiso check the answer against documents you\nprovide, and return a per-claim verdict alongside it:\n\n```json\n{\n  \"model\": \"openai/gpt-5.2\",\n  \"messages\": [{\"role\": \"user\", \"content\": \"How long do refunds take?\"}],\n  \"lodestar\": {\n    \"enhance\": \"verify\",\n    \"sources\": [{\"id\": \"policy\", \"text\": \"Refunds are issued within 30 days.\"}]\n  }\n}\n```\n\n`enhance` selects how much work to do:\n\n- `off` — no verification (same as omitting the block)\n- `floor` — return the answer as-is, no extra model calls\n- `verify` — check the answer's claims against `sources`\n- `full` — as `verify`, plus one attempt to correct claims the sources contradict\n\nRequests that include a `lodestar` block get one back:\n\n```json\n\"lodestar\": {\n  \"verdict\": \"passed\",\n  \"shipped_from\": \"floor\",\n  \"claims\": [\n    {\"text\": \"Refunds take 30 days\", \"status\": \"supported\", \"source_ids\": [\"policy\"]}\n  ],\n  \"usage_delta\": {\"extra_prompt_tokens\": 412, \"extra_completion_tokens\": 88, \"multiplier\": 2.1}\n}\n```\n\nEach claim is `supported`, `disputed`, `unclear` or `not_found` against your\nsources. `verify` and `full` require `sources` — without documents to check\nagainst there is nothing to verify, and the request returns the unverified\nanswer with `verdict: \"enhancement_error\"`.\n\n**Cost:** verification issues extra model calls, typically 3.7x-9.9x the\ntokens of a plain call. `usage_delta` reports exactly what it added on every\nrequest, and top-level `usage` is the total including it.\n\n**Verification never breaks your request.** If a check cannot run, the\nunverified answer is still returned with a `verdict` explaining why.\n\n**Error Responses:**\n- `400 Bad Request`: malformed body, or `stream: true` combined with\n  `enhance` other than `off` (verified output cannot be streamed)\n- `401 Unauthorized`: missing or invalid API key\n- `429 Too Many Requests`: rate limited, or the key's spend cap is exhausted\n- `502 Bad Gateway`: the upstream model provider failed\n\n**Related Endpoints:**\n- `POST /api/v2/llm/chat` — same capability, Taiso-native request shape\n- `POST /api/v2/llm/simple-json` — prompt in, structured JSON out\n- `GET /api/v2/llm/models` — list available models","operationId":"completions_api_v2_llm_completions_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Chat completion, optionally with a `lodestar` audit block","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request or unsupported combination"},"401":{"description":"API key required or invalid"},"429":{"description":"Rate limited or key budget exhausted"},"502":{"description":"Upstream provider unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/calc":{"post":{"tags":["tools","tools"],"summary":"Calculator","description":"Safe mathematical calculator (TaisoCalc).\n\nEvaluates mathematical expressions safely using AST parsing with no code execution.\nPerfect for LLMs that need to perform calculations without security risks.\n\n**What It Does:**\n- Evaluates arithmetic expressions (no variables, functions, or imports)\n- Returns floating-point results for all calculations\n- Parses expressions using Python AST for safety\n- Blocks all potentially dangerous operations\n\n**When to Use:**\n- LLM-powered calculators or chat applications\n- Safe evaluation of user-provided math expressions\n- Building tools that require arithmetic without exec()\n- Replacing eval() with a secure alternative\n\n**Supported Operations:**\n- Addition: `+`\n- Subtraction: `-`\n- Multiplication: `*`\n- Division: `/`\n- Exponentiation: `**`\n- Modulo: `%`\n- Floor division: `//`\n- Parentheses: `( )`\n- Unary operators: `-5`, `+3`\n\n**Blocked Operations (Security):**\n- Variables: `x`, `y`\n- Functions: `sqrt()`, `pow()`\n- Imports: `import math`\n- Attribute access: `__class__`\n- Code execution: `exec()`, `eval()`\n\n**Request Body:**\n- `expression` (string, required): Math expression to evaluate\n\n**Example Request:**\n```json\n{\n  \"expression\": \"2 + 2\"\n}\n```\n\n**Example Response:**\n```json\n{\n  \"expression\": \"2 + 2\",\n  \"result\": 4.0,\n  \"message\": \"Calculation successful\"\n}\n```\n\n**More Examples:**\n```json\n// Complex expression with parentheses\n{\"expression\": \"((10 + 5) * 3) / 2\"}\n// Returns: {\"result\": 22.5}\n\n// Exponentiation\n{\"expression\": \"2 ** 8\"}\n// Returns: {\"result\": 256.0}\n\n// Floor division and modulo\n{\"expression\": \"17 // 5\"}\n// Returns: {\"result\": 3.0}\n\n{\"expression\": \"17 % 5\"}\n// Returns: {\"result\": 2.0}\n\n// Negative numbers\n{\"expression\": \"-5 + 10\"}\n// Returns: {\"result\": 5.0}\n\n// Order of operations\n{\"expression\": \"2 + 3 * 4\"}\n// Returns: {\"result\": 14.0}\n```\n\n**Error Responses:**\n- 400: Invalid syntax (e.g., \"2 +\", \"x + 5\")\n- 400: Unsupported operation (e.g., \"sqrt(25)\")\n- 400: Division by zero (e.g., \"5 / 0\")\n- 401: Missing or invalid API key\n- 500: Unexpected server error\n\n**Related Endpoints:**\n- `GET /tools/calc/test` - Test calculator with predefined expressions\n- `POST /tools/expression` - Evaluate boolean expressions with variables","operationId":"calculator_api_v2_tools_calc_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalcRequest"}}}},"responses":{"200":{"description":"Calculation completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalcResponse"}}}},"400":{"description":"Invalid expression syntax or unsupported operation"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Unexpected calculation error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/calc/test":{"get":{"tags":["tools","tools"],"summary":"Calculator Test","description":"Test the calculator with example expressions.","operationId":"calculator_test_api_v2_tools_calc_test_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/expression":{"post":{"tags":["tools","tools"],"summary":"Evaluate Expression Tool","description":"Safe boolean expression evaluator with variables.\n\nEvaluates boolean expressions with user-provided variables safely using a custom parser.\nPerfect for conditional logic, routing decisions, and dynamic rule evaluation.\n\n**What It Does:**\n- Evaluates boolean expressions with comparisons and logic operators\n- Supports variables passed at runtime (no hardcoded values)\n- Returns boolean result and tracks which variables were used\n- Parses expressions with custom grammar (no eval/exec)\n\n**When to Use:**\n- Testing expressions before using in decision tool or SOPs\n- Dynamic routing based on runtime conditions\n- Rule engines and conditional workflows\n- Implementing switch/case-like logic with LLMs\n- Building calculators or scoring systems\n\n**Supported Operators:**\n- **Comparisons**: `==`, `!=`, `<`, `<=`, `>`, `>=`\n- **Boolean Logic**: `and`, `or`, `not`\n- **Types**: numbers, strings (quoted), booleans (true/false), null\n- **Variables**: Any valid identifier (letters, numbers, underscore)\n\n**Grammar:**\n```\nexpr       → logic_or\nlogic_or   → logic_and (\"or\" logic_and)*\nlogic_and  → logic_not (\"and\" logic_not)*\nlogic_not  → \"not\" logic_not | comparison\ncomparison → term ((\"==\" | \"!=\" | \">\" | \">=\" | \"<\" | \"<=\") term)?\nterm       → NUMBER | STRING | BOOLEAN | NAME\n```\n\n**Security:**\n- No function calls allowed\n- No attribute access (blocks `__class__` exploits)\n- No subscript/index access\n- No imports or code execution\n- Only safe comparisons and lookups\n\n**Request Body:**\n- `expression` (string, required): Boolean expression to evaluate (max 1000 chars)\n- `variables` (object, required): Variable values as key-value pairs\n\n**Example Request:**\n```json\n{\n  \"expression\": \"score >= 80 and score < 95\",\n  \"variables\": {\n    \"score\": 85\n  }\n}\n```\n\n**Example Response:**\n```json\n{\n  \"result\": true,\n  \"expression\": \"score >= 80 and score < 95\",\n  \"variables_used\": [\"score\"],\n  \"error\": null\n}\n```\n\n**More Examples:**\n```json\n// String comparison\n{\n  \"expression\": \"status == \"active\" and role != \"guest\"\",\n  \"variables\": {\"status\": \"active\", \"role\": \"admin\"}\n}\n// Returns: {\"result\": true}\n\n// Nested logic with not\n{\n  \"expression\": \"not is_expired and (tier == \"premium\" or credits > 0)\",\n  \"variables\": {\"is_expired\": false, \"tier\": \"free\", \"credits\": 10}\n}\n// Returns: {\"result\": true}\n\n// Numeric comparison\n{\n  \"expression\": \"age >= 18 and age <= 65\",\n  \"variables\": {\"age\": 25}\n}\n// Returns: {\"result\": true}\n\n// Complex conditional routing\n{\n  \"expression\": \"priority == \"high\" or (priority == \"medium\" and days_waiting > 3)\",\n  \"variables\": {\"priority\": \"medium\", \"days_waiting\": 5}\n}\n// Returns: {\"result\": true}\n\n// Boolean only\n{\n  \"expression\": \"is_authenticated and has_permission\",\n  \"variables\": {\"is_authenticated\": true, \"has_permission\": false}\n}\n// Returns: {\"result\": false}\n```\n\n**Error Responses:**\n- 400: Invalid expression syntax\n- 400: Undefined variable used in expression\n- 400: Type mismatch in comparison (e.g., comparing string to number)\n- 401: Missing or invalid API key\n- 500: Unexpected server error\n\n**Tips:**\n- Strings must be quoted with double quotes: `\"active\"`\n- Booleans are lowercase: `true`, `false`\n- Variables are case-sensitive: `Score` ≠ `score`\n- All variables must be provided (no defaults)\n\n**Related Endpoints:**\n- `GET /tools/expression/test` - Test with predefined expressions\n- `POST /tools/decision` - Make routing decisions with LLM + expressions\n- `POST /tools/calc` - Evaluate mathematical expressions (no variables)","operationId":"evaluate_expression_tool_api_v2_tools_expression_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpressionRequest"}}}},"responses":{"200":{"description":"Expression evaluated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpressionResponse"}}}},"400":{"description":"Invalid expression syntax or undefined variable"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Unexpected evaluation error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/expression/test":{"get":{"tags":["tools","tools"],"summary":"Expression Test","description":"Test the expression evaluator with example expressions.","operationId":"expression_test_api_v2_tools_expression_test_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/web-search":{"post":{"tags":["tools","tools"],"summary":"Perform web search via Tavily","description":"Web search via Tavily (LLM-optimized search engine).\n\nPerforms web search using Tavily API, which is specifically optimized for LLMs and RAG\napplications. Returns high-quality, factual results with AI-generated summaries and\ncontent snippets perfect for grounding LLM responses.\n\n**What It Does:**\n- Searches the web using Tavily's LLM-optimized search engine\n- Returns ranked results with titles, URLs, and content snippets\n- Optionally provides AI-generated answer summaries\n- Can include related images and full page content\n\n**When to Use:**\n- Grounding LLM responses with up-to-date information\n- RAG applications requiring web search\n- Fact-checking or verification workflows\n- Research assistants and question-answering systems\n- Content discovery and recommendation engines\n\n**Search Modes:**\n- **Basic**: Fast results (200-500ms), top sources, good for quick lookups\n- **Advanced**: Comprehensive search (1-2s), deeper crawling, better for research\n\n**Request Body:**\n- `query` (string, required): Search query (natural language or keywords)\n- `max_results` (int, optional): Number of results to return (default: 5, max: 20)\n- `search_depth` (string, optional): \"basic\" (default) or \"advanced\"\n- `include_answer` (bool, optional): Include AI-generated answer summary (default: true)\n- `include_raw_content` (bool, optional): Include full page HTML (default: false)\n- `include_images` (bool, optional): Include related images (default: false)\n\n**Example Request:**\n```json\n{\n  \"query\": \"latest developments in CRISPR gene editing 2025\",\n  \"max_results\": 5,\n  \"search_depth\": \"basic\",\n  \"include_answer\": true\n}\n```\n\n**Example Response:**\n```json\n{\n  \"query\": \"latest developments in CRISPR gene editing 2025\",\n  \"results\": [\n    {\n      \"title\": \"CRISPR Breakthrough: New Base Editing Technique\",\n      \"url\": \"https://nature.com/articles/crispr-2025\",\n      \"content\": \"Researchers have developed a new base editing technique that increases precision by 95%...\",\n      \"score\": 0.98,\n      \"published_date\": \"2025-01-20\"\n    },\n    {\n      \"title\": \"FDA Approves First CRISPR Gene Therapy\",\n      \"url\": \"https://fda.gov/news/crispr-approval\",\n      \"content\": \"The FDA has approved the first CRISPR-based gene therapy for sickle cell disease...\",\n      \"score\": 0.95,\n      \"published_date\": \"2025-01-15\"\n    }\n  ],\n  \"answer\": \"Recent CRISPR developments in 2025 include a new base editing technique with 95% precision and FDA approval of the first CRISPR gene therapy for sickle cell disease.\",\n  \"response_time\": 0.42\n}\n```\n\n**More Examples:**\n```json\n// Quick fact lookup with answer\n{\n  \"query\": \"what is the capital of France\",\n  \"max_results\": 3,\n  \"include_answer\": true\n}\n// Returns: {\"answer\": \"Paris\", \"results\": [...]}\n\n// Comprehensive research\n{\n  \"query\": \"Python async programming best practices\",\n  \"max_results\": 10,\n  \"search_depth\": \"advanced\",\n  \"include_raw_content\": true\n}\n// Returns: 10 detailed results with full page content\n\n// Image search for visual content\n{\n  \"query\": \"machine learning architecture diagrams\",\n  \"max_results\": 5,\n  \"include_images\": true\n}\n// Returns: results with related image URLs\n```\n\n**Result Fields:**\n- `title`: Page title\n- `url`: Page URL\n- `content`: Relevant content snippet (200-500 chars)\n- `score`: Relevance score 0-1 (higher = more relevant)\n- `published_date`: Publication date (if available)\n- `raw_content`: Full page HTML (if `include_raw_content=true`)\n\n**Error Responses:**\n- 400: Invalid query (empty, too long)\n- 401: Missing or invalid API key\n- 503: Tavily API unavailable or not configured\n- 504: Search timed out (query too complex)\n\n**Performance:**\n- Basic search: 200-500ms typical response time\n- Advanced search: 1-2s typical response time\n- Rate limits: Enforced by Tavily API key tier\n- Caching: Not implemented (fresh results each time)\n\n**Related Endpoints:**\n- `POST /tools/web-crawl-simple` - Fetch specific URL content (no search)\n- `POST /structured-blocks/generate` - Extract structured data from search results","operationId":"web_search_api_v2_tools_web_search_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TavilySearchRequest"}}}},"responses":{"200":{"description":"Search completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TavilySearchResponse"}}}},"400":{"description":"Invalid query or parameters"},"401":{"description":"Authentication required - missing or invalid API key"},"503":{"description":"Web search service unavailable or API key not configured"},"504":{"description":"Search request timed out"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/time":{"get":{"tags":["tools","tools"],"summary":"Get Time","description":"Get current time with comprehensive formatting options.\n\nReturns the current time in various formats and timezones. Perfect for LLMs that need\nto know what time it is, calculate deadlines, or timestamp events.\n\n**What It Returns:**\n- ISO 8601 timestamp (for APIs and databases)\n- Unix timestamp (seconds since epoch)\n- Individual components (year, month, day, hour, minute, second, weekday)\n- Multiple human-readable formats (date, datetime, RFC 2822, etc.)\n\n**When to Use:**\n- LLMs that need current time context\n- Timestamping events or logs\n- Calculating deadlines or durations\n- Building time-aware applications\n- Generating time-based reports\n\n**Query Parameters:**\n- `tz` (string, optional): IANA timezone name (default: \"UTC\")\n  - Examples: \"UTC\", \"America/New_York\", \"Europe/London\", \"Asia/Tokyo\"\n  - Full list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\n\n**Example Request:**\n```\nGET /api/v2/tools/time?tz=America/New_York\n```\n\n**Example Response:**\n```json\n{\n  \"timestamp_iso\": \"2025-01-29T14:30:45-05:00\",\n  \"timestamp_unix\": 1738181445,\n  \"timezone\": \"America/New_York\",\n  \"year\": 2025,\n  \"month\": 1,\n  \"day\": 29,\n  \"hour\": 14,\n  \"minute\": 30,\n  \"second\": 45,\n  \"weekday\": \"Wednesday\",\n  \"formatted\": {\n    \"date\": \"2025-01-29\",\n    \"time\": \"14:30:45\",\n    \"datetime\": \"2025-01-29 14:30:45\",\n    \"human\": \"Wednesday, January 29, 2025 at 02:30:45 PM\",\n    \"iso\": \"2025-01-29T14:30:45-05:00\",\n    \"rfc2822\": \"Wed, 29 Jan 2025 14:30:45 -0500\"\n  }\n}\n```\n\n**More Examples:**\n```\n// UTC time (default)\nGET /api/v2/tools/time\n// Returns: current UTC time\n\n// Tokyo time\nGET /api/v2/tools/time?tz=Asia/Tokyo\n// Returns: current time in JST timezone\n\n// London time\nGET /api/v2/tools/time?tz=Europe/London\n// Returns: current time in GMT/BST\n```\n\n**Response Fields:**\n- `timestamp_iso`: ISO 8601 format with timezone (e.g., \"2025-01-29T14:30:45-05:00\")\n- `timestamp_unix`: Seconds since Unix epoch (e.g., 1738181445)\n- `timezone`: IANA timezone name (e.g., \"America/New_York\")\n- `year`, `month`, `day`: Date components\n- `hour`, `minute`, `second`: Time components (24-hour format)\n- `weekday`: Day of week (e.g., \"Monday\", \"Tuesday\")\n- `formatted`: Object with multiple format options:\n  - `date`: YYYY-MM-DD format\n  - `time`: HH:MM:SS format (24-hour)\n  - `datetime`: YYYY-MM-DD HH:MM:SS format\n  - `human`: Human-readable format with 12-hour time\n  - `iso`: ISO 8601 with timezone\n  - `rfc2822`: Email header format\n\n**Error Responses:**\n- 400: Invalid timezone name (e.g., \"America/InvalidCity\")\n- 401: Missing or invalid API key\n- 500: Time retrieval error\n\n**Common Timezones:**\n- **Americas**: America/New_York, America/Chicago, America/Los_Angeles, America/Toronto\n- **Europe**: Europe/London, Europe/Paris, Europe/Berlin, Europe/Moscow\n- **Asia**: Asia/Tokyo, Asia/Shanghai, Asia/Dubai, Asia/Kolkata\n- **Oceania**: Australia/Sydney, Pacific/Auckland\n- **UTC**: UTC (Coordinated Universal Time)\n\n**Related Endpoints:**\n- `POST /tools/time-diff` - Calculate time difference between two timestamps","operationId":"get_time_api_v2_tools_time_get","parameters":[{"name":"tz","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"description":"Timezone (e.g., 'America/New_York', 'UTC', 'Asia/Tokyo')","title":"Tz"},"description":"Timezone (e.g., 'America/New_York', 'UTC', 'Asia/Tokyo')"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Current time retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeResponse"}}}},"400":{"description":"Invalid timezone name"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Time retrieval error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/time-diff":{"post":{"tags":["tools","tools"],"summary":"Calculate Time Diff","description":"Calculate time difference between two timestamps.\n\nComputes the duration between any two timestamps and formats it in various ways.\nPerfect for age calculations, SLA monitoring, deadline tracking, and duration reporting.\n\n**What It Calculates:**\n- Absolute difference (always positive, regardless of order)\n- Supports multiple timestamp formats (ISO 8601, Unix, RFC 2822, natural language)\n- Returns both raw seconds and human-readable formats\n\n**When to Use:**\n- Calculating ages or time since events\n- SLA compliance checking (time since ticket creation)\n- Deadline monitoring (time until/since deadline)\n- Duration reporting (meeting lengths, project durations)\n- Building timelines and activity logs\n\n**Request Body:**\n- `timestamp1` (string, required): First timestamp\n- `timestamp2` (string, required): Second timestamp\n- `format` (string, optional): Output format (default: \"seconds\")\n  - \"seconds\": Total seconds (e.g., \"86400.0 seconds\")\n  - \"minutes\": Total minutes (e.g., \"1440.00 minutes\")\n  - \"hours\": Total hours (e.g., \"24.00 hours\")\n  - \"days\": Total days (e.g., \"1.00 days\")\n  - \"human\": Human-readable (e.g., \"1 day, 5 hours, 30 minutes\")\n  - \"components\": Compact format (e.g., \"1d 5h 30m 45s\")\n\n**Supported Timestamp Formats:**\n- ISO 8601: \"2025-01-29T14:30:45Z\", \"2025-01-29T14:30:45-05:00\"\n- Unix timestamp (seconds): \"1738181445\"\n- Unix timestamp (milliseconds): \"1738181445000\"\n- RFC 2822: \"Wed, 29 Jan 2025 14:30:45 -0500\"\n- Natural language: \"January 29, 2025 2:30 PM\"\n\n**Example Request:**\n```json\n{\n  \"timestamp1\": \"2025-01-01T00:00:00Z\",\n  \"timestamp2\": \"2025-01-29T14:30:45Z\",\n  \"format\": \"human\"\n}\n```\n\n**Example Response:**\n```json\n{\n  \"timestamp1\": \"2025-01-01T00:00:00Z\",\n  \"timestamp2\": \"2025-01-29T14:30:45Z\",\n  \"difference_seconds\": 2466645.0,\n  \"difference_formatted\": \"28 days, 14 hours, 30 minutes, 45 seconds\",\n  \"format\": \"human\"\n}\n```\n\n**More Examples:**\n```json\n// Calculate hours between dates\n{\n  \"timestamp1\": \"2025-01-01T00:00:00Z\",\n  \"timestamp2\": \"2025-01-02T06:00:00Z\",\n  \"format\": \"hours\"\n}\n// Returns: {\"difference_seconds\": 108000, \"difference_formatted\": \"30.00 hours\"}\n\n// Calculate age in days\n{\n  \"timestamp1\": \"1990-05-15\",\n  \"timestamp2\": \"2025-01-29\",\n  \"format\": \"days\"\n}\n// Returns: {\"difference_seconds\": 1097654400, \"difference_formatted\": \"12700.00 days\"}\n\n// SLA compliance (time since ticket creation)\n{\n  \"timestamp1\": \"1738181445\",\n  \"timestamp2\": \"1738267845\",\n  \"format\": \"human\"\n}\n// Returns: {\"difference_formatted\": \"1 day\"}\n\n// Compact format for logs\n{\n  \"timestamp1\": \"2025-01-29T10:00:00Z\",\n  \"timestamp2\": \"2025-01-29T13:45:30Z\",\n  \"format\": \"components\"\n}\n// Returns: {\"difference_formatted\": \"0d 3h 45m 30s\"}\n```\n\n**Output Formats Explained:**\n- **seconds**: Total duration in seconds (decimal)\n- **minutes**: Total duration in minutes (decimal)\n- **hours**: Total duration in hours (decimal)\n- **days**: Total duration in days (decimal)\n- **human**: Readable format with units (e.g., \"2 days, 3 hours\")\n- **components**: Compact format \"Xd Yh Zm Ws\" (useful for logs)\n\n**Error Responses:**\n- 400: Invalid timestamp format (unparseable)\n- 400: Invalid output format (not in allowed list)\n- 401: Missing or invalid API key\n- 500: Unexpected calculation error\n\n**Notes:**\n- Difference is always positive (absolute value)\n- Time zones are preserved in parsing\n- Millisecond timestamps auto-detected (> 10 billion)\n- Supports dates far in past/future (Unix epoch limits)\n\n**Related Endpoints:**\n- `GET /tools/time` - Get current time in any timezone","operationId":"calculate_time_diff_api_v2_tools_time_diff_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeDiffRequest"}}}},"responses":{"200":{"description":"Time difference calculated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeDiffResponse"}}}},"400":{"description":"Invalid timestamp format or invalid output format"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Calculation error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/text-search":{"post":{"tags":["tools","tools"],"summary":"Text Search","description":"Search for regex patterns in text with full regex support.\n\nThis tool provides powerful regex-based text searching with comprehensive\nmatch information including positions, groups, and line numbers. Perfect\nfor LLMs that need to find patterns in text buffers.\n\nFeatures:\n    - Full Python regex support (re module)\n    - Case sensitive/insensitive matching\n    - Multiline and dotall modes\n    - Capture groups (numbered and named)\n    - Position information (character offsets and line numbers)\n    - Global matching (find all occurrences)\n\nArgs:\n    text: The text content to search in\n    pattern: Regular expression pattern (Python regex syntax)\n    case_sensitive: Whether to match case (default: True)\n    multiline: Enable multiline mode (^ and $ match line boundaries)\n    dotall: Enable dotall mode (. matches newlines)\n    return_positions: Include start/end positions in results\n    return_groups: Include capture groups in results\n    max_matches: Limit number of matches returned (None = all matches)\n\nReturns:\n    Search results including:\n    - Total match count\n    - List of matches with text, positions, line numbers\n    - Capture groups (if pattern contains groups)\n    - Flags used for the search\n\nExamples:\n    Example 1: Simple case-insensitive search\n    POST /api/v2/tools/text-search\n    {\n      \"text\": \"Hello World\\nHELLO there\\nhello again\",\n      \"pattern\": \"hello\",\n      \"case_sensitive\": false\n    }\n    -> Returns 3 matches with positions and line numbers\n\n    Example 2: Extract email addresses with groups\n    POST /api/v2/tools/text-search\n    {\n      \"text\": \"Contact: john@example.com or jane@company.org\",\n      \"pattern\": \"([a-z]+)@([a-z]+\\\\.[a-z]+)\",\n      \"return_groups\": true\n    }\n    -> Returns matches with groups: [\"john\", \"example.com\"], [\"jane\", \"company.org\"]\n\n    Example 3: Find function definitions with named groups\n    POST /api/v2/tools/text-search\n    {\n      \"text\": \"def calculate(x, y):\\n    return x + y\\n\\ndef process(data):\\n    return data\",\n      \"pattern\": \"def (?P<name>\\\\w+)\\\\((?P<params>[^)]*)\\\\):\",\n      \"multiline\": true\n    }\n    -> Returns matches with named groups: {\"name\": \"calculate\", \"params\": \"x, y\"}\n\n    Example 4: Find URLs and limit results\n    POST /api/v2/tools/text-search\n    {\n      \"text\": \"Visit https://example.com or http://test.org or https://demo.net\",\n      \"pattern\": \"https?://[\\\\w.-]+\",\n      \"max_matches\": 2\n    }\n    -> Returns first 2 URL matches only\n\n    Example 5: Multiline code block extraction\n    POST /api/v2/tools/text-search\n    {\n      \"text\": \"Some text\\n```python\\nprint('hello')\\n```\\nMore text\\n```js\\nconsole.log('hi')\\n```\",\n      \"pattern\": \"```(\\\\w+)\\n(.+?)```\",\n      \"multiline\": true,\n      \"dotall\": true\n    }\n    -> Returns code blocks with language and content groups\n\nRegex Syntax Notes:\n    - Use Python regex syntax (re module)\n    - Escape special chars: \\\\ for backslash, \\\\. for literal dot\n    - Character classes: \\\\d (digit), \\\\w (word), \\\\s (whitespace)\n    - Quantifiers: * (0+), + (1+), ? (0-1), {n,m} (n to m times)\n    - Groups: () for capture, (?:) for non-capture, (?P<name>) for named\n    - Anchors: ^ (start), $ (end), \\\\b (word boundary)\n    - Flags set via parameters: case_sensitive, multiline, dotall","operationId":"text_search_api_v2_tools_text_search_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextSearchRequest"}}}},"responses":{"200":{"description":"Search completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextSearchResponse"}}}},"400":{"description":"Invalid regex pattern"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Search execution error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/text-replace":{"post":{"tags":["tools","tools"],"summary":"Text Replace","description":"Replace regex patterns in text with full regex support.\n\nThis tool provides powerful regex-based find and replace functionality\nwith support for backreferences, groups, and global replacement. Perfect\nfor LLMs that need to transform text buffers.\n\nFeatures:\n    - Full Python regex replacement support\n    - Global replacement (replace all) or limited replacements\n    - Backreferences: \\1, \\2, etc. or \\g<name> for named groups\n    - Case sensitive/insensitive matching\n    - Multiline and dotall modes\n    - Optional unified diff output\n\nArgs:\n    text: The text content to perform replacements on\n    pattern: Regular expression pattern to find (Python regex syntax)\n    replacement: Replacement string (supports backreferences)\n    case_sensitive: Whether to match case (default: True)\n    multiline: Enable multiline mode (^ and $ match line boundaries)\n    dotall: Enable dotall mode (. matches newlines)\n    max_replacements: Maximum replacements (None = replace all/global)\n    return_diff: Return unified diff showing changes\n\nReturns:\n    Replacement results including:\n    - Original and modified text\n    - Number of replacements made\n    - Optional unified diff output\n    - Flags used for the operation\n\nExamples:\n    Example 1: Simple global replacement\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"foo bar foo baz foo\",\n      \"pattern\": \"foo\",\n      \"replacement\": \"qux\"\n    }\n    -> Returns: \"qux bar qux baz qux\" (3 replacements)\n\n    Example 2: Case-insensitive replacement\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"Hello HELLO hello\",\n      \"pattern\": \"hello\",\n      \"replacement\": \"hi\",\n      \"case_sensitive\": false\n    }\n    -> Returns: \"hi hi hi\" (3 replacements)\n\n    Example 3: Backreference - swap two words\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"firstName lastName\",\n      \"pattern\": \"(\\\\w+) (\\\\w+)\",\n      \"replacement\": \"\\\\2, \\\\1\"\n    }\n    -> Returns: \"lastName, firstName\"\n\n    Example 4: Named groups - format dates\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"Date: 2025-10-12\",\n      \"pattern\": \"(?P<year>\\\\d{4})-(?P<month>\\\\d{2})-(?P<day>\\\\d{2})\",\n      \"replacement\": \"\\\\g<month>/\\\\g<day>/\\\\g<year>\"\n    }\n    -> Returns: \"Date: 10/12/2025\"\n\n    Example 5: Limited replacements\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"test test test test\",\n      \"pattern\": \"test\",\n      \"replacement\": \"TEST\",\n      \"max_replacements\": 2\n    }\n    -> Returns: \"TEST TEST test test\" (only 2 replacements)\n\n    Example 6: Add line numbers with groups\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"line one\\nline two\\nline three\",\n      \"pattern\": \"^(.+)$\",\n      \"replacement\": \"\\\\g<0>\",\n      \"multiline\": true\n    }\n    -> Can add line numbers or modify each line\n\n    Example 7: Extract and reformat code\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"function add(a, b) { return a + b; }\",\n      \"pattern\": \"function (\\\\w+)\\\\(([^)]*)\\\\)\",\n      \"replacement\": \"const \\\\1 = (\\\\2) =>\"\n    }\n    -> Returns: \"const add = (a, b) => { return a + b; }\"\n\n    Example 8: With diff output\n    POST /api/v2/tools/text-replace\n    {\n      \"text\": \"old value\",\n      \"pattern\": \"old\",\n      \"replacement\": \"new\",\n      \"return_diff\": true\n    }\n    -> Returns modified text plus unified diff showing change\n\nBackreference Syntax:\n    - \\\\1, \\\\2, ... : Reference numbered groups\n    - \\\\g<1>, \\\\g<2> : Alternative numbered group syntax\n    - \\\\g<name> : Reference named groups\n    - \\\\g<0> : Reference entire match\n    - \\\\\\\\: Literal backslash in replacement\n\nRegex Syntax Notes:\n    - Same as text-search: Python regex syntax (re module)\n    - Use () for capture groups that can be referenced\n    - Use (?:) for non-capturing groups\n    - Use (?P<name>) for named groups","operationId":"text_replace_api_v2_tools_text_replace_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextReplaceRequest"}}}},"responses":{"200":{"description":"Replacement completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextReplaceResponse"}}}},"400":{"description":"Invalid regex pattern or replacement string"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Replacement execution error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/text-extract":{"post":{"tags":["tools","tools"],"summary":"Text Extract","description":"Extract text sections using regex patterns.\n\nThis tool extracts specific sections of text using regex patterns,\noptionally extracting specific capture groups and joining results.\nPerfect for LLMs that need to pull out structured data from text.\n\nFeatures:\n    - Extract full matches or specific capture groups\n    - Support for numbered and named groups\n    - Join multiple extractions with custom delimiter\n    - Case sensitive/insensitive matching\n    - Multiline and dotall modes\n\nArgs:\n    text: The text content to extract from\n    pattern: Regular expression pattern (Python regex syntax)\n    case_sensitive: Whether to match case (default: True)\n    multiline: Enable multiline mode (^ and $ match line boundaries)\n    dotall: Enable dotall mode (. matches newlines)\n    extract_group: Which group to extract (None=full match, 1+=group number)\n    extract_named_group: Extract specific named group (overrides extract_group)\n    join_with: Join all extractions with this delimiter (e.g., \"\\n\", \", \")\n\nReturns:\n    Extraction results including:\n    - List of extracted strings\n    - Count of extractions\n    - Optional joined result\n    - Flags used for the operation\n\nExamples:\n    Example 1: Extract all email addresses\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"Contact us at john@example.com or support@company.org\",\n      \"pattern\": \"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}\"\n    }\n    -> Returns: [\"john@example.com\", \"support@company.org\"]\n\n    Example 2: Extract only usernames from emails\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"john@example.com and jane@company.org\",\n      \"pattern\": \"([a-z]+)@[a-z]+\\\\.[a-z]+\",\n      \"extract_group\": 1\n    }\n    -> Returns: [\"john\", \"jane\"]\n\n    Example 3: Extract code between triple backticks\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"Here's code:\\n```\\nprint('hello')\\n```\\nAnd more:\\n```\\nconsole.log('hi')\\n```\",\n      \"pattern\": \"```\\n(.+?)```\",\n      \"extract_group\": 1,\n      \"dotall\": true\n    }\n    -> Returns: [\"print('hello')\", \"console.log('hi')\"]\n\n    Example 4: Extract named groups - parse structured data\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"User: John Doe (ID: 12345)\\nUser: Jane Smith (ID: 67890)\",\n      \"pattern\": \"User: (?P<name>[^(]+)\\\\(ID: (?P<id>\\\\d+)\\\\)\",\n      \"extract_named_group\": \"name\"\n    }\n    -> Returns: [\"John Doe \", \"Jane Smith \"]\n\n    Example 5: Extract and join URLs\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"Visit https://example.com and http://test.org or https://demo.net\",\n      \"pattern\": \"https?://[\\\\w.-]+\",\n      \"join_with\": \", \"\n    }\n    -> Returns: extractions list + joined_result: \"https://example.com, http://test.org, https://demo.net\"\n\n    Example 6: Extract function names from code\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"def process(data):\\n    pass\\n\\ndef calculate(x, y):\\n    return x + y\",\n      \"pattern\": \"def (\\\\w+)\\\\(\",\n      \"extract_group\": 1,\n      \"multiline\": true,\n      \"join_with\": \"\\n\"\n    }\n    -> Returns: [\"process\", \"calculate\"], joined: \"process\\ncalculate\"\n\n    Example 7: Extract quoted strings\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"He said \\\"hello\\\" and she said \\\"goodbye\\\"\",\n      \"pattern\": \"\\\\\\\"([^\\\\\\\"]+)\\\\\\\"\",\n      \"extract_group\": 1\n    }\n    -> Returns: [\"hello\", \"goodbye\"]\n\n    Example 8: Extract CSV columns\n    POST /api/v2/tools/text-extract\n    {\n      \"text\": \"name,email,age\\nJohn,john@example.com,30\\nJane,jane@test.org,25\",\n      \"pattern\": \"^[^,]+,([^,]+),\",\n      \"extract_group\": 1,\n      \"multiline\": true\n    }\n    -> Returns: [\"email\", \"john@example.com\", \"jane@test.org\"]\n\nGroup Extraction Notes:\n    - extract_group: None (default) extracts full match (group 0)\n    - extract_group: 1, 2, ... extracts numbered groups\n    - extract_named_group: \"name\" extracts named group (?P<name>...)\n    - If both set, extract_named_group takes precedence","operationId":"text_extract_api_v2_tools_text_extract_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextExtractRequest"}}}},"responses":{"200":{"description":"Extraction completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextExtractResponse"}}}},"400":{"description":"Invalid regex pattern or group reference"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Extraction execution error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/text-diff":{"post":{"tags":["tools","tools"],"summary":"Text Diff","description":"Compare two text buffers and generate a diff.\n\nThis tool generates various diff formats comparing two text strings,\nuseful for LLMs that need to show changes, compare versions, or\nvalidate modifications.\n\nFeatures:\n    - Multiple diff formats (unified, context, ndiff, HTML)\n    - Configurable context lines\n    - Change statistics (additions, deletions, modifications)\n    - Custom file labels for diff output\n\nArgs:\n    text1: First text (original/old version)\n    text2: Second text (modified/new version)\n    format: Diff format to generate:\n        - \"unified\" (default): Standard unified diff (git-style)\n        - \"context\": Context diff format\n        - \"ndiff\": Line-by-line diff with change indicators\n        - \"html\": HTML table diff with color coding\n    context_lines: Number of context lines around changes (default: 3)\n    from_file: Label for original text (default: \"text1\")\n    to_file: Label for modified text (default: \"text2\")\n\nReturns:\n    Diff results including:\n    - Formatted diff output\n    - Format used\n    - Change statistics (additions, deletions, modifications)\n    - File labels used\n\nExamples:\n    Example 1: Simple unified diff\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"Hello World\\nGoodbye World\",\n      \"text2\": \"Hello World\\nHello Universe\"\n    }\n    -> Returns unified diff showing \"Goodbye World\" -> \"Hello Universe\"\n\n    Example 2: HTML diff for display\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"Line 1\\nLine 2\\nLine 3\",\n      \"text2\": \"Line 1\\nModified Line 2\\nLine 3\",\n      \"format\": \"html\"\n    }\n    -> Returns HTML table with color-coded changes\n\n    Example 3: Ndiff for character-level changes\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"Hello World\",\n      \"text2\": \"Hallo World\",\n      \"format\": \"ndiff\"\n    }\n    -> Returns: \"- Hello World\\n? -\\n+ Hallo World\\n? +\"\n\n    Example 4: Custom file labels\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"original code\",\n      \"text2\": \"modified code\",\n      \"from_file\": \"main.py (before)\",\n      \"to_file\": \"main.py (after)\"\n    }\n    -> Diff shows custom file labels in header\n\n    Example 5: More context lines\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"line1\\nline2\\nline3\\nline4\\nline5\",\n      \"text2\": \"line1\\nline2\\nCHANGED\\nline4\\nline5\",\n      \"context_lines\": 5\n    }\n    -> Shows all 5 lines of context around the change\n\n    Example 6: Context diff format\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"Old version of file\",\n      \"text2\": \"New version of file\",\n      \"format\": \"context\"\n    }\n    -> Returns context-style diff (used by some older tools)\n\n    Example 7: Compare code versions\n    POST /api/v2/tools/text-diff\n    {\n      \"text1\": \"def calculate(x):\\n    return x * 2\",\n      \"text2\": \"def calculate(x, y):\\n    return x * y\",\n      \"from_file\": \"v1.0/utils.py\",\n      \"to_file\": \"v2.0/utils.py\"\n    }\n    -> Shows function signature and implementation changes\n\nDiff Format Details:\n    unified: Standard format, shows changes with +/- lines\n        - Lines start with space (unchanged), + (added), - (removed)\n        - Header shows file names and line numbers\n        - Most common format, used by git diff\n\n    context: Similar to unified but different syntax\n        - Uses ! for changed lines\n        - Shows full context blocks\n        - Less common, used by some older tools\n\n    ndiff: Line-by-line comparison with change details\n        - Shows exact character differences with ? lines\n        - More verbose but very clear\n        - Good for small changes\n\n    html: HTML table format with color coding\n        - Generates side-by-side HTML table\n        - Color-coded additions (green) and deletions (red)\n        - Perfect for web display or reports\n\nChange Statistics:\n    - additions: Number of lines added\n    - deletions: Number of lines deleted\n    - modifications: Number of lines changed (delete + add in same place)","operationId":"text_diff_api_v2_tools_text_diff_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextDiffRequest"}}}},"responses":{"200":{"description":"Diff generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextDiffResponse"}}}},"400":{"description":"Invalid diff format"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Diff generation error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/text/text-stats":{"post":{"tags":["tools","tools"],"summary":"Compute Text Stats","description":"Compute comprehensive text statistics and readability metrics.\n\nThis tool analyzes text to provide basic statistics (word count, character count,\nline count, etc.) along with advanced readability metrics like Flesch Reading Ease,\nFlesch-Kincaid Grade Level, and Gunning Fog Index.\n\nPerfect for LLMs that need to:\n- Analyze document complexity\n- Assess readability levels\n- Generate content statistics\n- Validate content meets readability requirements\n\nFeatures:\n    - Basic statistics: characters, words, lines, sentences\n    - Word analysis: average length, longest word, unique words\n    - Readability scores: Flesch Reading Ease, Flesch-Kincaid, Gunning Fog\n    - Fast computation without external dependencies\n\nArgs:\n    text: The text content to analyze\n\nReturns:\n    Comprehensive statistics including:\n    - Character counts (with and without spaces)\n    - Word and line counts\n    - Sentence count\n    - Average word length\n    - Longest word\n    - Unique word count\n    - Readability metrics (Flesch, Flesch-Kincaid, Gunning Fog)\n\nReadability Score Interpretation:\n    Flesch Reading Ease (0-100, higher = easier):\n    - 90-100: Very easy (child-level)\n    - 60-70: Standard/plain English\n    - 0-30: Very difficult (academic)\n\n    Flesch-Kincaid Grade: U.S. school grade level needed to understand\n\n    Gunning Fog Index: Years of education needed to understand on first reading\n\nExample:\n    POST /api/v2/tools/text/text-stats\n    {\n      \"text\": \"This is a simple test. It should be easy to read.\"\n    }\n    -> Returns full statistics with readability scores","operationId":"compute_text_stats_api_v2_tools_text_text_stats_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextStatsRequest"}}}},"responses":{"200":{"description":"Statistics computed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextStatsResponse"}}}},"400":{"description":"Invalid text input"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Statistics computation error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/text/lorem-ipsum":{"post":{"tags":["tools","tools"],"summary":"Generate Lorem Ipsum","description":"Generate Lorem Ipsum placeholder text.\n\nThis tool generates Lorem Ipsum text with configurable length and formatting.\nPerfect for LLMs that need to create placeholder content, test data, or\nmock content for demonstrations.\n\nFeatures:\n    - Configurable length (or random if not specified)\n    - Configurable start position for variety\n    - Optional capitalization control\n    - Circular text wrapping for any length\n    - Never ends with whitespace\n\nArgs:\n    num_chars: Desired length in characters (random 25-174 if None)\n    start_spot: Starting position in source text (random if None)\n    start_with_capital_letter: Capitalize first letter (default: True)\n\nReturns:\n    Generated Lorem Ipsum text and its length\n\nExamples:\n    Example 1: Random length Lorem Ipsum\n    POST /api/v2/tools/text/lorem-ipsum\n    {}\n    -> Returns random length text (25-174 chars)\n\n    Example 2: Specific length\n    POST /api/v2/tools/text/lorem-ipsum\n    {\n      \"num_chars\": 100\n    }\n    -> Returns exactly 100 characters\n\n    Example 3: Custom start position and no capitalization\n    POST /api/v2/tools/text/lorem-ipsum\n    {\n      \"num_chars\": 50,\n      \"start_spot\": 100,\n      \"start_with_capital_letter\": false\n    }\n    -> Returns 50 chars starting from position 100, lowercase first letter\n\n    Example 4: Very long text\n    POST /api/v2/tools/text/lorem-ipsum\n    {\n      \"num_chars\": 1000\n    }\n    -> Returns 1000 chars (source text wraps/repeats as needed)\n\nNotes:\n    - Text is generated from canonical Lorem Ipsum paragraph\n    - Source text wraps circularly for any length\n    - Trailing spaces are replaced with periods\n    - First character defaults to uppercase (A-Z only)\n    - If capitalization fails, uses 'M' as fallback","operationId":"generate_lorem_ipsum_api_v2_tools_text_lorem_ipsum_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoremIpsumRequest"}}}},"responses":{"200":{"description":"Lorem ipsum generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoremIpsumResponse"}}}},"400":{"description":"Invalid parameters (negative counts, invalid format)"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Text generation error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/web-crawl-simple":{"post":{"tags":["tools","tools"],"summary":"Fetch and extract content from a web page","description":"Fetch and extract content from a single web page (HTTP-based, no JavaScript rendering).\n\nThis endpoint fetches a URL via HTTP and extracts content in multiple formats.\nPerfect for documentation pages, blogs, static sites, and content that doesn't\nrequire JavaScript rendering.\n\nFeatures:\n    - Fast HTTP-based fetching (no browser required)\n    - Multiple content formats (HTML, text, markdown)\n    - Metadata extraction (title, description, etc.)\n    - Link extraction with categorization\n    - Configurable timeout\n\nLimitations:\n    - Cannot render JavaScript (use crawl-complex for SPAs)\n    - No screenshot capability\n    - No interaction with page elements\n\nArgs:\n    url: The URL to fetch and extract content from\n    extract_formats: List of formats to extract content in:\n        - \"html\": Raw HTML\n        - \"text\": Plain text (stripped of all tags)\n        - \"markdown\": Converted to markdown\n    include_metadata: Whether to extract page metadata (title, description, etc.)\n    include_links: Whether to extract and categorize links\n    timeout: Request timeout in seconds (default: 10)\n\nReturns:\n    Page content in requested formats plus metadata and links\n\nExamples:\n    Example 1: Fetch documentation page as markdown\n    POST /api/v2/tools/web-crawl-simple\n    {\n      \"url\": \"https://docs.python.org/3/library/asyncio.html\",\n      \"extract_formats\": [\"markdown\"],\n      \"include_metadata\": true,\n      \"include_links\": false\n    }\n    -> Returns markdown content with title and description\n\n    Example 2: Fetch blog post with links\n    POST /api/v2/tools/web-crawl-simple\n    {\n      \"url\": \"https://blog.example.com/article\",\n      \"extract_formats\": [\"markdown\", \"text\"],\n      \"include_links\": true\n    }\n    -> Returns content in multiple formats plus categorized links\n\n    Example 3: Fetch HTML for processing\n    POST /api/v2/tools/web-crawl-simple\n    {\n      \"url\": \"https://example.com\",\n      \"extract_formats\": [\"html\"]\n    }\n    -> Returns raw HTML only\n\nSecurity:\n    - Private IP ranges are blocked (prevents SSRF)\n    - Timeout enforced to prevent hanging\n    - Maximum response size: 50MB\n\nPerformance:\n    - Typical response time: 200-1000ms\n    - Concurrent requests supported\n    - No browser overhead\n\nNext Steps:\n    - For JavaScript-heavy sites, use /crawl-complex (coming soon)\n    - For multi-page crawling, use max_depth parameter (coming soon)","operationId":"web_crawl_simple_api_v2_tools_web_crawl_simple_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrawlSimpleRequest"}}}},"responses":{"200":{"description":"Page fetched and content extracted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrawlSimpleResponse"}}}},"400":{"description":"Invalid URL or extraction format"},"401":{"description":"Authentication required - missing or invalid API key"},"403":{"description":"Access denied - SSRF protection (private IP blocked)"},"404":{"description":"Page not found"},"500":{"description":"Fetch or extraction error"},"504":{"description":"Request timed out"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/convert-to-pdf":{"post":{"tags":["tools","tools"],"summary":"Convert To Pdf","description":"Convert documents to PDF format.\n\nSupports conversion from:\n- Markdown (.md)\n- HTML (.html)\n- Microsoft Word (.docx)\n- Plain text (.txt)\n\nThe converted PDF is saved to the specified output path in the project.\n\nArgs:\n    file_id: ID of file to convert\n    project_id: Project containing the file\n    output_path: Destination path for PDF (default: /converted)\n    options: PDF conversion options:\n        - page_size: Paper size (Letter, A4, Legal, etc.)\n        - margin: Page margins (e.g., \"1in\", \"2cm\")\n        - header: Header text\n        - footer: Footer text (supports {page} and {total} placeholders)\n\nReturns:\n    Information about the converted PDF file including:\n    - file_id: ID of the new PDF file\n    - filename: Name of the PDF file\n    - path: Path in project\n    - size_bytes: File size\n    - pages: Number of pages (if available)\n    - download_url: URL to download the PDF\n\nExamples:\n    Example 1: Basic conversion\n    POST /api/v2/tools/convert-to-pdf\n    {\n      \"file_id\": \"fil-abc123\",\n      \"project_id\": \"prj-xyz456\"\n    }\n    -> Converts file to PDF and saves to /converted\n\n    Example 2: Custom output path\n    POST /api/v2/tools/convert-to-pdf\n    {\n      \"file_id\": \"fil-abc123\",\n      \"project_id\": \"prj-xyz456\",\n      \"output_path\": \"/sop-runs/patent-2025-10-16\"\n    }\n    -> Saves to specific SOP run directory\n\n    Example 3: With custom options\n    POST /api/v2/tools/convert-to-pdf\n    {\n      \"file_id\": \"fil-abc123\",\n      \"project_id\": \"prj-xyz456\",\n      \"output_path\": \"/pdfs\",\n      \"options\": {\n        \"page_size\": \"Letter\",\n        \"margin\": \"1in\",\n        \"header\": \"Confidential Document\",\n        \"footer\": \"Page {page} of {total}\"\n      }\n    }\n    -> Converts with custom formatting\n\nNotes:\n    - Original file is not modified\n    - PDF is created as a new file in the project\n    - Requires viewer+ permission on the project\n    - Output filename is automatically generated from source filename","operationId":"convert_to_pdf_api_v2_tools_convert_to_pdf_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertToPDFRequest"}}}},"responses":{"201":{"description":"PDF converted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertToPDFResponse"}}}},"400":{"description":"Invalid file format or unsupported conversion"},"401":{"description":"Authentication required - missing or invalid API key"},"403":{"description":"Access denied - insufficient project permissions"},"404":{"description":"File or project not found"},"500":{"description":"Conversion error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/pdf/extract_digital":{"post":{"tags":["tools","tools"],"summary":"Extract Digital","description":"Extract text from digital/native PDF files.\n\nThis endpoint extracts text from PDFs that have a native text layer\n(born-digital PDFs created from word processors, not scanned documents).\nIt uses PyMuPDF to directly extract embedded text, which is fast and accurate.\n\nArgs:\n    pdf_data: Base64-encoded PDF file content\n\nReturns:\n    Extraction results including:\n    - success: Whether extraction succeeded\n    - pdf_type: Always \"digital\" for this endpoint\n    - confidence: Heuristic confidence score (0.0-1.0)\n    - text: Concatenated plain text from all pages\n    - pages: Number of pages processed\n    - metadata: PDF metadata (author, title, creation_date, etc.)\n\nExamples:\n    Example 1: Extract from digital PDF\n    POST /api/v2/tools/pdf/extract_digital\n    {\n      \"pdf_data\": \"JVBERi0xLjQK...\"\n    }\n    -> Returns extracted text with high confidence (0.95+)\n\nNotes:\n    - Best for PDFs created from Word, Google Docs, LaTeX, etc.\n    - Not suitable for scanned documents (use extract_ocr instead)\n    - Confidence is based on text extraction quality heuristics\n    - Requires user-level authentication","operationId":"extract_digital_api_v2_tools_pdf_extract_digital_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PDFExtractDigitalRequest"}}}},"responses":{"200":{"description":"PDF text extracted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PDFExtractDigitalResponse"}}}},"400":{"description":"Invalid PDF file or page range"},"401":{"description":"Authentication required - missing or invalid API key"},"404":{"description":"PDF file not found"},"500":{"description":"PDF extraction error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/pdf/extract_ocr":{"post":{"tags":["tools","tools"],"summary":"Extract Ocr","description":"Extract text from scanned/image-based PDF files using OCR.\n\nSES-219: OCR runs in a thread pool to avoid blocking the event loop.\nA concurrency semaphore limits parallel OCR jobs. Per-page timeouts\nprevent individual pages from hanging indefinitely.\n\nArgs:\n    pdf_data: Base64-encoded PDF file content\n    dpi: Resolution for rendering pages (72-600, default: 300)\n    ocr_lang: OCR language code(s) (e.g., \"eng\", \"eng+spa\")\n    max_pages: Maximum pages to process (1-200, default: 50)\n\nReturns:\n    Extraction results including:\n    - success: Whether extraction succeeded\n    - pdf_type: Always \"scanned\" for this endpoint\n    - confidence: Overall OCR confidence score\n    - text: OCR-extracted text from all pages\n    - pages: Number of pages processed\n    - total_pages: Total pages in the document\n    - truncated: Whether the document was truncated\n    - metadata: PDF metadata if available\n    - ocr_details: OCR engine info and per-page confidence\n\nExamples:\n    Example 1: Basic OCR extraction\n    POST /api/v2/tools/pdf/extract_ocr\n    {\n      \"pdf_data\": \"JVBERi0xLjQK...\"\n    }\n    -> Uses default 300 DPI, English OCR, max 50 pages\n\n    Example 2: High-resolution multilingual OCR\n    POST /api/v2/tools/pdf/extract_ocr\n    {\n      \"pdf_data\": \"JVBERi0xLjQK...\",\n      \"dpi\": 400,\n      \"ocr_lang\": \"eng+spa\",\n      \"max_pages\": 100\n    }\n    -> Higher quality, English + Spanish, up to 100 pages\n\nNotes:\n    - OCR is slower than digital extraction (typically 5-30 seconds per page)\n    - Higher DPI = better accuracy but slower processing\n    - Documents exceeding max_pages are truncated with truncated=true\n    - Per-page timeout of 60 seconds prevents hung pages\n    - Requires Tesseract OCR to be installed on the system\n    - Requires user-level authentication","operationId":"extract_ocr_api_v2_tools_pdf_extract_ocr_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PDFExtractOCRRequest"}}}},"responses":{"200":{"description":"OCR text extraction completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PDFExtractOCRResponse"}}}},"400":{"description":"Invalid PDF file or OCR parameters"},"401":{"description":"Authentication required - missing or invalid API key"},"404":{"description":"PDF file not found or Tesseract not installed"},"500":{"description":"OCR extraction error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/pdf/extract_with_schema":{"post":{"tags":["tools","tools"],"summary":"Extract With Schema","description":"Extract structured data from PDF using a schema definition.\n\nThis endpoint combines PDF detection, extraction, and schema-based parsing\nto extract specific fields (invoice numbers, dates, tables, etc.) from PDFs.\n\nArgs:\n    pdf_data: Base64-encoded PDF file content\n    schema: Dictionary defining fields to extract with patterns/heuristics\n    mode: Extraction strategy:\n        - \"digital\": Force digital text extraction\n        - \"ocr\": Force OCR extraction\n        - \"auto\": Auto-detect PDF type and choose best method\n\nReturns:\n    Structured extraction results including:\n    - success: Whether extraction succeeded\n    - pdf_type: \"digital\", \"scanned\", or \"mixed\"\n    - confidence: Overall extraction confidence\n    - metadata: PDF metadata\n    - extraction_path: Method used (\"digital\", \"ocr\", or \"mixed\")\n    - fields: Per-field extraction results with values, confidence, method\n\nSchema Format:\n    {\n      \"field_name\": {\n        \"pattern\": \"regex pattern to match\",\n        \"type\": \"string|number|date|table\",\n        \"required\": true|false\n      }\n    }\n\nExamples:\n    Example 1: Extract invoice data\n    POST /api/v2/tools/pdf/extract_with_schema\n    {\n      \"pdf_data\": \"JVBERi0xLjQK...\",\n      \"schema\": {\n        \"invoice_number\": {\n          \"pattern\": \"INV-\\d+\",\n          \"type\": \"string\",\n          \"required\": true\n        },\n        \"total_amount\": {\n          \"pattern\": \"Total:?\\s*\\$?([\\d,]+\\.\\d{2})\",\n          \"type\": \"number\"\n        }\n      },\n      \"mode\": \"auto\"\n    }\n    -> Returns structured fields with extraction confidence\n\nNotes:\n    - \"auto\" mode detects PDF type and chooses best extraction method\n    - Schema matching uses regex patterns and text heuristics\n    - Basic implementation: extracts text then applies regex patterns\n    - Requires user-level authentication","operationId":"extract_with_schema_api_v2_tools_pdf_extract_with_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PDFExtractWithSchemaRequest"}}}},"responses":{"200":{"description":"Structured data extracted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PDFExtractWithSchemaResponse"}}}},"400":{"description":"Invalid PDF file or schema"},"401":{"description":"Authentication required - missing or invalid API key"},"404":{"description":"PDF file not found"},"500":{"description":"Schema-based extraction error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/docx/extract":{"post":{"tags":["tools","tools"],"summary":"Extract Docx Text","description":"Extract text from DOCX document with metadata and structure information.\n\nThis endpoint extracts all text content from a Microsoft Word DOCX file,\nincluding paragraphs, tables, headers, and footers. It provides comprehensive\nmetadata and document structure information.\n\nArgs:\n    docx_data: Base64-encoded DOCX file content\n    include_tables: Include table text in extraction (default: True)\n    include_headers: Include header text (default: True)\n    include_footers: Include footer text (default: True)\n\nReturns:\n    Extraction results including:\n    - success: Whether extraction succeeded\n    - text: Full extracted text\n    - metadata: Document metadata (author, title, created, modified, word count, etc.)\n    - structure: Document structure (paragraph count, table count, sections)\n    - tables: Table information (rows, columns, text)\n\nExamples:\n    Example 1: Extract all text from DOCX\n    POST /api/v2/tools/docx/extract\n    {\n      \"docx_data\": \"UEsDBBQABgAIAAAAI...\"\n    }\n    -> Returns full text with metadata\n\n    Example 2: Extract without tables\n    POST /api/v2/tools/docx/extract\n    {\n      \"docx_data\": \"UEsDBBQABgAIAAAAI...\",\n      \"include_tables\": false\n    }\n    -> Returns text without table content\n\nNotes:\n    - DOCX files must be valid Microsoft Word format\n    - Base64 encoding required for binary data transmission\n    - No OCR needed (DOCX is structured text)\n    - Requires user-level authentication","operationId":"extract_docx_text_api_v2_tools_docx_extract_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DOCXExtractRequest"}}}},"responses":{"200":{"description":"DOCX text extracted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DOCXExtractResponse"}}}},"400":{"description":"Invalid DOCX file"},"401":{"description":"Authentication required - missing or invalid API key"},"404":{"description":"DOCX file not found"},"500":{"description":"DOCX extraction error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/docx/extract_with_schema":{"post":{"tags":["tools","tools"],"summary":"Extract Docx With Schema","description":"Extract structured fields from DOCX using schema definition.\n\nThis endpoint extracts specific fields from a DOCX document using\nuser-defined regex patterns. It provides per-field confidence scores\nand handles required/optional fields with defaults.\n\nArgs:\n    docx_data: Base64-encoded DOCX file content\n    schema: Dictionary defining fields to extract with patterns\n    include_tables: Search within tables (default: True)\n    search_headers: Search within headers (default: True)\n    search_footers: Search within footers (default: False)\n\nReturns:\n    Structured extraction results including:\n    - success: Whether extraction succeeded\n    - confidence: Overall extraction confidence (0.0-1.0)\n    - metadata: Document metadata\n    - fields: Extracted field values per schema definition\n\nSchema Format:\n    {\n      \"field_name\": {\n        \"pattern\": \"regex pattern to match\",\n        \"required\": true|false,\n        \"default\": null|\"value\"\n      }\n    }\n\nExamples:\n    Example 1: Extract contract fields\n    POST /api/v2/tools/docx/extract_with_schema\n    {\n      \"docx_data\": \"UEsDBBQABgAIAAAAI...\",\n      \"schema\": {\n        \"contract_id\": {\n          \"pattern\": \"Contract ID:\\\\s*([A-Z0-9-]+)\",\n          \"required\": true\n        },\n        \"effective_date\": {\n          \"pattern\": \"Effective Date:\\\\s*(\\\\d{2}/\\\\d{2}/\\\\d{4})\",\n          \"required\": true\n        },\n        \"termination_clause\": {\n          \"pattern\": \"Termination:\\\\s*(.+?)(?=\\\\n\\\\n|$)\",\n          \"required\": false,\n          \"default\": null\n        }\n      }\n    }\n    -> Returns extracted fields with confidence scores\n\nNotes:\n    - Regex patterns are applied case-insensitive and multiline\n    - Required fields with no match return null and lower confidence\n    - Optional fields with no match use default value\n    - Confidence weighted toward required fields (70% required, 30% optional)\n    - Requires user-level authentication","operationId":"extract_docx_with_schema_api_v2_tools_docx_extract_with_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DOCXExtractWithSchemaRequest"}}}},"responses":{"200":{"description":"Structured data extracted from DOCX successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DOCXExtractWithSchemaResponse"}}}},"400":{"description":"Invalid DOCX file or schema"},"401":{"description":"Authentication required - missing or invalid API key"},"404":{"description":"DOCX file not found"},"500":{"description":"Schema-based extraction error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/pubmed/search":{"post":{"tags":["tools","tools"],"summary":"Search PubMed for scientific articles","description":"Search PubMed for scientific articles and retrieve full metadata.\n\nThis endpoint provides access to PubMed's database of biomedical literature.\nIt uses NCBI E-utilities API to search and retrieve article metadata including\ntitles, abstracts, authors, citations, and related articles.\n\nFeatures:\n    - Natural language and advanced NCBI query syntax support\n    - Full article metadata including abstracts and MeSH terms\n    - Citation graph data (references and related articles)\n    - Redis caching (24hr TTL) to minimize API calls\n    - Rate limiting (2 req/sec system-wide)\n    - Authenticated access with usage tracking\n\nArgs:\n    query: Search query string. Supports:\n        - Natural language: \"CRISPR gene editing in cancer\"\n        - Field tags: \"cancer immunotherapy[Title/Abstract]\"\n        - Boolean: \"mRNA AND vaccine AND (COVID-19 OR SARS-CoV-2)\"\n        - Author: \"Smith J[Author]\"\n        - Journal: \"Nature[Journal]\"\n    max_results: Maximum articles to return (1-100, default: 10)\n    filters: Optional search filters:\n        - publication_types: List of types (e.g., [\"Clinical Trial\", \"Review\"])\n        - date_range: {\"start\": \"2020/01/01\", \"end\": \"2024/12/31\"}\n        - sort: \"relevance\" (default), \"pub_date\", \"first_author\", \"journal\"\n\nReturns:\n    Search results including:\n    - List of articles with full metadata\n    - Citation graph data (references, related articles)\n    - Search metadata (API calls, cache hits, rate limit status)\n\nRate Limiting:\n    - System-wide limit: 2 requests/second to NCBI API\n    - Cached results do not count toward limit\n    - Rate limit info returned in search_metadata\n\nCaching:\n    - Articles cached for 24 hours by PMID\n    - Significantly reduces API calls for popular articles\n    - Cache stats included in search_metadata\n\nExamples:\n    Example 1: Simple natural language search\n    POST /api/v2/tools/pubmed/search\n    {\n      \"query\": \"CRISPR gene editing in cancer therapy\",\n      \"max_results\": 10\n    }\n    -> Returns top 10 relevant articles\n\n    Example 2: Advanced search with field tags\n    POST /api/v2/tools/pubmed/search\n    {\n      \"query\": \"immunotherapy[Title] AND melanoma[MeSH Terms]\",\n      \"max_results\": 20\n    }\n    -> Returns 20 articles with \"immunotherapy\" in title and melanoma MeSH term\n\n    Example 3: Recent articles with date filter\n    POST /api/v2/tools/pubmed/search\n    {\n      \"query\": \"mRNA vaccine\",\n      \"max_results\": 15,\n      \"filters\": {\n        \"date_range\": {\n          \"start\": \"2023/01/01\",\n          \"end\": \"2024/12/31\"\n        },\n        \"publication_types\": [\"Clinical Trial\"],\n        \"sort\": \"pub_date\"\n      }\n    }\n    -> Returns recent clinical trials sorted by publication date\n\n    Example 4: Author-specific search\n    POST /api/v2/tools/pubmed/search\n    {\n      \"query\": \"Doudna JA[Author] AND CRISPR\",\n      \"max_results\": 10\n    }\n    -> Returns articles by Jennifer Doudna about CRISPR\n\nNCBI Query Syntax:\n    Field Tags:\n        - [Title/Abstract]: Search in title or abstract\n        - [Title]: Search in title only\n        - [Author]: Search by author name\n        - [Journal]: Search by journal name\n        - [MeSH Terms]: Search by MeSH (medical subject headings)\n        - [Publication Type]: Filter by publication type\n\n    Boolean Operators:\n        - AND: Both terms must be present\n        - OR: Either term must be present\n        - NOT: Exclude term\n\n    Example Queries:\n        - \"breast cancer AND chemotherapy\"\n        - \"diabetes[MeSH] AND diet[Title/Abstract]\"\n        - \"(lung cancer OR pulmonary carcinoma) AND immunotherapy\"\n        - \"Smith J[Author] AND Nature[Journal]\"\n\nCitation Graph:\n    Each article includes:\n    - references: PMIDs of articles cited by this article\n    - related_articles: PMIDs of similar articles suggested by NCBI\n\n    Use these to follow citation chains or discover related research.\n\nSecurity:\n    - Requires valid API key authentication\n    - Usage tracked per user/project for billing\n    - Rate limiting prevents API abuse\n\nPerformance:\n    - Cache hit: ~5-10ms response time\n    - Cache miss: ~200-500ms (NCBI API latency)\n    - Batch queries are efficient (single API call for metadata)\n\nError Handling:\n    - 400: Invalid query syntax\n    - 429: Rate limit exceeded (wait and retry)\n    - 503: NCBI API temporarily unavailable\n    - 504: NCBI API timeout\n\nSee Also:\n    - NCBI E-utilities documentation: https://www.ncbi.nlm.nih.gov/books/NBK25501/\n    - PubMed search tips: https://pubmed.ncbi.nlm.nih.gov/help/\n    - MeSH database: https://www.ncbi.nlm.nih.gov/mesh/","operationId":"pubmed_search_api_v2_tools_pubmed_search_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubMedSearchRequest"}}}},"responses":{"200":{"description":"PubMed search completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubMedSearchResponse"}}}},"400":{"description":"Invalid query or parameters"},"401":{"description":"Authentication required - missing or invalid API key"},"429":{"description":"Rate limit exceeded - too many requests"},"500":{"description":"PubMed search error or NCBI API failure"},"503":{"description":"PubMed service unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools":{"get":{"tags":["tools","tools"],"summary":"List available agent-executable tools","description":"List all tools available for use in agent definitions.\n\nSES-150: These are agent-executable tools that can be used in agent YAML\nstep definitions. This is a subset of all API tools - only tools that\nthe execution worker knows how to run.\n\nReturns:\n    List of tool summaries with name, description, and endpoint.","operationId":"list_tools_api_v2_tools_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Tool list retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ToolListItem"},"title":"Response List Tools Api V2 Tools Get"}}}},"401":{"description":"Authentication required - missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/intents/schema":{"get":{"tags":["tools","tools"],"summary":"Get Intent Schema","description":"Discover supported intent schema versions.\n\nReturns the current schema version, all supported versions, and the\nfull JSON Schema for the current response object.  Use this endpoint\nto detect breaking changes, validate client parsers, and plan\nmigrations when new schema versions are released.\n\n**Use Cases**\n\n- **Version pinning**: Check `current_version` before calling extract,\n  and validate your client can handle it.\n- **Schema validation**: Use the `json_schema` field to validate\n  responses programmatically (e.g. with jsonschema library).\n- **Migration planning**: When a new version is released, compare\n  `versions` to understand what changed.\n\n**Example Response:**\n```json\n{\n  \"current_version\": \"1.0\",\n  \"supported_versions\": [\"1.0\"],\n  \"versions\": {\n    \"1.0\": {\n      \"version\": \"1.0\",\n      \"status\": \"current\",\n      \"description\": \"Initial intent extraction schema...\",\n      \"intent_types\": [\"action\", \"question\", \"reference\", \"requirement\", \"constraint\", \"goal\"],\n      \"quality_dimensions\": [\"citation_support\", \"specificity\", \"completeness\", \"type_correctness\", \"unambiguity\"],\n      \"modes\": [\"quick\", \"robust\"]\n    }\n  },\n  \"json_schema\": { ... }\n}\n```","operationId":"get_intent_schema_api_v2_tools_intents_schema_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Intent schema versions returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentSchemaResponse"}}}},"401":{"description":"Authentication required — missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/{tool_name}/schema":{"get":{"tags":["tools","tools"],"summary":"Get tool schema","description":"Get the full schema for a specific tool.\n\nSES-150: Returns the request schema (JSON Schema from Pydantic model)\nand required/optional parameters for the tool.\n\nArgs:\n    tool_name: Name of the tool (e.g., \"calc\", \"web-search\")\n\nReturns:\n    Full tool schema including request parameters.\n\nRaises:\n    404: If tool not found. Error includes available tools.","operationId":"get_tool_schema_api_v2_tools__tool_name__schema_get","parameters":[{"name":"tool_name","in":"path","required":true,"schema":{"type":"string","title":"Tool Name"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Tool schema retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolSchemaResponse"}}}},"401":{"description":"Authentication required - missing or invalid API key"},"404":{"description":"Tool not found - check available tools list"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/decision":{"post":{"tags":["tools","tools"],"summary":"Make routing/triage decision","description":"Make a routing/triage decision using the specified strategy (SES-161).\n\nThe Decision Tool analyzes inputs and selects from a set of choices with reasoning.\nIt supports multiple strategies (LLM, code/expr, hybrid) but **always returns\nthe same output schema**.\n\n**Key Features:**\n- LLM strategy: Classification with ordinal confidence (not numeric self-report)\n- Code/expr strategy: Expression returns scalar matched against choice_id\n- Hybrid strategy: Code result passed to LLM as context (not matched)\n- Default fallback: Always returns a valid decision, uses default on failure\n\n**Strategy Selection Guide:**\n- Use `llm` for classification based on unstructured text\n- Use `code` or `expr` for deterministic decisions with scalar matching\n- Use `hybrid` when you need LLM judgment informed by computed values\n\n**Request Body:**\n- `strategy`: \"llm\" (default), \"code\", \"expr\" (alias of code), or \"hybrid\"\n- `document`: Text to analyze (required for llm/hybrid)\n- `prompt`: Instruction for classification (required for llm/hybrid)\n- `inputs`: Structured data for code/expr evaluation (required for code/expr)\n- `code_expression`: Expression returning scalar (required for code/expr/hybrid)\n- `choices`: List of 2-10 choices with `choice_id` (canonical) or `id` (deprecated)\n- `default`: Required top-level default block with `choice_id` and `label`\n- `model`: LLM model to use (default: claude-3.5-sonnet)\n\n**Response:**\n- `selected_id`: The selected choice_id\n- `used_default`: True if default was used due to failure or no match\n- `choices`: Array of all choices (including default) with confidence/rationale\n- `metadata`: Provenance (strategy, model, tokens, latency)\n\n**Example (LLM strategy):**\n```json\n{\n  \"strategy\": \"llm\",\n  \"document\": \"Customer complaint about billing issue...\",\n  \"prompt\": \"Classify the input into one of the choices.\",\n  \"choices\": [\n    {\"choice_id\": \"billing\", \"label\": \"Billing Issue\"},\n    {\"choice_id\": \"support\", \"label\": \"Technical Support\"}\n  ],\n  \"default\": {\"choice_id\": \"other\", \"label\": \"Other\"}\n}\n```\n\n**Example (Code strategy - scalar matching):**\n```json\n{\n  \"strategy\": \"code\",\n  \"inputs\": {\"risk\": 0.85},\n  \"code_expression\": \"'high_risk' if risk > 0.8 else 'low_risk'\",\n  \"choices\": [\n    {\"choice_id\": \"high_risk\", \"label\": \"High Risk\"},\n    {\"choice_id\": \"low_risk\", \"label\": \"Low Risk\"}\n  ],\n  \"default\": {\"choice_id\": \"unknown\", \"label\": \"Unknown\"}\n}\n```\n\n**Example (Legacy v1 format - still supported):**\n```json\n{\n  \"strategy\": \"code\",\n  \"inputs\": {\"score\": 85},\n  \"code_expression\": \"'fail' if score < 60 else 'pass'\",\n  \"choices\": [\n    {\"id\": \"fail\", \"label\": \"Fail\"},\n    {\"id\": \"pass\", \"label\": \"Pass\"},\n    {\"id\": \"honors\", \"label\": \"Honors\"}\n  ],\n  \"default\": {\"id\": \"unknown\", \"label\": \"Unknown\"}\n}\n```\n\n**Validation Rules:**\n- Minimum 2 choices, maximum 10\n- `choice_id` (canonical) or `id` (deprecated alias) must match: `^[a-z][a-z0-9_]*$`\n- Choice IDs cannot start with `_meta` or `_system` (reserved)\n- No duplicate choice IDs; default must not duplicate a choice\n- `default` block is required\n- LLM strategy requires `document` and `prompt`\n- Code/expr strategy requires `code_expression` and `inputs`\n- Hybrid strategy requires `document`, `prompt`, and `code_expression`\n\n**Side Effects:**\n- Makes LLM API calls for llm/hybrid strategies (uses tokens)\n- Evaluates expressions in sandboxed namespace (no builtins)\n\nSee: sop-server/issue-explore/ses-161-decision-tool-grammar.md","operationId":"decision_tool_api_v2_tools_decision_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionRequest"}}}},"responses":{"200":{"description":"Decision made successfully","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request - missing choices, invalid strategy, or expression error"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Decision execution error - LLM failure or unexpected error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/intents/extract":{"post":{"tags":["tools","tools"],"summary":"Extract Intents Endpoint","description":"Extract structured intents from natural language text.\n\nDecomposes free-form text into a list of typed, structured intents using\nLLM-powered analysis.  Each intent includes the type, subject-verb-object\ntriple, the original citation, a reasoning explanation, and optional\ncontext metadata.\n\nThe response includes a `schema_version` field (currently `\"1.0\"`).\nUse `GET /api/v2/tools/intents/schema` to discover supported versions\nand retrieve the full JSON Schema.\n\n**Intent Types**\n\n| Type | Description | Example |\n|------|-------------|---------|\n| `action` | A task or command to execute | \"Fix the login bug\" |\n| `question` | An inquiry or info request | \"What is the test coverage?\" |\n| `reference` | A fact, date, or data point | \"The deadline is Friday\" |\n| `requirement` | A stated need or feature | \"We need SSO support\" |\n| `constraint` | A limitation or boundary | \"Must use PostgreSQL\" |\n| `goal` | A desired outcome or metric | \"Reduce latency by 50%\" |\n\n**Quality Modes**\n\n- `quick` (default) — Single LLM pass.  Fast and cost-effective.\n- `robust` — Adds a SLAF verification pass that classifies each intent\n  as `supported`, `disputed`, `unclear`, or `not_found` relative to\n  the original text.  Results appear in `audit_result` on each intent.\n\n**Quality Scoring** (opt-in via `include_quality_score=true`)\n\nAn additional LLM pass scores each intent on 5 rubric dimensions:\n\n| Dimension | What it measures |\n|-----------|------------------|\n| `citation_support` | Does the citation match the source text? |\n| `specificity` | Is the intent specific and actionable? |\n| `completeness` | Does it capture the full meaning? |\n| `type_correctness` | Is the assigned type correct? |\n| `unambiguity` | Is the intent clear and unambiguous? |\n\nEach dimension is classified into one of 5 categories\n(`not_at_all`=0, `low`=1, `partial`=2, `high`=3, `exceptional`=4),\nnormalised to 0.0–1.0, and the `overall_score` is the mean.\n\n**Conversation History — Multi-Turn Extraction**\n\nSupply `messages` (prior conversation turns) to extract intents from the\n**entire conversation**, not just the latest message.  Intents are\nextracted from ALL turns — user and assistant alike.  Each intent includes\na `source_turn` field (e.g. `\"user:1\"`, `\"assistant:2\"`, `\"current\"`)\ntracing it back to its origin.\n\nThe model also uses conversation history to resolve pronouns: *\"Fix it\"*\nbecomes *\"Fix the login page authentication bug\"* when prior context\nmentions the login page.\n\n**The Actor / Verb / Target Triple**\n\nEach intent includes a structured triple that tells the caller WHO should\ndo WHAT to WHICH entity:\n\n- `actor` — who should perform this. `\"caller\"` for imperative sentences\n  (the implicit reader/agent), or a specific role from context\n  (e.g. `\"QA team\"`, `\"DevOps\"`).\n- `verb` — the core action in infinitive form: `\"fix\"`, `\"add\"`, `\"test\"`.\n- `target` — the specific entity being acted upon: `\"login page\n  authentication bug\"`, `\"discount service\"`.\n- `direction` — how to handle: `\"do\"` (act), `\"answer\"` (respond to\n  question), `\"note\"` (acknowledge fact), `\"enforce\"` (apply constraint).\n\n---\n\n**Example 1 — Minimal request (quick mode):**\n\n```json\nPOST /api/v2/tools/intents/extract\n{\"text\": \"Fix the login bug and update the docs by Friday\"}\n```\n\nResponse:\n```json\n{\n  \"schema_version\": \"1.0\",\n  \"message_type\": \"single\",\n  \"intents\": [\n    {\n      \"id\": \"int_a1b2c3d4e5f6\",\n      \"type\": \"action\",\n      \"actor\": \"caller\",\n      \"verb\": \"fix\",\n      \"target\": \"login page authentication bug\",\n      \"direction\": \"do\",\n      \"original_text\": \"Fix the login bug\",\n      \"enriched_text\": \"Fix the login page authentication bug\",\n      \"reasoning\": \"Imperative sentence — actor is implicit (caller). Target is the login bug.\",\n      \"citation\": \"Fix the login bug\",\n      \"source_turn\": \"current\",\n      \"context\": {\"priority\": \"high\", \"complexity\": \"medium\", \"timing\": null, \"deadline\": \"Friday\", \"tags\": [\"bugfix\", \"auth\"]}\n    },\n    {\n      \"id\": \"int_f6e5d4c3b2a1\",\n      \"type\": \"action\",\n      \"actor\": \"caller\",\n      \"verb\": \"update\",\n      \"target\": \"project documentation\",\n      \"direction\": \"do\",\n      \"original_text\": \"update the docs by Friday\",\n      \"enriched_text\": \"Update the project documentation by Friday\",\n      \"reasoning\": \"Imperative task with stated deadline. Actor is implicit (caller).\",\n      \"citation\": \"update the docs by Friday\",\n      \"source_turn\": \"current\",\n      \"context\": {\"priority\": \"medium\", \"complexity\": \"low\", \"timing\": null, \"deadline\": \"Friday\", \"tags\": [\"docs\"]}\n    }\n  ],\n  \"tokens_used\": 512,\n  \"model_used\": \"anthropic/claude-sonnet-4.5\",\n  \"mode\": \"quick\",\n  \"quality_scoring_applied\": false\n}\n```\n\n**Example 2 — Multi-turn extraction with conversation history:**\n\n```json\nPOST /api/v2/tools/intents/extract\n{\n  \"text\": \"Ok lets do that. Also handle the edge cases you mentioned.\",\n  \"messages\": [\n    {\"role\": \"user\", \"content\": \"The checkout page throws 500 errors on discount codes\"},\n    {\"role\": \"assistant\", \"content\": \"We should add input sanitization. Watch out for empty fields and concurrent redemptions.\"},\n    {\"role\": \"user\", \"content\": \"We also need rate limiting. Deadline is Wednesday.\"}\n  ]\n}\n```\n\nIntents are extracted from ALL turns — not just the latest message.\nEach intent has a `source_turn` tracing it back (e.g. `\"user:1\"`,\n`\"assistant:2\"`, `\"current\"`).  The model resolves \"that\" and \"edge\ncases you mentioned\" to specific items from earlier turns.\n\n**Example 3 — Robust mode + quality scoring (all 3 LLM passes):**\n\n```json\nPOST /api/v2/tools/intents/extract\n{\n  \"text\": \"We need SSO support. Must use SAML. Reduce onboarding time by 40%.\",\n  \"mode\": \"robust\",\n  \"include_quality_score\": true\n}\n```\n\nResponse (truncated for brevity):\n```json\n{\n  \"schema_version\": \"1.0\",\n  \"message_type\": \"mixed\",\n  \"intents\": [\n    {\n      \"id\": \"int_abc123def456\",\n      \"type\": \"requirement\",\n      \"actor\": \"engineering team\",\n      \"verb\": \"implement\",\n      \"target\": \"Single Sign-On (SSO) support\",\n      \"direction\": \"do\",\n      \"original_text\": \"We need SSO support\",\n      \"enriched_text\": \"The engineering team needs to implement SSO support\",\n      \"reasoning\": \"Stated feature need. Actor inferred as engineering team from 'We'.\",\n      \"citation\": \"We need SSO support\",\n      \"source_turn\": \"current\",\n      \"audit_result\": {\n        \"classification\": \"supported\",\n        \"supporting_evidence\": \"We need SSO support\",\n        \"notes\": \"Directly stated in input\"\n      },\n      \"quality_score\": {\n        \"overall_score\": 0.85,\n        \"dimensions\": {\n          \"citation_support\": {\"category\": \"exceptional\", \"score\": 1.0},\n          \"specificity\": {\"category\": \"high\", \"score\": 0.75},\n          \"completeness\": {\"category\": \"high\", \"score\": 0.75},\n          \"type_correctness\": {\"category\": \"exceptional\", \"score\": 1.0},\n          \"unambiguity\": {\"category\": \"high\", \"score\": 0.75}\n        }\n      }\n    },\n    {\n      \"id\": \"int_789ghi012jkl\",\n      \"type\": \"constraint\",\n      \"actor\": \"engineering team\",\n      \"verb\": \"use\",\n      \"target\": \"SAML protocol\",\n      \"direction\": \"enforce\",\n      \"original_text\": \"Must use SAML\",\n      \"enriched_text\": \"The SSO implementation must use the SAML protocol\",\n      \"reasoning\": \"Technical constraint on implementation. Actor must comply.\",\n      \"citation\": \"Must use SAML\",\n      \"source_turn\": \"current\",\n      \"audit_result\": {\"classification\": \"supported\", \"supporting_evidence\": \"Must use SAML\", \"notes\": \"Directly stated\"},\n      \"quality_score\": {\"overall_score\": 0.9, \"dimensions\": {\"citation_support\": {\"category\": \"exceptional\", \"score\": 1.0}, \"specificity\": {\"category\": \"exceptional\", \"score\": 1.0}, \"completeness\": {\"category\": \"high\", \"score\": 0.75}, \"type_correctness\": {\"category\": \"exceptional\", \"score\": 1.0}, \"unambiguity\": {\"category\": \"high\", \"score\": 0.75}}}\n    },\n    {\n      \"id\": \"int_mno345pqr678\",\n      \"type\": \"goal\",\n      \"actor\": \"engineering team\",\n      \"verb\": \"reduce\",\n      \"target\": \"user onboarding time\",\n      \"direction\": \"do\",\n      \"original_text\": \"Reduce onboarding time by 40%\",\n      \"enriched_text\": \"Reduce user onboarding time by 40%\",\n      \"reasoning\": \"Measurable outcome target. Actor is the team responsible.\",\n      \"citation\": \"Reduce onboarding time by 40%\",\n      \"source_turn\": \"current\",\n      \"audit_result\": {\"classification\": \"supported\", \"supporting_evidence\": \"Reduce onboarding time by 40%\", \"notes\": \"Directly stated\"},\n      \"quality_score\": {\"overall_score\": 0.95, \"dimensions\": {\"citation_support\": {\"category\": \"exceptional\", \"score\": 1.0}, \"specificity\": {\"category\": \"exceptional\", \"score\": 1.0}, \"completeness\": {\"category\": \"exceptional\", \"score\": 1.0}, \"type_correctness\": {\"category\": \"exceptional\", \"score\": 1.0}, \"unambiguity\": {\"category\": \"high\", \"score\": 0.75}}}\n    }\n  ],\n  \"tokens_used\": 1847,\n  \"model_used\": \"anthropic/claude-sonnet-4.5\",\n  \"mode\": \"robust\",\n  \"quality_scoring_applied\": true\n}\n```\n\n**How Callers Use Intents**\n\nThe structured output is designed for programmatic consumption:\n\n- **Task routing**: Filter by `direction` — route `\"do\"` intents to task\n  queues, `\"answer\"` to Q&A handlers, `\"enforce\"` to policy checkers.\n- **Agent orchestration**: Use `actor` to assign intents to the right\n  handler — `\"caller\"` = current agent, named roles = route to specialists.\n- **Workflow generation**: Convert `\"do\"` intents into workflow steps,\n  using `target` as the artifact and `verb` as the operation.\n- **Compliance**: Use `\"enforce\"` intents as validation rules against\n  other intents in the same extraction.\n\n**Future: Advanced Intents**\n\nA future `POST /api/v2/tools/intents/extract-advanced` endpoint is planned\nto support northstar alignment (checking intents against project goals) and\npolicy enforcement (validating intents against organisational rules).\nThe schema version will be bumped when this is introduced.","operationId":"extract_intents_endpoint_api_v2_tools_intents_extract_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentExtractRequest"}}}},"responses":{"200":{"description":"Intents extracted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentExtractResponse"}}}},"400":{"description":"Invalid request — empty text or invalid mode"},"401":{"description":"Authentication required — missing or invalid API key"},"500":{"description":"LLM extraction failed"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tools/intents/align":{"post":{"tags":["tools","tools"],"summary":"Align Intents Endpoint","description":"Evaluate extracted intents against goals and policies.\n\nTakes a list of extracted intents (from the extract endpoint) and evaluates\neach one against the provided goals and optional policies using LLM-powered\nanalysis.\n\nEach intent receives an alignment classification:\n- `aligned` — directly supports one or more goals\n- `unaligned` — conflicts with one or more goals\n- `unclear` — relationship to goals is ambiguous\n- `not_applicable` — neutral (e.g. factual references, questions)\n\nIf policies are provided, the LLM also checks for policy violations.\nAn intent can be aligned with goals but still violate a policy.\n\nThis is the **Align** stage of the EADC (Extract → Align → Decide → Clarify)\nintent processing loop.\n\n**Example:**\n\n```json\nPOST /api/v2/tools/intents/align\n{\n  \"intents\": [<extracted intents from /intents/extract>],\n  \"goals\": [\"Improve user onboarding\", \"Reduce support tickets by 30%\"],\n  \"policies\": [\"No deployments on Fridays\"]\n}\n```","operationId":"align_intents_endpoint_api_v2_tools_intents_align_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentAlignRequest"}}}},"responses":{"200":{"description":"Intents aligned successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentAlignResponse"}}}},"401":{"description":"Authentication required — missing or invalid API key"},"422":{"description":"Validation error — empty intents or goals"},"500":{"description":"LLM alignment failed"}}}},"/api/v2/tools/intents/decide":{"post":{"tags":["tools","tools"],"summary":"Decide Intents Endpoint","description":"Apply deterministic decision matrix to intents.\n\nTakes intents with safety and alignment classifications and produces\nan action for each one using a deterministic matrix — no LLM call.\n\n**Decision Matrix (safety × alignment → action):**\n\n| | aligned | unaligned | unclear | not_applicable |\n|---|---|---|---|---|\n| **safe** | execute | confirm_and_ask_why | ask_why | execute |\n| **review** | confirm | confirm_and_ask_why | confirm_and_ask_why | confirm |\n| **unsafe** | reject | reject | reject | reject |\n\n**Additional rules:**\n- If confidence < `auto_execute_threshold` (default 0.7), `execute` → `confirm`\n- Non-execute actions include a `clarification_prompt` for the user\n- `executor_policy` allows overriding thresholds and behavior\n\nThis is the **Decide** stage of the EADC (Extract → Align → Decide → Clarify)\nintent processing loop. `tokens_used` is always 0.\n\n**Example:**\n\n```json\nPOST /api/v2/tools/intents/decide\n{\n  \"intents\": [\n    {\"intent_id\": \"int_abc123\", \"safety\": \"safe\", \"alignment\": \"aligned\", \"confidence\": 0.95, \"enriched_text\": \"Fix the login bug\"}\n  ]\n}\n```","operationId":"decide_intents_endpoint_api_v2_tools_intents_decide_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentDecideRequest"}}}},"responses":{"200":{"description":"Decisions computed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentDecideResponse"}}}},"401":{"description":"Authentication required — missing or invalid API key"},"422":{"description":"Validation error — empty intents"},"500":{"description":"Decision computation failed"}}}},"/api/v2/projects":{"post":{"tags":["projects"],"summary":"Create Project","description":"Create a new project.\n\nCreates a new project with the authenticated user as the owner. Projects are containers\nfor files, chat conversations, and SOP execution. Each user can create multiple projects\nfor different use cases (e.g., research, documentation, client work).\n\n**Prerequisites:**\n- Valid API key required\n- User must be authenticated\n\n**Request Body:**\n- `name` (required): Project name (1-255 characters)\n- `description` (optional): Project description\n- `is_default` (optional): Mark as default project (one per user)\n\n**Example Request:**\n```json\n{\n    \"name\": \"Research Project\",\n    \"description\": \"Market analysis and competitor research\"\n}\n```\n\n**Example Response (201):**\n```json\n{\n    \"id\": \"prj_1a2b3c4d5e6f\",\n    \"owner_id\": \"usr_9z8y7x6w5v\",\n    \"name\": \"Research Project\",\n    \"description\": \"Market analysis and competitor research\",\n    \"is_default\": false,\n    \"created_at\": \"2026-01-29T10:30:00Z\",\n    \"updated_at\": \"2026-01-29T10:30:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 422: Validation error - name is required or exceeds 255 characters\n\n**Related Endpoints:**\n- GET /projects - List all projects\n- GET /projects/{project_id} - Get project details\n- PATCH /projects/{project_id} - Update project","operationId":"create_project_api_v2_projects_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation error - name is required"}}},"get":{"tags":["projects"],"summary":"List Projects","description":"List all accessible projects.\n\nReturns all projects the user owns or has been granted permission to access.\nProjects are returned in descending order by last update time (most recently\nupdated first).\n\n**Prerequisites:**\n- Valid API key required\n\n**Path Parameters:**\nNone\n\n**Example Request:**\n```bash\nGET /api/v2/projects\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n[\n    {\n        \"id\": \"prj_1a2b3c4d5e6f\",\n        \"owner_id\": \"usr_9z8y7x6w5v\",\n        \"name\": \"Research Project\",\n        \"description\": \"Market analysis and competitor research\",\n        \"is_default\": false,\n        \"created_at\": \"2026-01-29T10:30:00Z\",\n        \"updated_at\": \"2026-01-29T15:45:00Z\"\n    },\n    {\n        \"id\": \"prj_7h8i9j0k1l2m\",\n        \"owner_id\": \"usr_9z8y7x6w5v\",\n        \"name\": \"Personal\",\n        \"description\": \"Default project for personal files\",\n        \"is_default\": true,\n        \"created_at\": \"2026-01-15T08:00:00Z\",\n        \"updated_at\": \"2026-01-20T12:30:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n\n**Related Endpoints:**\n- POST /projects - Create new project\n- GET /projects/{project_id} - Get specific project details","operationId":"list_projects_api_v2_projects_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Project"},"title":"Response List Projects Api V2 Projects Get"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/projects/{project_id}":{"get":{"tags":["projects"],"summary":"Get Project","description":"Get project details by ID.\n\nRetrieves detailed information about a specific project. The user must be the\nproject owner or have been granted permission to access it.\n\n**Prerequisites:**\n- Valid API key required\n- User must own the project or have been granted access\n\n**Path Parameters:**\n- `project_id` (required): Project ID (e.g., prj_1a2b3c4d5e6f)\n\n**Example Request:**\n```bash\nGET /api/v2/projects/prj_1a2b3c4d5e6f\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"id\": \"prj_1a2b3c4d5e6f\",\n    \"owner_id\": \"usr_9z8y7x6w5v\",\n    \"name\": \"Research Project\",\n    \"description\": \"Market analysis and competitor research\",\n    \"is_default\": false,\n    \"created_at\": \"2026-01-29T10:30:00Z\",\n    \"updated_at\": \"2026-01-29T15:45:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: Project not found or user lacks access permission\n\n**Related Endpoints:**\n- GET /projects - List all accessible projects\n- PATCH /projects/{project_id} - Update project\n- DELETE /projects/{project_id} - Delete project\n- GET /projects/{project_id}/permissions - List project permissions","operationId":"get_project_api_v2_projects__project_id__get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Project not found or access denied"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["projects"],"summary":"Update Project","description":"Update project details.\n\nUpdates a project's name and/or description. Only the project owner or users\nwith editor permission can update project details. At least one field must be\nprovided in the request body.\n\n**Prerequisites:**\n- Valid API key required\n- User must be project owner or have editor permission\n\n**Path Parameters:**\n- `project_id` (required): Project ID to update\n\n**Request Body:**\n- `name` (optional): New project name (1-255 characters)\n- `description` (optional): New project description\n\n**Example Request:**\n```json\n{\n    \"name\": \"Updated Research Project\",\n    \"description\": \"Expanded to include competitive analysis and market trends\"\n}\n```\n\n**Example Response (200):**\n```json\n{\n    \"id\": \"prj_1a2b3c4d5e6f\",\n    \"owner_id\": \"usr_9z8y7x6w5v\",\n    \"name\": \"Updated Research Project\",\n    \"description\": \"Expanded to include competitive analysis and market trends\",\n    \"is_default\": false,\n    \"created_at\": \"2026-01-29T10:30:00Z\",\n    \"updated_at\": \"2026-01-29T16:20:00Z\"\n}\n```\n\n**Error Responses:**\n- 400: No fields provided to update\n- 401: Missing or invalid API key\n- 403: User lacks owner or editor permission\n- 422: Validation error - name exceeds 255 characters or is empty\n\n**Related Endpoints:**\n- GET /projects/{project_id} - Get project details\n- DELETE /projects/{project_id} - Delete project\n- GET /projects/{project_id}/permissions - Manage permissions","operationId":"update_project_api_v2_projects__project_id__patch","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"No fields provided to update"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Only project owner or editor can update"},"422":{"description":"Validation error - invalid field values"}}},"delete":{"tags":["projects"],"summary":"Delete Project","description":"Delete a project.\n\nPermanently deletes a project. Only the project owner can delete projects.\nThe default \"Personal\" project cannot be deleted. Projects with active\nresources (agent runs, SOP runs, invocations) cannot be deleted until\nthose resources are completed or cancelled.\n\n**Prerequisites:**\n- Valid API key required\n- User must be project owner\n- Project cannot be the \"Personal\" project\n- Project must not have active agent runs, SOP runs, or invocations\n\n**Path Parameters:**\n- `project_id` (required): Project ID to delete\n\n**Example Request:**\n```bash\nDELETE /api/v2/projects/prj_1a2b3c4d5e6f\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (204):**\nNo content returned on successful deletion.\n\n**Error Responses:**\n- 400: Attempting to delete the \"Personal\" project (not allowed)\n- 401: Missing or invalid API key\n- 403: User is not the project owner\n- 404: Project not found\n- 409: Project has active resources (agent runs, SOP runs, or invocations)\n  - For agent runs: Cancel or wait for completion, then retry\n  - For SOP runs: Cancel or wait for completion, then retry\n  - For invocations: Cancel pending invocations, then retry\n\n**Related Endpoints:**\n- GET /projects/{project_id} - Get project details\n- POST /jobs/{job_id}/cancel - Cancel active jobs/runs","operationId":"delete_project_api_v2_projects__project_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"400":{"description":"Cannot delete Personal project"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Only project owner can delete"},"404":{"description":"Project not found"},"409":{"description":"Cannot delete project with active resources","content":{"application/json":{"example":{"error":{"type":"conflict","message":"Cannot delete project with active agent runs","details":{"project_id":"prj_1a2b3c4d5e6f"},"hint":"Cancel active agent runs or wait for them to complete, then retry."}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/projects/{project_id}/permissions":{"post":{"tags":["projects"],"summary":"Grant Permission","description":"Grant project permission to a user.\n\nGrants access permission to another user for this project. Only the project\nowner can grant permissions. If a permission already exists for the user,\nit will be updated with the new role.\n\nRoles:\n- owner: Full control including permission management\n- editor: Can modify project, upload files, create chats\n- viewer: Read-only access to project contents\n\n**Prerequisites:**\n- Valid API key required\n- User must be project owner\n- Target user must exist in the system\n\n**Path Parameters:**\n- `project_id` (required): Project ID to grant permission for\n\n**Request Body:**\n- `user_id` (required): User ID to grant permission to\n- `role` (required): Permission role - \"owner\", \"editor\", or \"viewer\"\n\n**Example Request:**\n```json\n{\n    \"user_id\": \"usr_5t4r3e2w1q\",\n    \"role\": \"editor\"\n}\n```\n\n**Example Response (201):**\n```json\n{\n    \"id\": \"perm_9x8c7v6b5n\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"user_id\": \"usr_5t4r3e2w1q\",\n    \"role\": \"editor\",\n    \"granted_by\": \"usr_9z8y7x6w5v\",\n    \"created_at\": \"2026-01-29T16:45:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User is not the project owner\n- 404: Target user does not exist\n- 422: Invalid role (must be owner, editor, or viewer)\n\n**Related Endpoints:**\n- GET /projects/{project_id}/permissions - List all permissions\n- PATCH /projects/{project_id}/permissions/{user_id} - Update permission\n- DELETE /projects/{project_id}/permissions/{user_id} - Revoke permission","operationId":"grant_permission_api_v2_projects__project_id__permissions_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectPermissionCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectPermission"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Only project owner can grant permissions"},"404":{"description":"User not found"},"422":{"description":"Validation error - invalid role or missing fields"}}},"get":{"tags":["projects"],"summary":"List Permissions","description":"List all project permissions.\n\nReturns all permissions granted for a specific project. Users can view\npermissions for projects they own or have access to. Permissions are\nreturned in descending order by creation time (most recent first).\n\n**Prerequisites:**\n- Valid API key required\n- User must own the project or have been granted access\n\n**Path Parameters:**\n- `project_id` (required): Project ID to list permissions for\n\n**Example Request:**\n```bash\nGET /api/v2/projects/prj_1a2b3c4d5e6f/permissions\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n[\n    {\n        \"id\": \"perm_9x8c7v6b5n\",\n        \"project_id\": \"prj_1a2b3c4d5e6f\",\n        \"user_id\": \"usr_5t4r3e2w1q\",\n        \"role\": \"editor\",\n        \"granted_by\": \"usr_9z8y7x6w5v\",\n        \"created_at\": \"2026-01-29T16:45:00Z\"\n    },\n    {\n        \"id\": \"perm_2w1q3e4r5t\",\n        \"project_id\": \"prj_1a2b3c4d5e6f\",\n        \"user_id\": \"usr_7y6u5i4o3p\",\n        \"role\": \"viewer\",\n        \"granted_by\": \"usr_9z8y7x6w5v\",\n        \"created_at\": \"2026-01-28T14:20:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: Project not found or user lacks access\n\n**Related Endpoints:**\n- POST /projects/{project_id}/permissions - Grant permission\n- PATCH /projects/{project_id}/permissions/{user_id} - Update permission\n- DELETE /projects/{project_id}/permissions/{user_id} - Revoke permission","operationId":"list_permissions_api_v2_projects__project_id__permissions_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectPermission"},"title":"Response List Permissions Api V2 Projects  Project Id  Permissions Get"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Project not found or access denied"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/projects/{project_id}/permissions/{user_id}":{"patch":{"tags":["projects"],"summary":"Update Permission","description":"Update a user's permission level.\n\nChanges the role for an existing project permission. Only the project owner\ncan update permissions. Use this to promote a viewer to editor, or demote\nan editor to viewer.\n\n**Prerequisites:**\n- Valid API key required\n- User must be project owner\n- Permission must already exist for the target user\n\n**Path Parameters:**\n- `project_id` (required): Project ID\n- `user_id` (required): User ID whose permission to update\n\n**Request Body:**\n- `role` (required): New permission role - \"owner\", \"editor\", or \"viewer\"\n\n**Example Request:**\n```json\n{\n    \"role\": \"viewer\"\n}\n```\n\n**Example Response (200):**\n```json\n{\n    \"id\": \"perm_9x8c7v6b5n\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"user_id\": \"usr_5t4r3e2w1q\",\n    \"role\": \"viewer\",\n    \"granted_by\": \"usr_9z8y7x6w5v\",\n    \"created_at\": \"2026-01-29T16:45:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User is not the project owner\n- 404: No permission exists for this user\n- 422: Invalid role (must be owner, editor, or viewer)\n\n**Related Endpoints:**\n- POST /projects/{project_id}/permissions - Grant new permission\n- GET /projects/{project_id}/permissions - List all permissions\n- DELETE /projects/{project_id}/permissions/{user_id} - Revoke permission","operationId":"update_permission_api_v2_projects__project_id__permissions__user_id__patch","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectPermissionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectPermission"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Only project owner can update permissions"},"404":{"description":"Permission not found"},"422":{"description":"Validation error - invalid role"}}},"delete":{"tags":["projects"],"summary":"Revoke Permission","description":"Revoke a user's permission.\n\nPermanently removes a user's access permission to the project. Only the\nproject owner can revoke permissions. After revocation, the user will no\nlonger be able to access the project.\n\n**Prerequisites:**\n- Valid API key required\n- User must be project owner\n- Permission must exist for the target user\n\n**Path Parameters:**\n- `project_id` (required): Project ID\n- `user_id` (required): User ID whose permission to revoke\n\n**Example Request:**\n```bash\nDELETE /api/v2/projects/prj_1a2b3c4d5e6f/permissions/usr_5t4r3e2w1q\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (204):**\nNo content returned on successful revocation.\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User is not the project owner\n- 404: No permission exists for this user\n\n**Related Endpoints:**\n- POST /projects/{project_id}/permissions - Grant new permission\n- GET /projects/{project_id}/permissions - List all permissions\n- PATCH /projects/{project_id}/permissions/{user_id} - Update permission","operationId":"revoke_permission_api_v2_projects__project_id__permissions__user_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Only project owner can revoke permissions"},"404":{"description":"Permission not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/projects/{project_id}/export":{"post":{"tags":["projects"],"summary":"Export Project","description":"Export project data as JSON.\n\nSES-114: Gathers project metadata, files, and chat history into a single\nJSON response. For async export of large projects, wrap in an http_call job:\n\n```bash\nPOST /api/v2/jobs\n{\n  \"job_type\": \"http_call\",\n  \"project_id\": \"prj-...\",\n  \"parameters\": {\n    \"method\": \"POST\",\n    \"endpoint\": \"/api/v2/projects/prj-.../export?format=json\"\n  }\n}\n```\n\nArgs:\n    project_id: Project to export\n    format: Export format (only \"json\" supported)\n    include_files: Whether to include file metadata\n    include_chats: Whether to include chat conversations\n    current_user: Authenticated user (injected)\n\nReturns:\n    ProjectExportResponse with project data\n\nRaises:\n    401: Authentication required\n    403: No access to project\n    404: Project not found\n    400: Unsupported export format","operationId":"export_project_api_v2_projects__project_id__export_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","description":"Export format (currently only 'json' supported)","default":"json","title":"Format"},"description":"Export format (currently only 'json' supported)"},{"name":"include_files","in":"query","required":false,"schema":{"type":"boolean","description":"Include file metadata in export","default":true,"title":"Include Files"},"description":"Include file metadata in export"},{"name":"include_chats","in":"query","required":false,"schema":{"type":"boolean","description":"Include chat history in export","default":true,"title":"Include Chats"},"description":"Include chat history in export"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectExportResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"No permission to export this project"},"404":{"description":"Project not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/files":{"post":{"tags":["files"],"summary":"Upload File","description":"Upload a file to a project.\n\n**SES-105: Explicit Processing Control**\n\nBy default, file upload is **fast and synchronous** - the file is stored and immediately\navailable for download. No background processing occurs.\n\nTo enable processing (RAG chunking, indexing), use the `?process=true` query parameter.\nThis will **block** until processing completes (typically 1-30 seconds depending on file size).\n\n## Processing Options\n\n- **?process=false** (default): Upload only, no processing (fast, ~100ms)\n- **?process=true**: Upload + synchronous processing (slower, 1-30s)\n\nFor async processing, use the separate `/files/{id}/process` endpoint or wrap it in a Job.\n\n## Duplicate File Handling (SES-214)\n\nBy default, uploading a file with the same name as an existing file returns 409 Conflict.\nUse `?overwrite=true` to replace the existing file.\n\nArgs:\n    file: The file to upload (multipart/form-data)\n    project_id: Project ID to upload to. If not specified, uses user's default project.\n                System admin keys MUST specify project_id explicitly.\n    path: Virtual path within project (default: \"/\")\n    description: Optional file description\n    tags: Comma-separated list of tags\n    process: Process file immediately (default: false). **Blocks until complete if true.**\n    overwrite: Replace existing file with same name (default: false). See SES-214.\n\nReturns:\n    FileResponse with file metadata including:\n    - id: Unique file identifier for downloads\n    - storage_path: Physical storage location (for debugging)\n    - size_bytes: File size in bytes\n    - filename: Original filename\n\nRaises:\n    400: System admin key without project_id, or empty file\n    403: No permission to upload to specified project\n    409: File with same name already exists (use overwrite=true to replace)\n    413: File exceeds quota limits\n\nExample:\n    ```bash\n    # Fast upload (no processing)\n    curl -X POST https://api.taiso.ai/api/v2/files \\\n      -H \"Authorization: Bearer YOUR_API_KEY\" \\\n      -F \"file=@document.pdf\" \\\n      -F \"project_id=prj-abc123\"\n\n    # Upload with immediate processing (blocks until done)\n    curl -X POST \"https://api.taiso.ai/api/v2/files?process=true\" \\\n      -H \"Authorization: Bearer YOUR_API_KEY\" \\\n      -F \"file=@document.pdf\" \\\n      -F \"project_id=prj-abc123\"\n    ```\n\nNote: Files are immediately available for download after successful upload (status 201).\nProcessing (if enabled) happens before the response is returned.\n\nSee Also:\n    - POST /files/{id}/process - Process file separately (allows custom params)\n    - POST /jobs - Queue processing as async job","operationId":"upload_file_api_v2_files_post","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Project Id"}},{"name":"process","in":"query","required":false,"schema":{"type":"boolean","description":"Process file immediately after upload (sync)","default":false,"title":"Process"},"description":"Process file immediately after upload (sync)"},{"name":"overwrite","in":"query","required":false,"schema":{"type":"boolean","description":"Replace existing file with same name (SES-214)","default":false,"title":"Overwrite"},"description":"Replace existing file with same name (SES-214)"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_file_api_v2_files_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileResponse"}}}},"400":{"description":"System admin key without project_id"},"401":{"description":"Missing or invalid API key"},"403":{"description":"No permission to upload to specified project"},"409":{"description":"File with same name already exists (SES-214)"},"413":{"description":"File exceeds quota limits"},"422":{"description":"Validation error - invalid parameters"}}},"get":{"tags":["files"],"summary":"List Files","description":"List files accessible to the user.\n\nReturns a paginated list of files the user has access to. Can be filtered by\nproject and/or path. If no project_id is specified, returns files from all\naccessible projects.\n\n**Prerequisites:**\n- Valid API key required\n\n**Query Parameters:**\n- `project_id` (optional): Filter files by project ID\n- `path` (optional): Filter files by virtual path (e.g., \"/documents\")\n- `limit` (optional): Maximum files to return (default: 100, max: 1000)\n- `offset` (optional): Number of files to skip for pagination (default: 0)\n\n**Example Request:**\n```bash\nGET /api/v2/files?project_id=prj_1a2b3c4d5e6f&limit=50&offset=0\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"files\": [\n        {\n            \"id\": \"fil_9z8y7x6w5v\",\n            \"project_id\": \"prj_1a2b3c4d5e6f\",\n            \"filename\": \"market-analysis.pdf\",\n            \"path\": \"/\",\n            \"full_path\": \"/market-analysis.pdf\",\n            \"file_type\": \"pdf\",\n            \"mime_type\": \"application/pdf\",\n            \"size_bytes\": 245760,\n            \"storage_path\": \"projects/prj_1a2b3c4d5e6f/fil_9z8y7x6w5v\",\n            \"description\": \"Q4 market analysis report\",\n            \"tags\": [\"research\", \"quarterly\"],\n            \"created_by_job\": null,\n            \"updated_by_job\": null,\n            \"job_type\": null,\n            \"run_id\": null,\n            \"created_at\": \"2026-01-29T10:30:00Z\",\n            \"updated_at\": \"2026-01-29T10:30:00Z\"\n        }\n    ],\n    \"total\": 1,\n    \"limit\": 50,\n    \"offset\": 0\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n\n**Related Endpoints:**\n- POST /files - Upload new file\n- GET /files/{file_id} - Get file metadata\n- GET /files/{file_id}/download - Download file content","operationId":"list_files_api_v2_files_get","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Project Id"}},{"name":"path","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Path"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/files/{file_id}":{"get":{"tags":["files"],"summary":"Get File Metadata","description":"Get file metadata.\n\nRetrieves metadata for a specific file without downloading the content.\nUse this to check file properties, size, type, and associated tags before\ndownloading.\n\n**Prerequisites:**\n- Valid API key required\n- User must have access to the file's project\n\n**Path Parameters:**\n- `file_id` (required): File ID (e.g., fil_9z8y7x6w5v)\n\n**Example Request:**\n```bash\nGET /api/v2/files/fil_9z8y7x6w5v\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"id\": \"fil_9z8y7x6w5v\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"filename\": \"market-analysis.pdf\",\n    \"path\": \"/\",\n    \"full_path\": \"/market-analysis.pdf\",\n    \"file_type\": \"pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"size_bytes\": 245760,\n    \"storage_path\": \"projects/prj_1a2b3c4d5e6f/fil_9z8y7x6w5v\",\n    \"description\": \"Q4 market analysis report\",\n    \"tags\": [\"research\", \"quarterly\"],\n    \"created_by_job\": null,\n    \"updated_by_job\": null,\n    \"job_type\": null,\n    \"run_id\": null,\n    \"created_at\": \"2026-01-29T10:30:00Z\",\n    \"updated_at\": \"2026-01-29T10:30:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to access the file's project\n- 404: File not found or file_id is invalid\n\n**Related Endpoints:**\n- GET /files/{file_id}/download - Download file content\n- POST /files - Upload new file\n- DELETE /files/{file_id} - Delete file","operationId":"get_file_metadata_api_v2_files__file_id__get","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"No permission to access this file"},"404":{"description":"File not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["files"],"summary":"Delete File","description":"Delete a file.\n\nPermanently deletes a file and its associated content from storage. The user\nmust have editor or owner permission on the file's project. This action cannot\nbe undone.\n\n**Prerequisites:**\n- Valid API key required\n- User must have editor or owner permission on the file's project\n\n**Path Parameters:**\n- `file_id` (required): File ID to delete\n\n**Example Request:**\n```bash\nDELETE /api/v2/files/fil_9z8y7x6w5v\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (204):**\nNo content returned on successful deletion.\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to delete files in this project\n- 404: File not found or already deleted\n\n**Related Endpoints:**\n- GET /files/{file_id} - Get file metadata\n- POST /files - Upload new file\n- GET /files - List files","operationId":"delete_file_api_v2_files__file_id__delete","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid API key"},"403":{"description":"No permission to delete this file"},"404":{"description":"File not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/files/{file_id}/download":{"get":{"tags":["files"],"summary":"Download File","description":"Download file content.\n\nArgs:\n    file_id: Unique file identifier from upload response\n\nReturns:\n    StreamingResponse with file content and headers:\n    - Content-Disposition: attachment; filename=\"original-name.ext\"\n    - Content-Type: Original MIME type\n    - Content-Length: File size in bytes\n\nRaises:\n    401: Missing or invalid authentication\n    403: No permission to access this file\n    404: File not found or file_id invalid\n\nExample:\n    ```bash\n    # Get file_id from upload response\n    curl -X POST https://api.taiso.ai/api/v2/files \\\n      -H \"Authorization: Bearer YOUR_API_KEY\" \\\n      -F \"file=@doc.pdf\" \\\n      -F \"project_id=prj-abc123\"\n    # Response: {\"id\": \"fil-xyz789\", ...}\n\n    # Download the file\n    curl https://api.taiso.ai/api/v2/files/fil-xyz789/download \\\n      -H \"Authorization: Bearer YOUR_API_KEY\" \\\n      --output document.pdf\n    ```\n\nNote: Files are available immediately after upload (no delay).\nIf you receive 404, verify:\n1. file_id is correct (from upload response)\n2. You have permission to access the project\n3. File has not been deleted","operationId":"download_file_api_v2_files__file_id__download_get","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"No permission to access this file"},"404":{"description":"File not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/files/{file_id}/process":{"post":{"tags":["files"],"summary":"Process File","description":"Process a file: RAG chunking, Elasticsearch indexing, and metadata extraction.\n\n**SES-105: Explicit File Processing Pattern**\n\nThis endpoint allows explicit, on-demand processing of files. Processing includes:\n- **RAG Chunking**: Split text into chunks and generate embeddings for semantic search\n- **Elasticsearch Indexing**: Full-text search indexing\n- **Metadata Extraction**: Extract title, author, dates, and other metadata\n\nProcessing is **synchronous** (blocks until complete) and **idempotent** (safe to call\nmultiple times on the same file).\n\n## Use Cases\n\n- **Re-process after content changes**: If file content is updated externally\n- **Change chunking parameters**: Re-chunk with different size/overlap settings\n- **Retry failed processing**: If initial processing failed\n- **On-demand indexing**: Index files uploaded without processing\n\n## Processing Options\n\nAll processing options are optional and default to True:\n\n- `rag_chunking`: Enable RAG chunking and embedding generation\n- `elasticsearch_index`: Enable Elasticsearch full-text indexing\n- `extract_metadata`: Enable metadata extraction\n- `chunk_size`: Chunk size in characters (128-2048, default: 512)\n- `overlap`: Overlap between chunks in characters (0-512, default: 50)\n- `embedding_model`: Embedding model to use (default: text-embedding-3-small)\n\n## Performance\n\nProcessing time depends on file size and enabled options:\n- Small files (< 100KB): 1-5 seconds\n- Medium files (100KB-1MB): 5-15 seconds\n- Large files (> 1MB): 15-30 seconds\n\n**Note**: This endpoint blocks until processing completes. For async processing,\nwrap this call in the Jobs API:\n\n```bash\nPOST /api/v2/jobs\n{\n  \"job_type\": \"http_call\",\n  \"parameters\": {\n    \"method\": \"POST\",\n    \"endpoint\": \"/api/v2/files/{file_id}/process\"\n  }\n}\n```\n\nArgs:\n    file_id: File ID to process\n    process_request: Optional processing parameters (defaults to all enabled)\n    current_user: Authenticated user (injected)\n\nReturns:\n    FileProcessResponse with processing results:\n    - file_id: File that was processed\n    - status: \"completed\", \"indexed\", \"success\", or \"skipped\" (empty files)\n    - chunks_created: Number of RAG chunks created\n    - elasticsearch_indexed: Whether Elasticsearch indexing succeeded\n    - metadata_extracted: Whether metadata extraction succeeded\n    - processing_time_seconds: Total processing time\n    - message: Additional status message or warnings\n\nRaises:\n    401: Authentication required\n    403: User lacks read permission for this file\n    404: File not found\n    400: Invalid processing parameters\n\nExample:\n    ```bash\n    # Process with default settings\n    curl -X POST https://api.taiso.ai/api/v2/files/file-abc123/process \\\n      -H \"Authorization: Bearer YOUR_API_KEY\"\n\n    # Process with custom chunking\n    curl -X POST https://api.taiso.ai/api/v2/files/file-abc123/process \\\n      -H \"Authorization: Bearer YOUR_API_KEY\" \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\n        \"rag_chunking\": true,\n        \"elasticsearch_index\": true,\n        \"chunk_size\": 1024,\n        \"overlap\": 100\n      }'\n\n    # Process only RAG (skip Elasticsearch)\n    curl -X POST https://api.taiso.ai/api/v2/files/file-abc123/process \\\n      -H \"Authorization: Bearer YOUR_API_KEY\" \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\n        \"rag_chunking\": true,\n        \"elasticsearch_index\": false\n      }'\n    ```\n\nSee Also:\n    - POST /files?process=true - Upload with immediate processing\n    - POST /jobs - Queue processing as async job","operationId":"process_file_api_v2_files__file_id__process_post","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FileProcessRequest"},{"type":"null"}],"title":"Process Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileProcessResponse"}}}},"400":{"description":"Invalid processing parameters"},"401":{"description":"Missing or invalid API key"},"403":{"description":"No permission to process this file"},"404":{"description":"File not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/chats":{"post":{"tags":["chats"],"summary":"Create Chat","description":"Create a new chat in a project.\n\nCreates a new chat conversation within a project context. If no project is\nspecified, the chat is automatically added to the user's Personal project.\nRequires editor permission on the project.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the project\n- Project must exist (or defaults to Personal project)\n\n**Request Body:**\n- `name` (string, required): Chat name (1-255 characters)\n- `project_id` (string, optional): Project ID (defaults to Personal project if omitted)\n\n**Example Request:**\n```json\n{\n    \"name\": \"Research Discussion\",\n    \"project_id\": \"prj_1234567890abcdef\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"chat_01JDX2P1ABCDEF\",\n    \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"name\": \"Research Discussion\",\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T10:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No write access to this project (requires editor permission)\n- 404: Project not found\n- 500: Personal project not found (system error)\n\n**Related Endpoints:**\n- GET /chats - List all accessible chats\n- GET /chats/{chat_id} - Get chat details\n- POST /chats/{chat_id}/messages - Add message to chat","operationId":"create_chat_api_v2_chats_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCreate"}}}},"responses":{"201":{"description":"Chat created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}},"401":{"description":"Authentication required"},"403":{"description":"No write access to this project"},"404":{"description":"Project not found"},"500":{"description":"Internal server error (Personal project not found)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chats"],"summary":"List Chats","description":"List all chats accessible to the user.\n\nReturns all chats in projects the user owns or has been granted access to.\nCan optionally filter to a specific project. Chats are ordered by most\nrecently updated first.\n\n**Prerequisites:**\n- User must be authenticated\n- For project filtering: User must have viewer permission on the project\n\n**Query Parameters:**\n- `project_id` (string, optional): Filter chats to specific project\n\n**Example Request:**\n```\nGET /chats?project_id=prj_1234567890abcdef\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"chat_01JDX2P1ABCDEF\",\n        \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n        \"project_id\": \"prj_1234567890abcdef\",\n        \"name\": \"Research Discussion\",\n        \"created_at\": \"2025-11-27T10:00:00Z\",\n        \"updated_at\": \"2025-11-27T14:30:00Z\"\n    },\n    {\n        \"id\": \"chat_01JDX2P2GHIJKL\",\n        \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n        \"project_id\": \"prj_1234567890abcdef\",\n        \"name\": \"Literature Review\",\n        \"created_at\": \"2025-11-26T09:00:00Z\",\n        \"updated_at\": \"2025-11-27T11:15:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No access to the specified project\n\n**Related Endpoints:**\n- POST /chats - Create new chat\n- GET /chats/{chat_id} - Get specific chat details\n- GET /chats/{chat_id}/messages - Get messages in a chat","operationId":"list_chats_api_v2_chats_get","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of chats retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Chat"},"title":"Response List Chats Api V2 Chats Get"}}}},"401":{"description":"Authentication required"},"403":{"description":"No access to this project"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/chats/{chat_id}":{"get":{"tags":["chats"],"summary":"Get Chat","description":"Get chat metadata.\n\nReturns metadata for a specific chat including name, owner, project,\nand timestamps. Does not include messages (use GET /chats/{chat_id}/messages\nfor messages). Requires viewer permission on the project.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer permission on the chat's project\n- Chat must exist\n\n**Path Parameters:**\n- `chat_id` (string, required): Chat identifier (prefixed with 'chat_')\n\n**Example Request:**\n```\nGET /chats/chat_01JDX2P1ABCDEF\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"chat_01JDX2P1ABCDEF\",\n    \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"name\": \"Research Discussion\",\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T14:30:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No access to this chat (user lacks viewer permission on project)\n- 404: Chat not found\n\n**Related Endpoints:**\n- GET /chats - List all chats\n- PATCH /chats/{chat_id} - Update chat metadata\n- DELETE /chats/{chat_id} - Delete chat\n- GET /chats/{chat_id}/messages - Get chat messages","operationId":"get_chat_api_v2_chats__chat_id__get","parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Chat details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}},"401":{"description":"Authentication required"},"403":{"description":"No access to this chat"},"404":{"description":"Chat not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chats"],"summary":"Delete Chat","description":"Delete a chat.\n\nPermanently deletes a chat and all its messages. Only users with editor\npermission on the project can delete chats. This action cannot be undone.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the chat's project\n- Chat must exist\n\n**Path Parameters:**\n- `chat_id` (string, required): Chat identifier (prefixed with 'chat_')\n\n**Example Request:**\n```\nDELETE /chats/chat_01JDX2P1ABCDEF\n```\n\n**Example Response:**\n```\nHTTP/1.1 204 No Content\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No write access to this project (requires editor permission)\n- 404: Chat not found\n\n**Related Endpoints:**\n- GET /chats/{chat_id} - Get chat details before deletion\n- GET /chats/{chat_id}/messages - View messages before deletion\n- PATCH /chats/{chat_id} - Update chat instead of deleting","operationId":"delete_chat_api_v2_chats__chat_id__delete","parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Chat deleted successfully"},"401":{"description":"Authentication required"},"403":{"description":"No write access to this project"},"404":{"description":"Chat not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["chats"],"summary":"Update Chat","description":"Update chat metadata.\n\nUpdates chat name or moves chat to a different project. Requires editor\npermission on the current project. If moving to a different project,\nalso requires editor permission on the target project.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the chat's current project\n- If changing project_id: User must have editor permission on target project\n- Chat must exist\n\n**Path Parameters:**\n- `chat_id` (string, required): Chat identifier (prefixed with 'chat_')\n\n**Request Body:**\nAll fields are optional - only provided fields will be updated.\n- `name` (string, optional): Updated chat name (1-255 characters)\n- `project_id` (string, optional): Move chat to different project\n\n**Example Request:**\n```json\n{\n    \"name\": \"Updated Research Discussion\",\n    \"project_id\": \"prj_9876543210fedcba\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"chat_01JDX2P1ABCDEF\",\n    \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"project_id\": \"prj_9876543210fedcba\",\n    \"name\": \"Updated Research Discussion\",\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T15:45:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No write access to source or target project\n- 404: Chat not found\n- 422: Invalid update (target project doesn't exist)\n\n**Related Endpoints:**\n- GET /chats/{chat_id} - Get current chat details\n- DELETE /chats/{chat_id} - Delete chat\n- GET /chats - List all chats","operationId":"update_chat_api_v2_chats__chat_id__patch","parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatUpdate"}}}},"responses":{"200":{"description":"Chat updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}},"401":{"description":"Authentication required"},"403":{"description":"No write access to source or target project"},"404":{"description":"Chat not found"},"422":{"description":"Invalid update (target project doesn't exist)"}}}},"/api/v2/chats/{chat_id}/messages":{"post":{"tags":["chats"],"summary":"Add Message","description":"Add a message to a chat.\n\nAppends a new message to the chat conversation. Messages are automatically\nindexed sequentially. Updates the chat's updated_at timestamp. Requires\neditor permission on the project.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the chat's project\n- Chat must exist\n\n**Path Parameters:**\n- `chat_id` (string, required): Chat identifier (prefixed with 'chat_')\n\n**Request Body:**\n- `content` (string, required): Message content (minimum 1 character)\n- `role` (string, optional): Message role (system, user, assistant, tool, default: 'user')\n- `tool_calls` (array[object], optional): Tool calls made by assistant\n- `tool_call_id` (string, optional): ID of tool call this message responds to\n- `use_rag` (boolean, optional): Enable RAG context retrieval (default: false)\n- `rag_config` (object, optional): RAG configuration\n\n**Example Request:**\n```json\n{\n    \"content\": \"What are the latest developments in mucosal healing?\",\n    \"role\": \"user\",\n    \"use_rag\": false\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"msg_01JDX2Q1ABCDEF\",\n    \"chat_id\": \"chat_01JDX2P1ABCDEF\",\n    \"message_index\": 0,\n    \"role\": \"user\",\n    \"content\": \"What are the latest developments in mucosal healing?\",\n    \"created_at\": \"2025-11-27T10:05:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No write access to this chat (requires editor permission on project)\n- 404: Chat not found\n\n**Related Endpoints:**\n- GET /chats/{chat_id}/messages - List all messages in chat\n- GET /chats/{chat_id} - Get chat metadata","operationId":"add_message_api_v2_chats__chat_id__messages_post","parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatMessageCreate"}}}},"responses":{"201":{"description":"Message added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatMessage-Output"}}}},"401":{"description":"Authentication required"},"403":{"description":"No write access to this chat"},"404":{"description":"Chat not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chats"],"summary":"Get Messages","description":"Get messages from a chat.\n\nReturns all messages in a chat conversation, ordered chronologically\n(oldest first). Supports pagination for long conversations. Requires\nviewer permission on the project.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer permission on the chat's project\n- Chat must exist\n\n**Path Parameters:**\n- `chat_id` (string, required): Chat identifier (prefixed with 'chat_')\n\n**Query Parameters:**\n- `limit` (integer, optional): Maximum messages to return (default: 100)\n- `offset` (integer, optional): Number of messages to skip (default: 0)\n\n**Example Request:**\n```\nGET /chats/chat_01JDX2P1ABCDEF/messages?limit=50&offset=0\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"msg_01JDX2Q1ABCDEF\",\n        \"chat_id\": \"chat_01JDX2P1ABCDEF\",\n        \"message_index\": 0,\n        \"role\": \"user\",\n        \"content\": \"What are the latest developments in mucosal healing?\",\n        \"created_at\": \"2025-11-27T10:05:00Z\"\n    },\n    {\n        \"id\": \"msg_01JDX2Q2GHIJKL\",\n        \"chat_id\": \"chat_01JDX2P1ABCDEF\",\n        \"message_index\": 1,\n        \"role\": \"assistant\",\n        \"content\": \"Recent research has shown that...\",\n        \"created_at\": \"2025-11-27T10:05:15Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: No access to this chat (user lacks viewer permission on project)\n- 404: Chat not found\n\n**Related Endpoints:**\n- POST /chats/{chat_id}/messages - Add new message to chat\n- GET /chats/{chat_id} - Get chat metadata\n- GET /chats - List all chats","operationId":"get_messages_api_v2_chats__chat_id__messages_get","parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Messages retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage-Output"},"title":"Response Get Messages Api V2 Chats  Chat Id  Messages Get"}}}},"401":{"description":"Authentication required"},"403":{"description":"No access to this chat"},"404":{"description":"Chat not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/search":{"get":{"tags":["search"],"summary":"Search","description":"Search across uploaded files and chat messages using hybrid semantic + keyword search.\n\nCombines PostgreSQL vector search (semantic similarity) with Elasticsearch text\nsearch (BM25 keyword ranking) using Reciprocal Rank Fusion for optimal relevance.\n\n**Prerequisites:**\n- Valid API key with project access\n- At least one accessible project with indexed content\n\n**Query Parameters:**\n- `q` (required): Search query (minimum 1 character)\n- `project_id` (optional): Limit search to specific project ID\n- `type` (optional): Filter by content type - \"files\", \"messages\", or \"all\" (default: \"all\")\n- `search_method` (optional): Search strategy - \"hybrid\", \"vector\", or \"text\" (default: \"hybrid\")\n- `limit` (optional): Maximum results to return, 1-100 (default: 20)\n\n**Example Request:**\n```bash\ncurl -X GET \"https://api.taiso.ai/api/v2/search?q=mucosal+barrier+function&type=files&limit=10\"       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"query\": \"mucosal barrier function\",\n  \"results\": [\n    {\n      \"type\": \"chunk\",\n      \"id\": \"fchunk-01JDX2R7PQRS...\",\n      \"project_id\": \"prj-1234567890abcdef\",\n      \"title\": \"mucosal_barrier_review.pdf\",\n      \"content\": \"The intestinal mucosal barrier is maintained by tight junctions, mucus, and immune factors...\",\n      \"score\": 0.92,\n      \"created_at\": \"2025-11-20T12:00:00Z\",\n      \"search_method\": \"hybrid\"\n    },\n    {\n      \"type\": \"message\",\n      \"id\": \"msg-98765432109876\",\n      \"project_id\": \"prj-1234567890abcdef\",\n      \"title\": \"Research Discussion\",\n      \"content\": \"We discussed the importance of mucosal barrier integrity in IBD patients...\",\n      \"score\": 0.85,\n      \"created_at\": \"2025-11-19T15:30:00Z\",\n      \"search_method\": \"hybrid\"\n    }\n  ],\n  \"total\": 2,\n  \"search_method\": \"hybrid\",\n  \"elasticsearch_available\": true\n}\n```\n\n**Search Methods:**\n\n**hybrid** (recommended):\n- Combines semantic (vector) and keyword (text) search\n- Uses Reciprocal Rank Fusion to merge results\n- Best overall relevance for most queries\n- Auto-fallback to PostgreSQL if Elasticsearch unavailable\n\n**vector**:\n- Pure semantic similarity search\n- Best for conceptual queries\n- Understands meaning, not just keywords\n- Uses PostgreSQL pgvector\n\n**text**:\n- Pure keyword/BM25 search\n- Best for exact term matching\n- Uses Elasticsearch (or PostgreSQL full-text search as fallback)\n\n**Content Types:**\n- `files`: Search only file chunks from uploaded documents\n- `messages`: Search only chat messages\n- `all`: Search both files and messages (default)\n\n**Result Fields:**\n- `type`: Content type (\"chunk\" for files, \"message\" for chats)\n- `id`: Unique identifier\n- `project_id`: Parent project ID\n- `title`: File name or chat name\n- `content`: Text snippet (truncated to 500 chars)\n- `score`: Relevance score 0.0-1.0 (higher is better)\n- `created_at`: ISO 8601 timestamp\n- `search_method`: Method used for this result\n\n**Graceful Degradation:**\n- If Elasticsearch unavailable: Falls back to PostgreSQL text search\n- If embeddings unavailable: Uses text-only search\n- Always returns results when possible\n\n**Performance Notes:**\n- Hybrid search: ~100-300ms typical\n- Vector-only search: ~50-150ms typical\n- Text-only search: ~50-100ms typical\n- Results are cached when possible\n\n**Error Responses:**\n\nNo Access to Project (403):\n```json\n{\n  \"detail\": \"No access to this project\"\n}\n```\n\nInvalid Query (400):\n```json\n{\n  \"detail\": \"Query must be at least 1 character\"\n}\n```\n\n**Use Cases:**\n- Find relevant documents for a topic\n- Search previous chat conversations\n- Discover related content across projects\n- RAG retrieval for agent workflows\n\n**Related Endpoints:**\n- `POST /api/v2/files` - Upload files to search\n- `POST /api/v2/jobs` - Create RAG indexing job\n- `POST /api/v2/chats/{id}/messages` - Add messages to search","operationId":"search_api_v2_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Search query","title":"Q"},"description":"Search query"},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Limit search to specific project","title":"Project Id"},"description":"Limit search to specific project"},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"enum":["files","messages","all"],"type":"string"},{"type":"null"}],"description":"Search type","default":"all","title":"Type"},"description":"Search type"},{"name":"search_method","in":"query","required":false,"schema":{"anyOf":[{"enum":["hybrid","vector","text"],"type":"string"},{"type":"null"}],"description":"Search method: hybrid (vector+text), vector (semantic only), text (keyword only)","default":"hybrid","title":"Search Method"},"description":"Search method: hybrid (vector+text), vector (semantic only), text (keyword only)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results","default":20,"title":"Limit"},"description":"Max results"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Search results with relevance ranking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"401":{"description":"Invalid or missing API key"},"403":{"description":"No access to specified project"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/structured-blocks/generate-schema":{"post":{"tags":["structured-blocks","structured-blocks"],"summary":"Generate Schema","description":"Generate JSON Schema from example data (Step 1 of SLAF pipeline).\n\nAnalyzes example structured data and generates a comprehensive JSON Schema (draft 2020-12)\nthat describes the structure, types, constraints, patterns, and required vs optional fields.\nThis is the first step in the SLAF structured data extraction pipeline.\n\n**When to Use:**\n- You have example data and need to generate a reusable schema\n- Building a structured extraction pipeline for similar documents\n- Creating validation schemas for downstream systems\n- Normalizing data structure across multiple sources\n\n**Request Body:**\n- `data` (object|array|string): Example data to analyze. Can be a JSON object/array or\n  JSON string. If it's example text, provide inline comments to guide schema generation.\n- `additional_instructions` (string, optional): Extra guidance for schema generation\n  (e.g., \"Make email optional\", \"Add pattern validation for phone numbers\")\n\n**Example Request:**\n```json\n{\n  \"data\": {\n    \"name\": \"John Doe\",\n    \"age\": 30,\n    \"email\": \"john@example.com\",\n    \"phone\": \"+1-555-0123\",\n    \"address\": {\n      \"street\": \"123 Main St\",\n      \"city\": \"Springfield\",\n      \"zip\": \"12345\"\n    }\n  },\n  \"additional_instructions\": \"Make email and phone optional. Add pattern validation for zip code.\"\n}\n```\n\n**Example Response:**\n```json\n{\n  \"json_schema\": {\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": {\"type\": \"string\"},\n      \"age\": {\"type\": \"integer\", \"minimum\": 0},\n      \"email\": {\"type\": \"string\", \"format\": \"email\"},\n      \"phone\": {\"type\": \"string\", \"pattern\": \"^\\\\+?[1-9]\\\\d{1,14}$\"},\n      \"address\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"street\": {\"type\": \"string\"},\n          \"city\": {\"type\": \"string\"},\n          \"zip\": {\"type\": \"string\", \"pattern\": \"^\\\\d{5}$\"}\n        },\n        \"required\": [\"street\", \"city\", \"zip\"]\n      }\n    },\n    \"required\": [\"name\", \"age\", \"address\"]\n  },\n  \"success\": true,\n  \"messages\": [\"Schema generated successfully\"],\n  \"metadata\": {\n    \"detected_fields\": 7,\n    \"required_fields\": 3,\n    \"optional_fields\": 4,\n    \"model_used\": \"openai:gpt-5.1\",\n    \"tokens_used\": 450,\n    \"execution_time_ms\": 1200\n  }\n}\n```\n\n**Error Responses:**\n- 400: Invalid data format or malformed JSON\n- 401: Authentication required or invalid API key\n- 500: LLM call failed, JSON parsing error, or timeout\n\n**Related Endpoints:**\n- `POST /structured-blocks/generate` - Use generated schema to extract data (Step 2)\n- `POST /structured-blocks/audit` - Verify extracted data for hallucinations (Step 3)\n- `POST /structured-blocks/transform` - Format extracted data for output (Step 4)\n\n**SLAF Pipeline Workflow:**\n1. **generate-schema** ← You are here - Create schema from examples\n2. **generate** - Extract structured data using the schema\n3. **audit** - Verify extraction accuracy with source citations\n4. **transform** - Format output as markdown/HTML/text","operationId":"generate_schema_api_v2_structured_blocks_generate_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSchemaRequest"}}}},"responses":{"200":{"description":"JSON schema generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSchemaResponse"}}}},"400":{"description":"Invalid request - malformed data or instructions"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Schema generation failed - LLM error or timeout"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/structured-blocks/generate":{"post":{"tags":["structured-blocks","structured-blocks"],"summary":"Generate","description":"Extract schema-compliant structured data from unstructured text (Step 2 of SLAF pipeline).\n\nTakes unstructured text input (documents, emails, transcripts, etc.) and extracts structured\ndata that conforms to the provided JSON schema. Uses LLM-based extraction with automatic\nschema validation and retry loop with error feedback (SLAF methodology).\n\n**When to Use:**\n- Extracting structured data from documents, emails, or transcripts\n- Converting unstructured text into database-ready records\n- Building data extraction pipelines for content processing\n- Normalizing inconsistent data formats\n\n**Request Body:**\n- `data` (string|array): Unstructured input text. Can be a single string or array of strings\n  (e.g., multiple document chunks, email threads, conversation turns)\n- `json_schema` (object): JSON Schema defining the target structure. Use /generate-schema\n  to create this from examples.\n- `temperature` (float, optional): LLM temperature 0-1 (default: 0.3). Lower = more deterministic\n- `max_retries` (int, optional): Maximum validation retry attempts (default: 3)\n- `include_reasoning` (bool, optional): Include LLM reasoning in response (default: false)\n\n**Example Request:**\n```json\n{\n  \"data\": [\n    \"Patient: John Doe, Male, DOB: 03/15/1985 (Age 39)\",\n    \"Diagnosis: Type 2 Diabetes Mellitus, Hypertension\",\n    \"Medications: Metformin 1000mg BID, Lisinopril 10mg QD\",\n    \"Vitals: BP 138/86, HR 72, Temp 98.6F\"\n  ],\n  \"json_schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"patient\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"name\": {\"type\": \"string\"},\n          \"gender\": {\"type\": \"string\", \"enum\": [\"Male\", \"Female\", \"Other\"]},\n          \"date_of_birth\": {\"type\": \"string\", \"format\": \"date\"},\n          \"age\": {\"type\": \"integer\"}\n        },\n        \"required\": [\"name\", \"gender\", \"age\"]\n      },\n      \"diagnoses\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}},\n      \"medications\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": {\"type\": \"string\"},\n            \"dosage\": {\"type\": \"string\"},\n            \"frequency\": {\"type\": \"string\"}\n          },\n          \"required\": [\"name\", \"dosage\"]\n        }\n      },\n      \"vitals\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"blood_pressure\": {\"type\": \"string\"},\n          \"heart_rate\": {\"type\": \"integer\"},\n          \"temperature\": {\"type\": \"number\"}\n        }\n      }\n    },\n    \"required\": [\"patient\", \"diagnoses\"]\n  },\n  \"temperature\": 0.2,\n  \"max_retries\": 3\n}\n```\n\n**Example Response:**\n```json\n{\n  \"output\": {\n    \"patient\": {\n      \"name\": \"John Doe\",\n      \"gender\": \"Male\",\n      \"date_of_birth\": \"1985-03-15\",\n      \"age\": 39\n    },\n    \"diagnoses\": [\"Type 2 Diabetes Mellitus\", \"Hypertension\"],\n    \"medications\": [\n      {\"name\": \"Metformin\", \"dosage\": \"1000mg\", \"frequency\": \"BID\"},\n      {\"name\": \"Lisinopril\", \"dosage\": \"10mg\", \"frequency\": \"QD\"}\n    ],\n    \"vitals\": {\n      \"blood_pressure\": \"138/86\",\n      \"heart_rate\": 72,\n      \"temperature\": 98.6\n    }\n  },\n  \"success\": true,\n  \"validation_passed\": true,\n  \"messages\": [\"Data generated and validated successfully\"],\n  \"attempts\": 1,\n  \"metadata\": {\n    \"model_used\": \"openai:gpt-5.1\",\n    \"tokens_used\": 850,\n    \"execution_time_ms\": 2100,\n    \"retry_count\": 0\n  }\n}\n```\n\n**Error Responses:**\n- 400: Invalid JSON schema or malformed input\n- 401: Authentication required or invalid API key\n- 500: LLM call failed or exceeded max retries\n\n**Validation & Retry Logic:**\n- Output is automatically validated against the provided schema\n- If validation fails, error feedback is sent back to LLM (SLAF loop)\n- Process retries up to `max_retries` times with specific error guidance\n- Graceful failure returns partial output with validation_errors\n\n**Related Endpoints:**\n- `POST /structured-blocks/generate-schema` - Create schema from examples (Step 1)\n- `POST /structured-blocks/audit` - Verify extraction for hallucinations (Step 3)\n- `POST /structured-blocks/audit/citations` - Get all source citations\n- `POST /structured-blocks/transform` - Format output for display (Step 4)\n\n**SLAF Pipeline Workflow:**\n1. **generate-schema** - Create schema from examples\n2. **generate** ← You are here - Extract structured data\n3. **audit** - Verify extraction accuracy with source citations\n4. **transform** - Format output as markdown/HTML/text","operationId":"generate_api_v2_structured_blocks_generate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateRequest"}}}},"responses":{"200":{"description":"Data extracted and validated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateResponse"}}}},"400":{"description":"Invalid schema or input format"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Extraction failed - LLM error or timeout"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/structured-blocks/transform":{"post":{"tags":["structured-blocks","structured-blocks"],"summary":"Transform","description":"Transform structured data to formatted output (Step 4 of SLAF pipeline).\n\nTakes validated structured data and generates beautifully formatted output in markdown,\nHTML, or plain text according to provided instructions. This is the final presentation\nstep in the SLAF pipeline, creating human-readable content from extracted data.\n\n**When to Use:**\n- Converting extracted data into reports, documents, or presentations\n- Generating formatted output for email, web display, or PDF rendering\n- Creating data visualizations or summary cards\n- Building template-driven content generation pipelines\n\n**Request Body:**\n- `data` (object): Structured data to transform (typically from /generate endpoint)\n- `prompt` (string): Instructions for transformation (e.g., \"Create a professional\n  executive summary\", \"Format as a table\", \"Generate a patient discharge summary\")\n- `output_format` (string): Target format - \"markdown\", \"html\", or \"text\"\n- `data_schema` (object, optional): JSON Schema to validate input data before transformation\n- `temperature` (float, optional): LLM creativity 0-1 (default: 0.5). Higher = more creative formatting\n\n**Example Request:**\n```json\n{\n  \"data\": {\n    \"patient\": {\n      \"name\": \"John Doe\",\n      \"age\": 39,\n      \"gender\": \"Male\"\n    },\n    \"diagnoses\": [\"Type 2 Diabetes Mellitus\", \"Hypertension\"],\n    \"medications\": [\n      {\"name\": \"Metformin\", \"dosage\": \"1000mg\", \"frequency\": \"BID\"},\n      {\"name\": \"Lisinopril\", \"dosage\": \"10mg\", \"frequency\": \"QD\"}\n    ],\n    \"vitals\": {\n      \"blood_pressure\": \"138/86\",\n      \"heart_rate\": 72,\n      \"temperature\": 98.6\n    }\n  },\n  \"prompt\": \"Create a professional patient summary card suitable for clinical handoff\",\n  \"output_format\": \"markdown\"\n}\n```\n\n**Example Response:**\n```json\n{\n  \"output\": \"# Patient Summary\\n\\n**Name:** John Doe  \\n**Age:** 39 years  \\n**Gender:** Male  \\n\\n## Active Diagnoses\\n\\n- Type 2 Diabetes Mellitus\\n- Hypertension\\n\\n## Current Medications\\n\\n| Medication | Dosage | Frequency |\\n|------------|--------|-----------|\\n| Metformin | 1000mg | BID |\\n| Lisinopril | 10mg | QD |\\n\\n## Vital Signs\\n\\n- **Blood Pressure:** 138/86 mmHg\\n- **Heart Rate:** 72 bpm\\n- **Temperature:** 98.6°F\\n\",\n  \"success\": true,\n  \"format\": \"markdown\",\n  \"messages\": [\"Transformation completed successfully\"],\n  \"metadata\": {\n    \"model_used\": \"openai:gpt-5.1\",\n    \"tokens_used\": 420,\n    \"execution_time_ms\": 1500,\n    \"output_length\": 345\n  }\n}\n```\n\n**Error Responses:**\n- 400: Input data validation failed or invalid format\n- 401: Authentication required or invalid API key\n- 500: LLM call failed or transformation error\n\n**Output Formats:**\n- `markdown`: GitHub-flavored Markdown with tables, headers, lists, emphasis\n- `html`: Clean HTML5 with semantic tags, suitable for web display or email\n- `text`: Plain text with basic formatting (indentation, line breaks, bullets)\n\n**Transformation Tips:**\n- Be specific in prompts: \"Create a 2-column table\" vs \"Format nicely\"\n- Mention target audience: \"technical report\", \"executive summary\", \"patient-friendly\"\n- Specify structure: \"Include section headers\", \"Use bullet points\", \"Add a summary paragraph\"\n- Request styling: \"professional\", \"conversational\", \"formal\", \"concise\"\n\n**Related Endpoints:**\n- `POST /structured-blocks/generate-schema` - Create schema from examples (Step 1)\n- `POST /structured-blocks/generate` - Extract structured data (Step 2)\n- `POST /structured-blocks/audit` - Verify extraction accuracy (Step 3)\n\n**SLAF Pipeline Workflow:**\n1. **generate-schema** - Create schema from examples\n2. **generate** - Extract structured data\n3. **audit** - Verify extraction accuracy with source citations\n4. **transform** ← You are here - Format output for display","operationId":"transform_api_v2_structured_blocks_transform_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransformRequest"}}}},"responses":{"200":{"description":"Data transformed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransformResponse"}}}},"400":{"description":"Invalid input data or schema validation failed"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Transformation failed - LLM error or timeout"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/structured-blocks/audit":{"post":{"tags":["structured-blocks","structured-blocks"],"summary":"Audit Extraction","description":"Audit extracted data for accuracy and hallucinations (Step 3 of SLAF pipeline).\n\nVerifies each field in the extracted data by searching source documents for supporting\nand contradictory evidence. This is the critical verification step in SLAF that prevents\nhallucinations and ensures data accuracy with source citations.\n\n**When to Use:**\n- After extracting structured data to verify accuracy\n- When source documents are available for fact-checking\n- Building compliance-critical data extraction pipelines\n- Creating citation-backed data extraction systems\n\n**How It Works:**\n1. For each field, searches source documents for supporting evidence\n2. Searches for contradictory or disputing evidence\n3. Classifies field as: **supported**, **disputed**, **unclear**, or **not_found**\n4. Provides character-level citations with context\n5. Calculates confidence scores based on evidence strength\n6. Returns summary statistics (accuracy rate, citation count, etc.)\n\n**Request Body:**\n- `extracted_data` (object): Structured data to audit (from /generate endpoint)\n- `source_documents` (array): Source documents to search for evidence. Each document:\n  - `id` (string): Unique document identifier\n  - `content` (string): Full document text\n  - `type` (string): Document type (e.g., \"pdf\", \"email\", \"transcript\")\n  - `metadata` (object, optional): Additional document metadata\n- `fields_to_audit` (array, optional): Specific fields to audit. If omitted, audits all fields\n- `audit_config` (object, optional): Audit configuration:\n  - `max_citations_per_field` (int): Limit citations per field (default: 3)\n  - `require_citations` (bool): Fail if no citations found (default: true)\n  - `check_contradictions` (bool): Search for disputing evidence (default: true)\n  - `completeness_check` (bool): Check for missing fields (default: false)\n  - `verbosity` (string): \"minimal\", \"standard\", \"detailed\" (default: \"standard\")\n\n**Example Request:**\n```json\n{\n  \"extracted_data\": {\n    \"patient_name\": \"John Doe\",\n    \"age\": 39,\n    \"diagnoses\": [\"Type 2 Diabetes\", \"Hypertension\"],\n    \"medication\": \"Metformin 1000mg BID\"\n  },\n  \"source_documents\": [\n    {\n      \"id\": \"doc-001\",\n      \"content\": \"Patient: John Doe (39 yo male). Diagnosed with Type 2 Diabetes and HTN. Started on Metformin 1000mg twice daily.\",\n      \"type\": \"clinical_note\",\n      \"metadata\": {\"date\": \"2025-01-15\", \"provider\": \"Dr. Smith\"}\n    }\n  ],\n  \"fields_to_audit\": [\"patient_name\", \"age\", \"medication\"],\n  \"audit_config\": {\n    \"max_citations_per_field\": 2,\n    \"check_contradictions\": true,\n    \"verbosity\": \"detailed\"\n  }\n}\n```\n\n**Example Response:**\n```json\n{\n  \"audit_results\": {\n    \"patient_name\": {\n      \"classification\": \"supported\",\n      \"confidence\": 0.95,\n      \"supporting_evidence\": [\n        {\n          \"source_id\": \"doc-001\",\n          \"source_type\": \"clinical_note\",\n          \"quote\": \"Patient: John Doe\",\n          \"start_char\": 0,\n          \"end_char\": 18,\n          \"context\": \"Patient: John Doe (39 yo male). Diagnosed...\",\n          \"relevance_score\": 0.98\n        }\n      ],\n      \"disputing_evidence\": [],\n      \"notes\": \"Name found verbatim in source document.\"\n    },\n    \"age\": {\n      \"classification\": \"supported\",\n      \"confidence\": 0.92,\n      \"supporting_evidence\": [\n        {\n          \"source_id\": \"doc-001\",\n          \"source_type\": \"clinical_note\",\n          \"quote\": \"39 yo male\",\n          \"start_char\": 20,\n          \"end_char\": 30,\n          \"context\": \"Patient: John Doe (39 yo male). Diagnosed...\",\n          \"relevance_score\": 0.95\n        }\n      ],\n      \"disputing_evidence\": [],\n      \"notes\": \"Age explicitly stated in clinical note.\"\n    },\n    \"medication\": {\n      \"classification\": \"supported\",\n      \"confidence\": 0.90,\n      \"supporting_evidence\": [\n        {\n          \"source_id\": \"doc-001\",\n          \"source_type\": \"clinical_note\",\n          \"quote\": \"Started on Metformin 1000mg twice daily\",\n          \"start_char\": 80,\n          \"end_char\": 120,\n          \"context\": \"...HTN. Started on Metformin 1000mg twice daily.\",\n          \"relevance_score\": 0.94\n        }\n      ],\n      \"disputing_evidence\": [],\n      \"notes\": \"Medication and dosage confirmed in source.\"\n    }\n  },\n  \"summary\": {\n    \"total_fields_audited\": 3,\n    \"supported\": 3,\n    \"disputed\": 0,\n    \"unclear\": 0,\n    \"not_found\": 0,\n    \"average_confidence\": 0.92,\n    \"total_citations\": 3,\n    \"fields_with_citations\": 3\n  },\n  \"metadata\": {\n    \"audit_duration_ms\": 2800,\n    \"llm_model\": \"openai:gpt-5.1\",\n    \"tokens_used\": {\"input\": 450, \"output\": 320, \"total\": 770},\n    \"cost_usd\": 0.0042\n  }\n}\n```\n\n**Classification Types:**\n- `supported`: Field has strong supporting evidence, no contradictions\n- `disputed`: Contradictory evidence found in source documents\n- `unclear`: Some evidence exists but is ambiguous or weak\n- `not_found`: No evidence found in any source document\n\n**Confidence Scores:**\n- 0.90-1.00: High confidence - strong, clear evidence\n- 0.70-0.89: Medium confidence - good evidence with minor ambiguity\n- 0.50-0.69: Low confidence - weak or conflicting evidence\n- 0.00-0.49: Very low confidence - minimal or contradictory evidence\n\n**Error Responses:**\n- 400: Missing source documents or invalid extracted data\n- 401: Authentication required or invalid API key\n- 500: Audit failed due to LLM error or processing failure\n\n**Related Endpoints:**\n- `POST /structured-blocks/generate-schema` - Create schema (Step 1)\n- `POST /structured-blocks/generate` - Extract data (Step 2)\n- `POST /structured-blocks/audit/citations` - Get unlimited citations for all fields\n- `POST /structured-blocks/transform` - Format output (Step 4)\n\n**SLAF Pipeline Workflow:**\n1. **generate-schema** - Create schema from examples\n2. **generate** - Extract structured data\n3. **audit** ← You are here - Verify extraction with source citations\n4. **transform** - Format output for display","operationId":"audit_extraction_api_v2_structured_blocks_audit_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditRequest"}}}},"responses":{"200":{"description":"Audit completed successfully with field-level results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditResponse"}}}},"400":{"description":"Invalid request - missing source documents or extracted data"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Audit failed - LLM error or processing failure"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/structured-blocks/audit/citations":{"post":{"tags":["structured-blocks","structured-blocks"],"summary":"Get All Citations","description":"Get comprehensive citations for all extracted fields (unlimited, no caps).\n\nSimilar to /audit but returns ALL evidence without citation limits. Can group results\nby source document (for bibliographies) or by field (for detailed review). Perfect for\ncompliance documentation, building reference sections, or detailed human review.\n\n**When to Use:**\n- Building bibliographies or reference sections\n- Compliance documentation requiring full citation trails\n- Detailed human review of extraction accuracy\n- Citation count analysis or evidence strength assessment\n- Legal/medical applications requiring complete audit trails\n\n**Differences from /audit:**\n- **No citation limits** - returns all evidence found (not capped at 3)\n- **Flexible grouping** - can group by source document or by field\n- **No classification** - focuses on evidence retrieval, not accuracy scoring\n- **Simplified output** - optimized for citation display, not verification\n\n**Request Body:**\n- `extracted_data` (object): Structured data to get citations for\n- `source_documents` (array): Source documents to search (same format as /audit)\n- `config` (object, optional): Citation configuration:\n  - `group_by_source` (bool): Group citations by document instead of field (default: false)\n  - `include_context` (bool): Include surrounding text context (default: true)\n  - `min_relevance_score` (float): Filter citations below this score 0-1 (default: 0.0)\n\n**Example Request (Group by Field):**\n```json\n{\n  \"extracted_data\": {\n    \"patient_name\": \"John Doe\",\n    \"diagnosis\": \"Type 2 Diabetes\"\n  },\n  \"source_documents\": [\n    {\n      \"id\": \"note-001\",\n      \"content\": \"Patient John Doe diagnosed with Type 2 Diabetes.\",\n      \"type\": \"clinical_note\"\n    },\n    {\n      \"id\": \"lab-001\",\n      \"content\": \"John Doe - HbA1c 8.2% confirms diabetes diagnosis.\",\n      \"type\": \"lab_report\"\n    }\n  ],\n  \"config\": {\n    \"group_by_source\": false,\n    \"include_context\": true\n  }\n}\n```\n\n**Example Response (Group by Field):**\n```json\n{\n  \"citations_by_field\": {\n    \"patient_name\": {\n      \"classification\": \"supported\",\n      \"total_citations\": 2,\n      \"supporting\": [\n        {\n          \"source_id\": \"note-001\",\n          \"source_type\": \"clinical_note\",\n          \"quote\": \"John Doe\",\n          \"start_char\": 8,\n          \"end_char\": 16,\n          \"context\": \"Patient John Doe diagnosed...\",\n          \"relevance_score\": 0.98\n        },\n        {\n          \"source_id\": \"lab-001\",\n          \"source_type\": \"lab_report\",\n          \"quote\": \"John Doe\",\n          \"start_char\": 0,\n          \"end_char\": 8,\n          \"context\": \"John Doe - HbA1c 8.2%...\",\n          \"relevance_score\": 0.95\n        }\n      ],\n      \"disputing\": []\n    },\n    \"diagnosis\": {\n      \"classification\": \"supported\",\n      \"total_citations\": 2,\n      \"supporting\": [\n        {\n          \"source_id\": \"note-001\",\n          \"source_type\": \"clinical_note\",\n          \"quote\": \"Type 2 Diabetes\",\n          \"start_char\": 37,\n          \"end_char\": 52,\n          \"relevance_score\": 0.99\n        },\n        {\n          \"source_id\": \"lab-001\",\n          \"source_type\": \"lab_report\",\n          \"quote\": \"confirms diabetes diagnosis\",\n          \"start_char\": 23,\n          \"end_char\": 50,\n          \"relevance_score\": 0.88\n        }\n      ],\n      \"disputing\": []\n    }\n  },\n  \"summary\": {\n    \"total_fields_with_citations\": 2,\n    \"total_citations\": 4,\n    \"citations_by_classification\": {\n      \"supporting\": 4,\n      \"disputing\": 0\n    }\n  },\n  \"metadata\": {\n    \"audit_duration_ms\": 3200,\n    \"llm_model\": \"openai:gpt-5.1\",\n    \"tokens_used\": {\"input\": 380, \"output\": 450, \"total\": 830},\n    \"cost_usd\": 0.0048\n  }\n}\n```\n\n**Example Request (Group by Source):**\n```json\n{\n  \"extracted_data\": {\n    \"patient_name\": \"John Doe\",\n    \"diagnosis\": \"Type 2 Diabetes\"\n  },\n  \"source_documents\": [...],\n  \"config\": {\n    \"group_by_source\": true\n  }\n}\n```\n\n**Example Response (Group by Source):**\n```json\n{\n  \"citations_by_source\": {\n    \"note-001\": {\n      \"source_metadata\": {\"type\": \"clinical_note\", \"date\": \"2025-01-15\"},\n      \"total_citations\": 2,\n      \"citations\": [\n        {\n          \"field\": \"patient_name\",\n          \"type\": \"supporting\",\n          \"quote\": \"John Doe\",\n          \"start_char\": 8,\n          \"end_char\": 16,\n          \"relevance_score\": 0.98\n        },\n        {\n          \"field\": \"diagnosis\",\n          \"type\": \"supporting\",\n          \"quote\": \"Type 2 Diabetes\",\n          \"start_char\": 37,\n          \"end_char\": 52,\n          \"relevance_score\": 0.99\n        }\n      ]\n    },\n    \"lab-001\": {\n      \"source_metadata\": {\"type\": \"lab_report\"},\n      \"total_citations\": 2,\n      \"citations\": [...]\n    }\n  },\n  \"summary\": {\n    \"total_sources_cited\": 2,\n    \"total_citations\": 4,\n    \"most_cited_source\": \"note-001\"\n  },\n  \"metadata\": {...}\n}\n```\n\n**Use Cases:**\n- **Bibliographies**: Group by source to build \"References\" sections\n- **Compliance**: Full audit trail for regulatory requirements\n- **Human Review**: Detailed evidence for QA/validation teams\n- **Citation Analysis**: Identify most-cited sources or weak evidence\n- **Legal/Medical**: Complete documentation for high-stakes applications\n\n**Error Responses:**\n- 400: Missing source documents or invalid data format\n- 401: Authentication required or invalid API key\n- 500: LLM call failed or citation extraction error\n\n**Related Endpoints:**\n- `POST /structured-blocks/generate` - Extract structured data (Step 2)\n- `POST /structured-blocks/audit` - Verify with limited citations (Step 3)\n- `POST /structured-blocks/transform` - Format output (Step 4)\n\n**SLAF Pipeline Workflow:**\n1. **generate-schema** - Create schema from examples\n2. **generate** - Extract structured data\n3. **audit** - Verify extraction with limited citations OR\n3. **audit/citations** ← You are here - Get all citations (unlimited)\n4. **transform** - Format output for display","operationId":"get_all_citations_api_v2_structured_blocks_audit_citations_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationsRequest"}}}},"responses":{"200":{"description":"Citations retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitationsResponse"}}}},"400":{"description":"Invalid request - missing source documents or data"},"401":{"description":"Authentication required - missing or invalid API key"},"500":{"description":"Citation extraction failed - LLM error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/structured-blocks/generate-block-schema":{"post":{"tags":["structured-blocks","structured-blocks"],"summary":"Generate Block Schema","description":"Generate a structured block extraction schema from a natural language description (SES-170).\n\nUses LLM to convert a data extraction description into a valid JSON schema,\nextraction instructions, and example output. Validates the schema and example\nusing a SLAF retry loop (up to 5 attempts). Supports iterative refinement\nvia conversation_id.\n\nThis endpoint generates everything needed to use the structured blocks pipeline:\n1. JSON Schema — use with POST /structured-blocks/generate\n2. Extraction instructions — feed as context to the generate endpoint\n3. Example output — reference for expected results\n\n**Prerequisites:**\n- Valid API key required\n\n**Request Body (GenerateBlockSchemaRequest):**\n- `description` (required): Natural language description of what data to extract\n- `hints` (optional): Hints like `fields`, `source_type`, `output_format`\n- `conversation_id` (optional): ID to continue refining a previous generation\n- `feedback` (optional): Feedback on previous generation for refinement\n\n**Hints Object:**\n```json\n{\n    \"fields\": [\"patient_name\", \"dob\", \"diagnosis_codes\", \"medications\"],\n    \"source_type\": \"clinical_notes\",\n    \"output_format\": \"json\"\n}\n```\n\n**Example Request:**\n```json\n{\n    \"description\": \"Extract patient demographics and medication list from clinical notes\",\n    \"hints\": {\n        \"fields\": [\"patient_name\", \"dob\", \"medications\"],\n        \"source_type\": \"clinical_notes\"\n    }\n}\n```\n\n**Example Response (200):**\n```json\n{\n    \"json_schema\": {\n        \"type\": \"object\",\n        \"properties\": {\n            \"patient_name\": {\"type\": \"string\", \"description\": \"Full patient name\"},\n            \"dob\": {\"type\": \"string\", \"format\": \"date\"},\n            \"medications\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}}\n        },\n        \"required\": [\"patient_name\", \"dob\", \"medications\"]\n    },\n    \"extraction_instructions\": \"Extract the following from clinical notes...\",\n    \"example_output\": {\n        \"patient_name\": \"Jane Smith\",\n        \"dob\": \"1985-03-15\",\n        \"medications\": [\"Lisinopril 10mg\", \"Metformin 500mg\"]\n    },\n    \"conversation_id\": \"conv_abc123\",\n    \"suggestions\": [\"Consider adding dosage as a separate field\"],\n    \"model_used\": \"anthropic/claude-3.5-sonnet\",\n    \"tokens_used\": 980,\n    \"generation_time_ms\": 2800\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 422: Generation failed after all SLAF retry attempts\n\n**Related Endpoints:**\n- POST /structured-blocks/generate-schema — Generate schema from example data\n- POST /structured-blocks/generate — Extract structured data using a schema\n- POST /structured-blocks/audit — Verify extraction accuracy\n- POST /sops/generate-sop-schema — Generate an SOP definition\n- POST /agents/generate-agent-schema — Generate an agent definition","operationId":"generate_block_schema_api_v2_structured_blocks_generate_block_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateBlockSchemaRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateBlockSchemaResponse"}}}},"422":{"description":"Generation failed after all SLAF retry attempts","content":{"application/json":{"example":{"detail":{"error":"GENERATION_FAILED","message":"LLM returned invalid JSON after 5 attempts","last_error":"Expecting ',' delimiter: line 5 column 2","attempts":5,"stage":"json_syntax"}}}}}}}},"/api/v2/jobs":{"post":{"tags":["jobs"],"summary":"Create Job","description":"Create a new background job.\n\nSubmits a job for asynchronous execution. Jobs are queued and processed by\nCelery workers. Use this for long-running operations like file processing,\nRAG indexing, or agent execution.\n\n**Idempotency:** If an idempotency_key is provided and a job with that key\nalready exists, returns the existing job with status 200 instead of creating\na duplicate (status 201).\n\n**Prerequisites:**\n- Valid API key required\n- User must have editor or owner permission on the project\n\n**Request Body:**\n- `project_id` (required): Project that owns the job and its outputs\n- `job_type` (required): Type of job - \"http_call\" or \"agent_execution\"\n- `name` (optional): Human-readable job name for easier identification\n- `idempotency_key` (optional): Unique key to prevent duplicate jobs\n- `run_id` (optional): Required for agent_execution jobs\n- `priority` (optional): Queue priority (-10 to 10, default: 0)\n- `parameters` (optional): Job-specific parameters\n- `webhook_url` (optional): Your HTTPS URL for a **completion callback** when the job reaches\n  `completed` or `failed` (see **Completion webhooks** below). Omit to poll only.\n\n**Job Types:**\n- `http_call`: Generic async wrapper for any API endpoint (requires method and endpoint in parameters)\n- `agent_execution`: Special case for agent runs with scheduling and nested jobs (requires run_id)\n\n**Example Request (http_call):**\n```json\n{\n    \"name\": \"Process file async\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"job_type\": \"http_call\",\n    \"priority\": 0,\n    \"parameters\": {\n        \"method\": \"POST\",\n        \"endpoint\": \"/api/v2/files/fil_9z8y7x6w5v/process\",\n        \"body\": {\n            \"rag_chunking\": true,\n            \"chunk_size\": 512\n        }\n    }\n}\n```\n\n**Example Request (agent_execution):**\n```json\n{\n    \"name\": \"Execute agent run\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"job_type\": \"agent_execution\",\n    \"run_id\": \"run_7h8i9j0k1l\",\n    \"priority\": 5\n}\n```\n\n**Example Response (201):**\n```json\n{\n    \"id\": \"job_2w3e4r5t6y\",\n    \"user_id\": \"usr_9z8y7x6w5v\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"run_id\": null,\n    \"idempotency_key\": null,\n    \"job_type\": \"http_call\",\n    \"status\": \"pending\",\n    \"priority\": 0,\n    \"error_code\": null,\n    \"error_message\": null,\n    \"error_traceback\": null,\n    \"retryable\": null,\n    \"output_file_id\": null,\n    \"parameters\": {\n        \"method\": \"POST\",\n        \"endpoint\": \"/api/v2/files/fil_9z8y7x6w5v/process\"\n    },\n    \"results\": null,\n    \"created_at\": \"2026-01-29T16:45:00Z\",\n    \"updated_at\": \"2026-01-29T16:45:00Z\",\n    \"started_at\": null,\n    \"completed_at\": null\n}\n```\n\n**Error Responses:**\n- 200: Existing job returned (idempotency_key matched)\n- 400: Missing required fields (method/endpoint for http_call, run_id for agent_execution)\n- 401: Missing or invalid API key\n- 403: User lacks editor permission on the project\n- 422: Invalid job_type or priority out of range\n\n**Completion webhooks (optional, SES-259):**\n\nSet `webhook_url` to receive an **outbound POST** when the job finishes — so you do not need\nto poll until terminal. **Progress is not pushed**; while status is `queued` or `running`,\nuse `GET /jobs/{job_id}` as today.\n\n| Topic | Behavior |\n|-------|----------|\n| Trigger | `completed` or `failed` only (not `cancelled`) |\n| Method | `POST` to your `webhook_url` |\n| Body | `JobWebhookPayload` — see schema in OpenAPI / `GET /jobs/webhook-callback-spec` |\n| `result_preview` | Inline results if JSON under 4KB; else null → fetch via Jobs API |\n| Retries | Up to 3 (30s, 120s, 300s backoff); respond 2xx within 10s |\n| Auth | No `Authorization` header on the outbound POST (HMAC planned) |\n\n**Example completed callback body:**\n```json\n{\n    \"event\": \"job.completed\",\n    \"job_id\": \"job_2w3e4r5t6y\",\n    \"status\": \"completed\",\n    \"job_type\": \"http_call\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"completed_at\": \"2026-06-14T12:00:00Z\",\n    \"error_code\": null,\n    \"error_message\": null,\n    \"result_preview\": {\"chunks_created\": 42, \"status\": \"success\"}\n}\n```\n\n**Example failed callback body:**\n```json\n{\n    \"event\": \"job.failed\",\n    \"job_id\": \"job_9x8y7z6w5v\",\n    \"status\": \"failed\",\n    \"job_type\": \"http_call\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"completed_at\": \"2026-06-14T12:05:00Z\",\n    \"error_code\": \"TIMEOUT\",\n    \"error_message\": \"Job did not complete within allowed time\",\n    \"result_preview\": null\n}\n```\n\n**Implementing your webhook receiver:**\n\nYour server must expose an HTTPS endpoint that accepts `POST` with `Content-Type: application/json`\nand returns any `2xx` status within 10 seconds. Example (Python/Flask):\n\n```python\n@app.post(\"/webhooks/taiso-job\")\ndef handle_taiso_webhook():\n    payload = request.get_json()\n    event = payload[\"event\"]        # \"job.completed\" or \"job.failed\"\n    job_id = payload[\"job_id\"]       # correlate with your records\n    if event == \"job.completed\":\n        results = payload.get(\"result_preview\")  # inline if < 4KB\n        if results is None:\n            # fetch full results from Taiso API\n            pass\n    elif event == \"job.failed\":\n        error = payload.get(\"error_message\")\n        # handle failure\n    return \"\", 200  # respond quickly — do heavy work async\n```\n\n**Related Endpoints:**\n- GET /jobs - List all jobs\n- GET /jobs/{job_id} - Get job status\n- POST /jobs/{job_id}/cancel - Cancel job\n- GET /jobs/{job_id}/result - Get job result\n- GET /jobs/webhook-callback-spec - Sample webhook payload for reference","operationId":"create_job_api_v2_jobs_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"200":{"description":"Existing job returned (idempotency key matched)"},"400":{"description":"Invalid parameters - missing required fields or invalid job type"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Permission denied - editor role required"},"422":{"description":"Validation error - invalid field values"}}},"get":{"tags":["jobs"],"summary":"List Jobs","description":"List jobs accessible to the user.\n\nReturns a paginated list of jobs the user has access to. Can be filtered by\nproject, run_id, and/or status. Jobs are returned with full details including\nparameters and results.\n\n**Prerequisites:**\n- Valid API key required\n\n**Query Parameters:**\n- `project_id` (optional): Filter jobs by project\n- `run_id` (optional): Filter jobs by agent run ID\n- `status` (optional): Filter by status - \"pending\", \"running\", \"completed\", \"failed\", \"cancelled\"\n- `limit` (optional): Maximum jobs to return (default: 100, max: 1000)\n- `offset` (optional): Number of jobs to skip for pagination (default: 0)\n\n**Example Request:**\n```bash\nGET /api/v2/jobs?project_id=prj_1a2b3c4d5e6f&status=completed&limit=50\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"jobs\": [\n        {\n            \"id\": \"job_2w3e4r5t6y\",\n            \"user_id\": \"usr_9z8y7x6w5v\",\n            \"project_id\": \"prj_1a2b3c4d5e6f\",\n            \"run_id\": null,\n            \"idempotency_key\": null,\n            \"job_type\": \"http_call\",\n            \"status\": \"completed\",\n            \"priority\": 0,\n            \"error_code\": null,\n            \"error_message\": null,\n            \"error_traceback\": null,\n            \"retryable\": null,\n            \"output_file_id\": null,\n            \"parameters\": {\n                \"method\": \"POST\",\n                \"endpoint\": \"/api/v2/files/fil_9z8y7x6w5v/process\"\n            },\n            \"results\": {\n                \"file_id\": \"fil_9z8y7x6w5v\",\n                \"status\": \"completed\",\n                \"chunks_created\": 42\n            },\n            \"created_at\": \"2026-01-29T16:45:00Z\",\n            \"updated_at\": \"2026-01-29T16:46:30Z\",\n            \"started_at\": \"2026-01-29T16:45:05Z\",\n            \"completed_at\": \"2026-01-29T16:46:30Z\"\n        }\n    ],\n    \"total\": 1,\n    \"limit\": 50,\n    \"offset\": 0\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view jobs\n\n**Related Endpoints:**\n- POST /jobs - Create new job\n- GET /jobs/{job_id} - Get specific job details\n- POST /jobs/{job_id}/cancel - Cancel job","operationId":"list_jobs_api_v2_jobs_get","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"}},{"name":"run_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobListResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Permission denied"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/jobs/webhook-callback-spec":{"get":{"tags":["jobs"],"summary":"[Reference] Job completion webhook payload","description":"Return a **sample** `JobWebhookPayload` — the JSON body Taiso POSTs to your `webhook_url`.\n\n**This is documentation for integrators.** It is not a webhook receiver; it shows the\nexact JSON your server will receive when a job reaches a terminal state.\n\n**Payload fields:**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `event` | `\"job.completed\"` \\| `\"job.failed\"` | Event type matching the terminal status |\n| `job_id` | string | Job identifier — use with `GET /jobs/{job_id}` to fetch full state |\n| `status` | `\"completed\"` \\| `\"failed\"` | Terminal status that triggered this callback |\n| `job_type` | string | Job type (e.g. `http_call`, `agent_execution`) |\n| `project_id` | string | Project that owns the job |\n| `completed_at` | string (ISO 8601) | Timestamp when the job reached terminal state |\n| `error_code` | string \\| null | Machine-readable error code on failure; null on success |\n| `error_message` | string \\| null | Human-readable error summary on failure; null on success |\n| `result_preview` | object \\| null | Job results inline if JSON < 4KB; null otherwise |\n\n**Delivery details:**\n- Method: `POST` to your `webhook_url`\n- Content-Type: `application/json`\n- Timeout: 10 seconds — respond with `2xx` quickly, process async\n- Retries: Up to 3 attempts (30s, 120s, 300s backoff)\n- Guarantee: At-least-once — deduplicate on `(job_id, status)` tuple\n- Auth: No `Authorization` header sent (HMAC signing planned)\n- Failure: Delivery failure does **not** change job status\n\n**After receiving the callback:**\n- On `job.completed`: use `result_preview` if present, or call\n  `GET /api/v2/jobs/{job_id}` / `GET /api/v2/jobs/{job_id}/result` for full output\n- On `job.failed`: inspect `error_code` and `error_message`, then decide whether to retry\n\nPass `?event_type=failed` to see the failure payload shape.","operationId":"get_job_webhook_callback_spec_api_v2_jobs_webhook_callback_spec_get","parameters":[{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Return a `\"failed\"` sample instead of the default `\"completed\"` sample.","enum":["completed","failed"],"title":"Event Type"},"description":"Return a `\"failed\"` sample instead of the default `\"completed\"` sample."},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Sample webhook payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobWebhookPayload"},"examples":{"completed":{"summary":"Successful job completion","value":{"event":"job.completed","job_id":"job_2w3e4r5t6y","status":"completed","job_type":"http_call","project_id":"prj_1a2b3c4d5e6f","completed_at":"2026-06-14T12:00:00Z","result_preview":{"chunks_created":42,"status":"success"}}},"failed":{"summary":"Failed job notification","value":{"event":"job.failed","job_id":"job_9x8y7z6w5v","status":"failed","job_type":"http_call","project_id":"prj_1a2b3c4d5e6f","completed_at":"2026-06-14T12:05:00Z","error_code":"TIMEOUT","error_message":"Job did not complete within allowed time"}}}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/jobs/{job_id}":{"get":{"tags":["jobs"],"summary":"Get Job","description":"Get job details by ID.\n\nRetrieves complete details for a specific job including its current status,\nparameters, results (if completed), and error information (if failed).\n\n**Prerequisites:**\n- Valid API key required\n- User must have access to the job's project\n\n**Path Parameters:**\n- `job_id` (required): Job ID (e.g., job_2w3e4r5t6y)\n\n**Example Request:**\n```bash\nGET /api/v2/jobs/job_2w3e4r5t6y\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"id\": \"job_2w3e4r5t6y\",\n    \"user_id\": \"usr_9z8y7x6w5v\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"run_id\": null,\n    \"idempotency_key\": null,\n    \"job_type\": \"http_call\",\n    \"status\": \"completed\",\n    \"priority\": 0,\n    \"error_code\": null,\n    \"error_message\": null,\n    \"error_traceback\": null,\n    \"retryable\": null,\n    \"output_file_id\": null,\n    \"parameters\": {\n        \"method\": \"POST\",\n        \"endpoint\": \"/api/v2/files/fil_9z8y7x6w5v/process\"\n    },\n    \"results\": {\n        \"file_id\": \"fil_9z8y7x6w5v\",\n        \"status\": \"completed\",\n        \"chunks_created\": 42\n    },\n    \"created_at\": \"2026-01-29T16:45:00Z\",\n    \"updated_at\": \"2026-01-29T16:46:30Z\",\n    \"started_at\": \"2026-01-29T16:45:05Z\",\n    \"completed_at\": \"2026-01-29T16:46:30Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view this job\n- 404: Job not found or invalid job_id\n\n**Related Endpoints:**\n- GET /jobs - List all jobs\n- POST /jobs/{job_id}/cancel - Cancel job\n- GET /jobs/{job_id}/result - Get job result (for completed jobs)","operationId":"get_job_api_v2_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Permission denied"},"404":{"description":"Job not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["jobs"],"summary":"Delete Job","description":"Delete a job (SES-220).\n\nPermanently deletes a job record from the database. This is useful for\ncleaning up orphaned jobs, completed jobs you no longer need, or jobs\ncreated with invalid parameters.\n\n**Prerequisites:**\n- Valid API key required\n- User must be the job owner\n\n**Path Parameters:**\n- `job_id` (required): Job ID to delete\n\n**Example Request:**\n```bash\nDELETE /api/v2/jobs/job_2w3e4r5t6y\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (204):**\nNo content returned on successful deletion.\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User is not the job owner\n- 404: Job not found\n\n**Related Endpoints:**\n- GET /jobs - List jobs\n- GET /jobs/{job_id} - Get job details\n- POST /jobs/{job_id}/cancel - Cancel running job","operationId":"delete_job_api_v2_jobs__job_id__delete","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Job deleted successfully"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Permission denied - not job owner"},"404":{"description":"Job not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/jobs/{job_id}/cancel":{"post":{"tags":["jobs"],"summary":"Cancel Job","description":"Cancel a running or queued job.\n\nAttempts to cancel a job that is pending or running. Completed, failed, or\nalready cancelled jobs cannot be cancelled. The job status will be updated\nto \"cancelled\" and workers will stop processing it.\n\n**Prerequisites:**\n- Valid API key required\n- User must have access to the job's project\n- Job must be in \"pending\" or \"running\" status\n\n**Path Parameters:**\n- `job_id` (required): Job ID to cancel\n\n**Example Request:**\n```bash\nPOST /api/v2/jobs/job_2w3e4r5t6y/cancel\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (200):**\n```json\n{\n    \"id\": \"job_2w3e4r5t6y\",\n    \"user_id\": \"usr_9z8y7x6w5v\",\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"run_id\": null,\n    \"idempotency_key\": null,\n    \"job_type\": \"http_call\",\n    \"status\": \"cancelled\",\n    \"priority\": 0,\n    \"error_code\": null,\n    \"error_message\": \"Job cancelled by user\",\n    \"error_traceback\": null,\n    \"retryable\": false,\n    \"output_file_id\": null,\n    \"parameters\": {\n        \"method\": \"POST\",\n        \"endpoint\": \"/api/v2/files/fil_9z8y7x6w5v/process\"\n    },\n    \"results\": null,\n    \"created_at\": \"2026-01-29T16:45:00Z\",\n    \"updated_at\": \"2026-01-29T16:46:00Z\",\n    \"started_at\": \"2026-01-29T16:45:05Z\",\n    \"completed_at\": null\n}\n```\n\n**Error Responses:**\n- 400: Job is already completed, failed, or cancelled\n- 401: Missing or invalid API key\n- 403: User lacks permission to cancel this job\n- 404: Job not found\n\n**Related Endpoints:**\n- GET /jobs/{job_id} - Check job status\n- POST /jobs - Create new job\n- GET /jobs - List jobs","operationId":"cancel_job_api_v2_jobs__job_id__cancel_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"description":"Job cannot be cancelled (already completed or failed)"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Permission denied"},"404":{"description":"Job not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/jobs/{job_id}/result":{"get":{"tags":["jobs"],"summary":"Get Job Result","description":"Retrieve job result.\n\nRetrieves the output/result from a completed job. Results are available for\n7 days after job completion, then automatically expired.\n\n**Three Result Modes:**\n\n1. **Direct Return** (no query params): Returns result content directly if < 20MB\n2. **Create File** (?project_id): Creates new file in project, returns file_id\n3. **Overwrite File** (?file_id): Overwrites existing file, returns file_id\n\n**Prerequisites:**\n- Valid API key required\n- Job must be completed\n- Result must be less than 7 days old\n- User must have access to the job's project\n\n**Path Parameters:**\n- `job_id` (required): Job ID whose result to retrieve\n\n**Query Parameters:**\n- `project_id` (optional): Create new file in this project with the result\n- `file_id` (optional): Overwrite this existing file with the result\n\n**Example Request (Direct):**\n```bash\nGET /api/v2/jobs/job_2w3e4r5t6y/result\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Request (Create File):**\n```bash\nGET /api/v2/jobs/job_2w3e4r5t6y/result?project_id=prj_1a2b3c4d5e6f\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Request (Overwrite File):**\n```bash\nGET /api/v2/jobs/job_2w3e4r5t6y/result?file_id=fil_9z8y7x6w5v\nAuthorization: Bearer YOUR_API_KEY\n```\n\n**Example Response (Direct - 200):**\nReturns raw content with appropriate Content-Type header.\n\n**Example Response (Create File - 200):**\n```json\n{\n    \"file_content\": null,\n    \"project_id\": \"prj_1a2b3c4d5e6f\",\n    \"file_id\": \"fil_3n2m1k0j9i\"\n}\n```\n\n**Example Response (Overwrite File - 200):**\n```json\n{\n    \"file_content\": null,\n    \"project_id\": null,\n    \"file_id\": \"fil_9z8y7x6w5v\"\n}\n```\n\n**Error Responses:**\n- 400: Job not completed or both project_id and file_id specified\n- 401: Missing or invalid API key\n- 403: User lacks access to target project or file\n- 404: Job not found or result not available (may be deleted or unsupported job type)\n- 410: Result expired (older than 7 days)\n- 413: Result too large for direct return (use project_id or file_id)\n\n**Related Endpoints:**\n- GET /jobs/{job_id} - Check job status\n- POST /jobs - Create new job","operationId":"get_job_result_api_v2_jobs__job_id__result_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Create file in this project (create file mode)","title":"Project Id"},"description":"Create file in this project (create file mode)"},{"name":"file_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Overwrite this file with result (overwrite file mode)","title":"File Id"},"description":"Overwrite this file with result (overwrite file mode)"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Job not completed or conflicting parameters","content":{"application/json":{"examples":{"not_completed":{"value":{"detail":"Job is not completed (status: running)"}},"both_params":{"value":{"detail":"Cannot specify both project_id and file_id"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"No access to target project or file"},"404":{"description":"Job not found or result not available","content":{"application/json":{"example":{"detail":"Job result not found (may have been deleted or job type doesn't store results)"}}}},"410":{"description":"Job result has expired (> 7 days old)","content":{"application/json":{"example":{"detail":"Job result expired (older than 7 days)"}}}},"413":{"description":"Result too large for direct return","content":{"application/json":{"example":{"detail":"Result too large for direct return (25165824 bytes > 20MB). Use ?project_id or ?file_id to save as file."}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/assist/ask":{"post":{"tags":["assist","assist"],"summary":"Ask Question","description":"Ask a natural language question about the SOP Engine API.\n\nGet AI-powered assistance with API endpoints, workflows, code examples, and\nbest practices. Returns structured guidance with step-by-step instructions.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Request Body:**\n- `question` (required): Natural language question about the API\n- `context` (optional): Context to guide the response (e.g., \"I am a Python developer\")\n- `response_format` (optional): Output format - \"json\", \"yaml\", or \"markdown\" (default: \"json\")\n- `include_code_examples` (optional): Include code snippets (default: true)\n- `code_languages` (optional): Languages for examples - [\"curl\", \"python\", \"javascript\"] (default: all)\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/assist/ask       -H \"Authorization: Bearer YOUR_API_KEY\"       -H \"Content-Type: application/json\"       -d '{\n    \"question\": \"How do I upload a file and index it?\",\n    \"response_format\": \"json\",\n    \"include_code_examples\": true,\n    \"code_languages\": [\"curl\", \"python\"]\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"answer\": \"To upload a file to SOP Engine:\\n\\n1. Create a project (optional)\\n2. Upload the file using POST /api/v2/files\\n3. Start RAG indexing with POST /api/v2/jobs\",\n  \"steps\": [\n    {\n      \"number\": 1,\n      \"method\": \"POST\",\n      \"path\": \"/api/v2/files\",\n      \"summary\": \"Upload the file\",\n      \"code\": \"curl -X POST https://api.taiso.ai/api/v2/files ...\"\n    }\n  ],\n  \"endpoints\": [\n    {\n      \"method\": \"POST\",\n      \"path\": \"/api/v2/files\",\n      \"summary\": \"Upload file\",\n      \"verified\": true\n    }\n  ],\n  \"examples\": {\n    \"curl\": \"curl -X POST ...\",\n    \"python\": \"import requests\\n...\"\n  },\n  \"citations\": [\n    {\n      \"source\": \"docs\",\n      \"path\": \"docs/files-guide.md\",\n      \"line_range\": [45, 89],\n      \"title\": \"Uploading Files\"\n    }\n  ],\n  \"confidence\": 0.95,\n  \"metadata\": {\n    \"openapi_version\": \"1.0.0\",\n    \"knowledge_version\": \"1.0.0\",\n    \"generated_at\": \"2025-11-20T12:00:00Z\"\n  }\n}\n```\n\n**Supported Question Types:**\n- Endpoint usage: \"How do I upload a file?\"\n- Workflow guidance: \"How do I create and schedule an agent?\"\n- Feature discovery: \"What agent step types are available?\"\n- Code examples: \"Show me how to use the LLM API\"\n- Best practices: \"How should I structure my SOP?\"\n\n**Response Formats:**\n- `json`: Structured JSON (default, best for programmatic access)\n- `yaml`: YAML format (human-readable)\n- `markdown`: Formatted markdown (best for documentation)\n\n**Error Responses:**\n- `400 Bad Request`: Invalid request format or parameters\n- `401 Unauthorized`: Invalid or missing API key\n\n**Related Endpoints:**\n- `POST /api/v2/assist/chat` - Multi-turn conversation\n- `GET /api/v2/assist/suggest` - Autocomplete suggestions\n- `GET /api/v2/assist/resources` - Knowledge base inventory","operationId":"ask_question_api_v2_assist_ask_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskRequest"}}}},"responses":{"200":{"description":"AI-generated answer with steps, examples, and citations","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Invalid or missing API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/assist/chat":{"post":{"tags":["assist","assist"],"summary":"Chat Conversation","description":"Multi-turn conversation about SOP Engine API documentation.\n\nHave a back-and-forth conversation to get help with the API. Maintains\nconversation context across multiple exchanges.\n\n**IMPORTANT:** This endpoint helps with API DOCUMENTATION only, not your project data.\nFor questions about YOUR uploaded files, use project-specific assist (coming soon).\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Request Body:**\n- `messages` (required): Array of conversation messages with `role` and `content`\n- `response_format` (optional): Output format - \"json\", \"yaml\", or \"markdown\" (default: \"json\")\n- `include_code_examples` (optional): Include code snippets (default: true)\n- `code_languages` (optional): Languages for examples (default: [\"curl\", \"python\"])\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/assist/chat       -H \"Authorization: Bearer YOUR_API_KEY\"       -H \"Content-Type: application/json\"       -d '{\n    \"messages\": [\n      {\"role\": \"user\", \"content\": \"How do I upload a file?\"},\n      {\"role\": \"assistant\", \"content\": \"You can upload files using POST /api/v2/files...\"},\n      {\"role\": \"user\", \"content\": \"Can I index multiple files at once?\"}\n    ],\n    \"response_format\": \"json\",\n    \"include_code_examples\": true\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"answer\": \"Yes, you can index multiple files. After uploading each file separately, you can create a single RAG indexing job that processes all files in a project...\",\n  \"steps\": [...],\n  \"examples\": {...},\n  \"citations\": [...],\n  \"confidence\": 0.92\n}\n```\n\n**Use Cases:**\n- ✅ \"How do I upload a file?\" - API documentation help\n- ✅ \"What endpoints are available?\" - API discovery\n- ✅ \"Show me example code for agents\" - Code examples\n- ✅ \"How do I schedule an agent?\" - Workflow guidance\n- ❌ \"What's in my uploaded files?\" - Use project assist instead\n- ❌ \"Summarize my documents\" - Use project assist instead\n\n**Important Notes:**\n- Server does NOT store conversation history\n- Client must provide full message history in each request\n- Last message must have `role: \"user\"`\n- Conversation focuses on API documentation, not project data\n\n**Error Responses:**\n- `400 Bad Request`: Last message is not from user, or invalid message format\n- `401 Unauthorized`: Invalid or missing API key\n\n**Related Endpoints:**\n- `POST /api/v2/assist/ask` - Single-turn Q&A (simpler, no conversation history)\n- `GET /api/v2/assist/suggest` - Autocomplete suggestions\n- `GET /api/v2/assist/resources` - Knowledge base inventory","operationId":"chat_conversation_api_v2_assist_chat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/src__models__assist__ChatRequest"}}}},"responses":{"200":{"description":"AI-generated answer in multi-turn context","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request - last message must be from user"},"401":{"description":"Invalid or missing API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/assist/resources":{"get":{"tags":["assist","assist"],"summary":"List Resources","description":"Get inventory of knowledge base resources powering the assist endpoints.\n\nReturns metadata about indexed documentation, examples, and API specification\nused to answer questions about the SOP Engine API.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Query Parameters:**\n- None\n\n**Example Request:**\n```bash\ncurl -X GET https://api.taiso.ai/api/v2/assist/resources       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"indexed_at\": \"2025-11-20T10:30:00Z\",\n  \"sources\": {\n    \"docs\": {\n      \"count\": 24,\n      \"total_tokens\": 45000,\n      \"files\": [\n        \"getting-started.md\",\n        \"taiso-agents-guide.md\",\n        \"files-guide.md\"\n      ]\n    },\n    \"examples\": {\n      \"count\": 15,\n      \"total_tokens\": 12000,\n      \"files\": [\n        \"prd_maker_example.py\",\n        \"agents/simple-research.yaml\"\n      ]\n    }\n  },\n  \"openapi\": {\n    \"version\": \"1.0.0\",\n    \"endpoint_count\": 87,\n    \"total_tokens\": 23000\n  },\n  \"total_tokens\": 80000\n}\n```\n\n**Response Fields:**\n- `indexed_at`: Timestamp when knowledge base was last updated\n- `sources.docs`: Documentation markdown files\n- `sources.examples`: Code examples and sample files\n- `openapi`: OpenAPI specification metadata\n- `total_tokens`: Estimated total context size\n\n**Use Cases:**\n- Check knowledge base freshness\n- Verify documentation coverage\n- Debug assist endpoint responses\n- Monitor knowledge base size\n\n**Related Endpoints:**\n- `POST /api/v2/assist/ask` - Ask questions using this knowledge base\n- `POST /api/v2/assist/chat` - Multi-turn conversations","operationId":"list_resources_api_v2_assist_resources_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Knowledge base inventory and metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourcesResponse"}}}},"401":{"description":"Invalid or missing API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/assist/suggest":{"get":{"tags":["assist","assist"],"summary":"Autocomplete Suggestions","description":"Get autocomplete suggestions for API endpoints and workflows.\n\nProvides smart autocomplete suggestions as users type, searching across\nendpoints, workflows, topics, and code examples.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Query Parameters:**\n- `prefix` (required): Search prefix (minimum 1 character)\n\n**Example Request:**\n```bash\ncurl -X GET \"https://api.taiso.ai/api/v2/assist/suggest?prefix=upload\"       -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"suggestions\": [\n    {\n      \"type\": \"endpoint\",\n      \"text\": \"Upload file - POST /api/v2/files\",\n      \"description\": \"Upload a file to a project\",\n      \"relevance\": 0.95\n    },\n    {\n      \"type\": \"workflow\",\n      \"text\": \"Upload and index files\",\n      \"description\": \"Complete workflow for uploading and indexing documents\",\n      \"relevance\": 0.87\n    },\n    {\n      \"type\": \"topic\",\n      \"text\": \"File upload limits\",\n      \"description\": \"Maximum file size and supported formats\",\n      \"relevance\": 0.75\n    }\n  ],\n  \"total\": 3\n}\n```\n\n**Suggestion Types:**\n- `endpoint`: API endpoint matches (e.g., \"POST /api/v2/files\")\n- `workflow`: Multi-step workflow matches (e.g., \"Upload and index files\")\n- `topic`: Documentation topic matches (e.g., \"Authentication\")\n- `example`: Code example matches (e.g., \"Python file upload example\")\n\n**Response Fields:**\n- `suggestions`: Array of suggestion objects\n- `total`: Total number of suggestions returned\n\n**Each Suggestion Contains:**\n- `type`: Suggestion category\n- `text`: Display text for the suggestion\n- `description`: Brief description\n- `relevance`: Relevance score 0.0-1.0 (higher is better)\n\n**Use Cases:**\n- Autocomplete in search boxes\n- Quick endpoint discovery\n- Workflow suggestions\n- Learning common patterns\n\n**Related Endpoints:**\n- `POST /api/v2/assist/ask` - Ask detailed questions\n- `POST /api/v2/assist/chat` - Multi-turn conversations","operationId":"autocomplete_suggestions_api_v2_assist_suggest_get","parameters":[{"name":"prefix","in":"query","required":true,"schema":{"type":"string","title":"Prefix"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of autocomplete suggestions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestResponse"}}}},"401":{"description":"Invalid or missing API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops":{"post":{"tags":["sops"],"summary":"Create Sop","description":"Create a new SOP definition.\n\nCreates a reusable workflow template that can be invoked multiple times\non different data. SOPs are versioned and support two grammar versions.\n\n**Request Body (SOPCreate):**\n- `name` (required): Human-readable name for the SOP\n- `description` (optional): Description of what the SOP does\n- `is_public` (optional): Make SOP publicly accessible (default: false)\n- `definition` (required): Complete SOP definition (v1 or v2 format)\n\n**v2 Definition (spec_version: 2, recommended):**\n- `spec_version`: 2\n- `mode`: \"state-machine\" (default) or \"dag\"\n- `nodes`: Array of processing steps with `id`, `type`, `tool`, `inputs`, `save`, `next`\n- `limits`: Execution limits (`max_nodes_visited`, etc.)\n- Templates: `{{input.*}}`, `{{run.state.*}}`, `{{node_id.output}}`\n\n**v1 Definition (legacy):**\n- `sop_version`: \"2.0\" (DAG) or \"2.1\" (State Machine)\n- `required_fields`: JSON Schema defining required inputs\n- `blocks`: Array with `id`, `name`, `order`, `type`, `depends_on`\n- `settings`: LLM and execution configuration\n\n**Example Request (v2):**\n```json\n{\n    \"name\": \"Web Research\",\n    \"definition\": {\n        \"spec_version\": 2,\n        \"mode\": \"dag\",\n        \"nodes\": [{\n            \"id\": \"search\",\n            \"type\": \"tool\",\n            \"tool\": \"web-search\",\n            \"inputs\": {\"query\": \"{{input.topic}}\"},\n            \"next\": {\"goto\": \"summarize\"}\n        }]\n    }\n}\n```\n\n**Returns:** Created SOP object with ID and version 1\n\n**Errors:**\n- 401: Missing or invalid API key\n- 422: Invalid SOP definition (validation error with details)\n- 500: Internal server error","operationId":"create_sop_api_v2_sops_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOP"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Invalid SOP definition (validation error)"}}},"get":{"tags":["sops"],"summary":"List Sops","description":"List all SOPs the user owns, has access to, or are public.\n\nReturns SOPs the authenticated user can access, with optional filtering\nby visibility (public/shared) or category. Results are paginated.\n\n**Prerequisites:**\n- Valid API key for authentication\n\n**Query Parameters:**\n- `public` (optional): If true, return only public SOPs (default: false)\n- `shared_with_me` (optional): If true, return only SOPs shared with you (default: false)\n- `category_id` (optional): Filter by category ID\n- `limit` (optional): Maximum results per page, 1-100 (default: 50)\n- `offset` (optional): Pagination offset (default: 0)\n\n**Example Request:**\n```\nGET /api/v2/sops?public=true&limit=20&offset=0\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"sop_abc123def456\",\n        \"name\": \"Clinical PRD Generator\",\n        \"description\": \"Generates product requirement documents from clinical trial data\",\n        \"owner_id\": \"usr_xyz789\",\n        \"is_public\": true,\n        \"current_version\": 3,\n        \"definition\": {...},\n        \"created_at\": \"2025-11-15T10:00:00Z\",\n        \"updated_at\": \"2025-12-01T14:30:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n\n**Related Endpoints:**\n- GET /sops/catalog - Browse public SOPs with usage stats\n- GET /sops/categories - List all categories\n- GET /sops/{sop_id} - Get specific SOP details","operationId":"list_sops_api_v2_sops_get","parameters":[{"name":"public","in":"query","required":false,"schema":{"type":"boolean","description":"List only public SOPs","default":false,"title":"Public"},"description":"List only public SOPs"},{"name":"shared_with_me","in":"query","required":false,"schema":{"type":"boolean","description":"List only SOPs shared with me","default":false,"title":"Shared With Me"},"description":"List only SOPs shared with me"},{"name":"category_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category ID","title":"Category Id"},"description":"Filter by category ID"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results per page","default":50,"title":"Limit"},"description":"Max results per page"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset","default":0,"title":"Offset"},"description":"Pagination offset"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SOP"},"title":"Response List Sops Api V2 Sops Get"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/catalog":{"get":{"tags":["sops"],"summary":"Get Catalog","description":"Browse SOP catalog with public and shared SOPs.\n\nReturns a curated catalog of SOPs sorted by popularity (usage count).\nIncludes both public SOPs and SOPs shared with the authenticated user.\nSupports category filtering and text search.\n\n**Prerequisites:**\n- Valid API key for authentication\n\n**Query Parameters:**\n- `category_id` (optional): Filter by category ID\n- `search` (optional): Search query (matches name and description)\n- `page` (optional): Page number, starts at 1 (default: 1)\n- `page_size` (optional): Results per page, 1-100 (default: 50)\n\n**Example Request:**\n```\nGET /api/v2/sops/catalog?search=clinical&page=1&page_size=20\n```\n\n**Example Response:**\n```json\n{\n    \"sops\": [\n        {\n            \"id\": \"sop_abc123def456\",\n            \"name\": \"Clinical PRD Generator\",\n            \"description\": \"Generates product requirement documents\",\n            \"owner\": \"SOP Engine Team\",\n            \"owner_id\": \"usr_xyz789\",\n            \"category\": \"Clinical Workflows\",\n            \"usage_count\": 127,\n            \"avg_rating\": 4.8,\n            \"is_public\": true,\n            \"version\": 3,\n            \"created_at\": \"2025-11-15T10:00:00Z\"\n        }\n    ],\n    \"total\": 45,\n    \"page\": 1,\n    \"page_size\": 20\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n\n**Related Endpoints:**\n- GET /sops/categories - List all categories with counts\n- GET /sops - List your owned/accessible SOPs\n- GET /sops/{sop_id} - Get specific SOP details","operationId":"get_catalog_api_v2_sops_catalog_get","parameters":[{"name":"category_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category Id"},"description":"Filter by category"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search query","title":"Search"},"description":"Search query"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page","default":50,"title":"Page Size"},"description":"Results per page"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPCatalogResponse"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/categories":{"get":{"tags":["sops"],"summary":"List Categories","description":"List all SOP categories with SOP counts.\n\nReturns a complete list of available SOP categories. No authentication\nrequired - this is a public endpoint for category discovery.\n\n**Prerequisites:**\n- None (public endpoint)\n\n**Example Request:**\n```\nGET /api/v2/sops/categories\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"cat_clinical\",\n        \"name\": \"Clinical Workflows\",\n        \"description\": \"Clinical trial and medical research workflows\",\n        \"icon\": \"medical\",\n        \"parent_id\": null,\n        \"sop_count\": 23,\n        \"created_at\": \"2025-01-15T10:00:00Z\"\n    },\n    {\n        \"id\": \"cat_research\",\n        \"name\": \"Research & Analysis\",\n        \"description\": \"Data analysis and research SOPs\",\n        \"icon\": \"chart\",\n        \"parent_id\": null,\n        \"sop_count\": 15,\n        \"created_at\": \"2025-01-15T10:00:00Z\"\n    }\n]\n```\n\n**Related Endpoints:**\n- GET /sops/categories/{category_name} - Get SOPs in a category\n- GET /sops/catalog - Browse all SOPs with filtering","operationId":"list_categories_api_v2_sops_categories_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SOPCategory"},"type":"array","title":"Response List Categories Api V2 Sops Categories Get"}}}}}}},"/api/v2/sops/categories/{category_name}":{"get":{"tags":["sops"],"summary":"Get Sops By Category","description":"Get all SOPs in a specific category.\n\nReturns SOPs in the specified category that are either public or\nshared with the authenticated user. Results are paginated.\n\n**Prerequisites:**\n- Valid API key for authentication\n- Category must exist\n\n**Path Parameters:**\n- `category_name` (required): Name of the category (e.g., \"Clinical Workflows\")\n\n**Query Parameters:**\n- `limit` (optional): Maximum results per page, 1-100 (default: 50)\n- `offset` (optional): Pagination offset (default: 0)\n\n**Example Request:**\n```\nGET /api/v2/sops/categories/Clinical%20Workflows?limit=20\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"sop_abc123def456\",\n        \"name\": \"Clinical PRD Generator\",\n        \"description\": \"Generates product requirement documents\",\n        \"owner_id\": \"usr_xyz789\",\n        \"is_public\": true,\n        \"current_version\": 3,\n        \"definition\": {...},\n        \"created_at\": \"2025-11-15T10:00:00Z\",\n        \"updated_at\": \"2025-12-01T14:30:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: Category not found\n\n**Related Endpoints:**\n- GET /sops/categories - List all categories\n- GET /sops/catalog - Browse all SOPs with filtering\n- GET /sops/{sop_id} - Get specific SOP details","operationId":"get_sops_by_category_api_v2_sops_categories__category_name__get","parameters":[{"name":"category_name","in":"path","required":true,"schema":{"type":"string","title":"Category Name"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SOP"},"title":"Response Get Sops By Category Api V2 Sops Categories  Category Name  Get"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Category not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/validate-sop-schema":{"post":{"tags":["sops"],"summary":"Validate Sop Schema","description":"Validate an SOP definition against the grammar schema (SES-161).\n\nUse this endpoint to validate SOP definitions before creating them.\nSupports both v1 (blocks) and v2 (nodes) grammar formats.\nReturns detailed errors with suggestions for fixes.\n\n**Request Body (ValidateSOPSchemaRequest):**\n- `definition` (required): Complete SOP definition to validate (v1 or v2)\n- `strict` (optional): Treat warnings as errors (default: true)\n\n**Validation Checks (v2, spec_version: 2):**\n- Structure: Required fields (spec_version, nodes)\n- Mode: \"state-machine\" (default) or \"dag\"\n- Nodes: Valid types (tool, llm, decision), valid `next` routing\n- Templates: `{{...}}` syntax in inputs/save fields\n- Limits: max_nodes_visited required for sm mode\n\n**Validation Checks (v1, legacy):**\n- Structure: Required fields (sop_version, required_fields, blocks)\n- Blocks: Valid types, order, depends_on, cycle detection\n\n**Example Request (v2):**\n```json\n{\n    \"definition\": {\n        \"spec_version\": 2,\n        \"mode\": \"dag\",\n        \"nodes\": [{\n            \"id\": \"search\",\n            \"type\": \"tool\",\n            \"tool\": \"web-search\",\n            \"inputs\": {\"query\": \"{{input.topic}}\"},\n            \"next\": {\"goto\": \"summarize\"}\n        }]\n    },\n    \"strict\": true\n}\n```\n\n**Success Response (200):**\n```json\n{\n    \"valid\": true,\n    \"spec_version\": 2,\n    \"mode\": \"dag\",\n    \"node_count\": 1,\n    \"errors\": [],\n    \"warnings\": [],\n    \"error_count\": 0,\n    \"warning_count\": 0\n}\n```\n\n**Error Response (200 with valid=false):**\n```json\n{\n    \"valid\": false,\n    \"errors\": [{\n        \"code\": \"MISSING_NEXT_TARGET\",\n        \"message\": \"Node 'search' references unknown target 'missing_node'\",\n        \"path\": \"nodes\",\n        \"node_id\": \"search\",\n        \"suggestion\": \"Check that the goto target exists in the nodes array\"\n    }]\n}\n```","operationId":"validate_sop_schema_api_v2_sops_validate_sop_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateSOPSchemaRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateSOPSchemaResponse"}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Invalid request body"}}}},"/api/v2/sops/generate-sop-schema":{"post":{"tags":["sops"],"summary":"Generate Sop Schema","description":"Generate an SOP definition from a natural language description (SES-161).\n\nUses LLM to convert workflow descriptions into valid SOP definitions.\nGenerates v2 grammar (spec_version: 2) by default. Supports iterative\nrefinement via conversation_id.\n\n**Request Body (GenerateSOPSchemaRequest):**\n- `description` (required): Natural language description of the workflow\n- `hints` (optional): Generation hints (inputs, tools, output_format, mode, spec_version)\n- `conversation_id` (optional): ID to continue refining a previous generation\n- `feedback` (optional): Feedback on previous generation for refinement\n\n**Hints Object:**\n```json\n{\n    \"inputs\": [\"topic\", \"max_results\"],\n    \"tools\": [\"web-search\", \"llm-chat\"],\n    \"output_format\": \"markdown\",\n    \"mode\": \"dag\",\n    \"spec_version\": 2\n}\n```\n\n**Example Request (new generation):**\n```json\n{\n    \"description\": \"Search the web for a topic and summarize the results\",\n    \"hints\": {\n        \"inputs\": [\"topic\"],\n        \"tools\": [\"web-search\"]\n    }\n}\n```\n\n**Example Request (refinement):**\n```json\n{\n    \"conversation_id\": \"conv_abc123\",\n    \"feedback\": \"Add error handling if no search results are found\"\n}\n```\n\n**Response:**\n- `definition`: Generated SOP definition (validated, v2 format with nodes/next/inputs)\n- `conversation_id`: ID for iterative refinement\n- `validation`: Validation result of the generated definition\n- `suggestions`: Improvement suggestions from the LLM\n- `model_used`: LLM model used\n- `tokens_used`: Tokens consumed\n- `generation_time_ms`: Generation time in milliseconds\n\n**Notes:**\n- Generated definitions are automatically validated\n- Only registered tools can be used (see /tools endpoint)\n- For sm mode, limits.max_nodes_visited is automatically added if missing\n- Use `hints.spec_version: 1` to generate legacy v1 format","operationId":"generate_sop_schema_api_v2_sops_generate_sop_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSOPSchemaRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSOPSchemaResponse"}}}},"422":{"description":"Generation failed after all SLAF retry attempts","content":{"application/json":{"example":{"detail":{"error":"GENERATION_FAILED","message":"LLM generated SOP failed schema validation after 5 attempts","last_error":"Additional properties are not allowed ('extra_field' was unexpected)","attempts":5,"stage":"schema_validation","validation_path":"root -> nodes -> 0"}}}}}}}},"/api/v2/sops/generate-decision-block":{"post":{"tags":["sops"],"summary":"Generate Decision Block","description":"Generate a decision block configuration from a natural language description (SES-161).\n\nUses LLM to convert a routing/triage description into a valid DecisionRequest\nconfiguration. Validates output against the DecisionRequest schema using a\nSLAF retry loop (up to 5 attempts). Supports iterative refinement via\nconversation_id.\n\n**Request Body (GenerateDecisionBlockRequest):**\n- `description` (required): Natural language description of the decision logic\n- `hints` (optional): Hints like `strategy`, `num_choices`, `input_fields`\n- `conversation_id` (optional): ID to continue refining a previous generation\n- `feedback` (optional): Feedback on previous generation for refinement\n\n**Hints Object:**\n```json\n{\n    \"strategy\": \"llm\",\n    \"num_choices\": 3,\n    \"input_fields\": [\"category\", \"priority\"]\n}\n```\n\n**Example Request (new generation):**\n```json\n{\n    \"description\": \"Route customer tickets by category: billing, support, or general inquiry\",\n    \"hints\": {\"strategy\": \"llm\", \"num_choices\": 3}\n}\n```\n\n**Example Request (refinement):**\n```json\n{\n    \"conversation_id\": \"dconv_abc123\",\n    \"feedback\": \"Add a 'technical' category for engineering issues\"\n}\n```\n\n**Response:**\n- `decision_block`: Generated DecisionRequest-compatible configuration\n- `conversation_id`: ID for iterative refinement\n- `valid`: Whether the block passed DecisionRequest schema validation\n- `validation_errors`: Validation errors (if any)\n- `suggestions`: Improvement suggestions from the LLM\n- `model_used`: LLM model used\n- `tokens_used`: Tokens consumed\n- `generation_time_ms`: Generation time in milliseconds\n\n**Notes:**\n- Generated blocks are validated against the DecisionRequest schema\n- Supports all strategies: llm, code, expr, hybrid\n- Default choice is always included in output\n- Use hints.strategy to control the decision strategy","operationId":"generate_decision_block_api_v2_sops_generate_decision_block_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateDecisionBlockRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateDecisionBlockResponse"}}}},"422":{"description":"Generation failed after all SLAF retry attempts","content":{"application/json":{"example":{"detail":{"error":"GENERATION_FAILED","message":"LLM returned invalid JSON after 5 attempts","last_error":"Expecting ',' delimiter: line 12 column 5","attempts":5,"stage":"json_syntax"}}}}}}}},"/api/v2/sops/webhook-callback-spec":{"get":{"tags":["sops"],"summary":"[Reference] SOP run completion webhook payload","description":"Return a **sample** `SOPRunWebhookPayload` -- the JSON body Taiso POSTs to your `webhook_url`.\n\n**This is documentation for integrators.** It is not a webhook receiver; it shows the\nexact JSON your server will receive when a SOP run reaches a terminal state.\n\n**Payload fields:**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `event` | `\"sop_run.completed\"` \\| `\"sop_run.failed\"` | Event type matching terminal status |\n| `run_id` | string | SOP run identifier |\n| `sop_id` | string | SOP identifier |\n| `project_id` | string | Project that owns the run |\n| `status` | `\"completed\"` \\| `\"failed\"` | Terminal status that triggered this callback |\n| `completed_at` | string (ISO 8601) | Timestamp when the run reached terminal state |\n| `error_message` | string \\| null | Human-readable error summary on failure; null on success |\n| `output_file_ids` | array \\| null | Output file IDs on success; null on failure |\n\n**Delivery details:**\n- Method: `POST` to your `webhook_url`\n- Content-Type: `application/json`\n- Timeout: 10 seconds -- respond with `2xx` quickly, process async\n- Retries: Up to 3 attempts (30s, 120s, 300s backoff)\n- Guarantee: At-least-once -- deduplicate on `(run_id, status)` tuple\n- Failure: Delivery failure does **not** change run status\n\nPass `?event_type=failed` to see the failure payload shape.","operationId":"get_sop_webhook_callback_spec_api_v2_sops_webhook_callback_spec_get","parameters":[{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Return a `\"failed\"` sample instead of the default `\"completed\"` sample.","enum":["completed","failed"],"title":"Event Type"},"description":"Return a `\"failed\"` sample instead of the default `\"completed\"` sample."},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Sample webhook payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPRunWebhookPayload"},"examples":{"completed":{"summary":"Successful SOP run completion","value":{"event":"sop_run.completed","run_id":"srun_01JDX2P4ABCDE","sop_id":"sop_01JDWXYZ","project_id":"prj_1a2b3c4d5e6f","status":"completed","completed_at":"2026-06-14T12:00:00Z","output_file_ids":["fil_abc123","fil_def456"]}},"failed":{"summary":"Failed SOP run notification","value":{"event":"sop_run.failed","run_id":"srun_01JDX2P4FGHIJ","sop_id":"sop_01JDWXYZ","project_id":"prj_1a2b3c4d5e6f","status":"failed","completed_at":"2026-06-14T12:05:00Z","error_message":"Node 'extract_data' failed: timeout"}}}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}":{"get":{"tags":["sops"],"summary":"Get Sop","description":"Get SOP definition by ID.\n\nRetrieves the complete SOP definition including metadata, blocks/nodes,\nand settings. Returns the latest version unless a specific version is\nrequested via /sops/{sop_id}/versions/{version}.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have viewer, executor, or editor permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Example Request:**\n```\nGET /api/v2/sops/sop_abc123def456\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"sop_abc123def456\",\n    \"name\": \"Clinical PRD Generator\",\n    \"description\": \"Generates product requirement documents from clinical trial data\",\n    \"owner_id\": \"usr_xyz789\",\n    \"is_public\": false,\n    \"current_version\": 3,\n    \"definition\": {\n        \"spec_version\": 2,\n        \"sop_version\": \"2.1\",\n        \"mode\": \"dag\",\n        \"required_fields\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"indication\": {\"type\": \"string\"}\n            },\n            \"required\": [\"indication\"]\n        },\n        \"nodes\": [\n            {\n                \"id\": \"extract_data\",\n                \"type\": \"llm\",\n                \"inputs\": {\"document\": \"{{input.indication}}\"},\n                \"next\": {\"goto\": \"generate_prd\"}\n            }\n        ]\n    },\n    \"created_at\": \"2025-11-15T10:00:00Z\",\n    \"updated_at\": \"2025-12-01T14:30:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view this SOP\n- 404: SOP not found or user has no access\n\n**Related Endpoints:**\n- GET /sops - List all accessible SOPs\n- GET /sops/{sop_id}/versions - List all versions\n- PUT /sops/{sop_id} - Update SOP definition\n- POST /sops/{sop_id}/invoke - Execute the SOP","operationId":"get_sop_api_v2_sops__sop_id__get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOP"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["sops"],"summary":"Update Sop","description":"Update SOP definition.\n\nUpdates SOP metadata and/or definition. When the definition is changed,\nthe version is automatically incremented. Breaking changes (e.g., removing\nrequired fields, changing block types) generate warnings in the response.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have editor permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Request Body (SOPUpdate):**\n- `name` (optional): New SOP name\n- `description` (optional): New description\n- `is_public` (optional): Change visibility\n- `definition` (optional): Updated SOP definition (triggers version increment)\n- `category_ids` (optional): Updated category assignments\n- `change_summary` (optional): Summary of changes for version history\n\n**Example Request:**\n```json\n{\n    \"name\": \"Clinical PRD Generator v2\",\n    \"definition\": {\n        \"spec_version\": 2,\n        \"mode\": \"dag\",\n        \"nodes\": [...]\n    },\n    \"change_summary\": \"Added error handling for missing data\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"sop_abc123def456\",\n    \"name\": \"Clinical PRD Generator v2\",\n    \"current_version\": 4,\n    \"previous_version\": 3,\n    \"updated_at\": \"2025-12-15T16:45:00Z\",\n    \"warnings\": [\n        {\n            \"type\": \"breaking_change\",\n            \"message\": \"Removed required field 'phase' from input schema\",\n            \"severity\": \"high\"\n        }\n    ]\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks editor permission\n- 404: SOP not found\n- 422: Invalid definition (validation failed)\n\n**Related Endpoints:**\n- GET /sops/{sop_id} - Get current SOP\n- GET /sops/{sop_id}/versions - View version history\n- POST /sops/{sop_id}/rollback - Rollback to previous version","operationId":"update_sop_api_v2_sops__sop_id__put","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPUpdateResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (editor required)"},"404":{"description":"SOP not found"},"422":{"description":"Invalid SOP definition"}}},"patch":{"tags":["sops"],"summary":"Patch Sop","description":"Partially update SOP (SES-203).\n\nUpdates only the provided fields, leaving others unchanged. This is useful\nwhen you only need to update name, description, or visibility without\nproviding the full definition.\n\n**Prerequisites:**\n- Valid API key required\n- User must have editor or owner permission\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier\n\n**Request Body (all fields optional):**\n- `name`: New name for the SOP\n- `description`: New description\n- `is_public`: Change visibility\n- `definition`: New definition (creates new version)\n- `change_summary`: Summary of changes (for version history)\n\n**Example Request:**\n```json\n{\n    \"description\": \"Updated description only\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks editor permission\n- 404: SOP not found\n- 422: Invalid data\n\n**Related Endpoints:**\n- PUT /sops/{sop_id} - Full update (same behavior, different HTTP method)\n- GET /sops/{sop_id} - Get current SOP","operationId":"patch_sop_api_v2_sops__sop_id__patch","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPUpdateResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (editor required)"},"404":{"description":"SOP not found"},"422":{"description":"Invalid SOP data"}}},"delete":{"tags":["sops"],"summary":"Delete Sop","description":"Delete SOP.\n\nPermanently deletes an SOP and all associated data including version\nhistory, permissions, and run records. This operation cannot be undone.\nOnly the SOP owner can delete it.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must be the SOP owner (not just editor)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Example Request:**\n```\nDELETE /api/v2/sops/sop_abc123def456\n```\n\n**Example Response:**\n```\n204 No Content\n(empty body)\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: Only the owner can delete an SOP\n- 404: SOP not found\n\n**Related Endpoints:**\n- GET /sops/{sop_id} - Get SOP details before deleting\n- GET /sops - List all SOPs","operationId":"delete_sop_api_v2_sops__sop_id__delete","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"SOP deleted successfully"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Only owner can delete SOP"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/versions":{"get":{"tags":["sops"],"summary":"List Sop Versions","description":"List all versions of an SOP.\n\nReturns complete version history for an SOP, including metadata about\neach version (creation time, author, change summary, breaking changes).\nVersions are numbered sequentially starting from 1.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have viewer permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Example Request:**\n```\nGET /api/v2/sops/sop_abc123def456/versions\n```\n\n**Example Response:**\n```json\n{\n    \"sop_id\": \"sop_abc123def456\",\n    \"name\": \"Clinical PRD Generator\",\n    \"current_version\": 3,\n    \"versions\": [\n        {\n            \"version\": 1,\n            \"created_at\": \"2025-11-15T10:00:00Z\",\n            \"created_by\": \"usr_xyz789\",\n            \"change_summary\": \"Initial version\",\n            \"breaking_changes\": false\n        },\n        {\n            \"version\": 2,\n            \"created_at\": \"2025-11-20T14:30:00Z\",\n            \"created_by\": \"usr_xyz789\",\n            \"change_summary\": \"Added error handling\",\n            \"breaking_changes\": false\n        },\n        {\n            \"version\": 3,\n            \"created_at\": \"2025-12-01T09:15:00Z\",\n            \"created_by\": \"usr_abc456\",\n            \"change_summary\": \"Updated input schema\",\n            \"breaking_changes\": true\n        }\n    ]\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view this SOP\n- 404: SOP not found\n\n**Related Endpoints:**\n- GET /sops/{sop_id}/versions/{version} - Get specific version\n- POST /sops/{sop_id}/rollback - Rollback to previous version\n- GET /sops/{sop_id} - Get current version","operationId":"list_sop_versions_api_v2_sops__sop_id__versions_get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPVersionList"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/versions/{version}":{"get":{"tags":["sops"],"summary":"Get Sop Version","description":"Get specific version of an SOP.\n\nRetrieves the complete SOP definition as it existed at a specific version\nnumber. Useful for comparing versions, auditing changes, or preparing\nto rollback.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- Version must exist\n- User must have viewer permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n- `version` (required): Version number (integer, starting from 1)\n\n**Example Request:**\n```\nGET /api/v2/sops/sop_abc123def456/versions/2\n```\n\n**Example Response:**\n```json\n{\n    \"sop_id\": \"sop_abc123def456\",\n    \"version\": 2,\n    \"definition\": {\n        \"spec_version\": 2,\n        \"sop_version\": \"2.1\",\n        \"mode\": \"dag\",\n        \"required_fields\": {...},\n        \"nodes\": [...]\n    },\n    \"created_at\": \"2025-11-20T14:30:00Z\",\n    \"created_by\": \"usr_xyz789\",\n    \"change_summary\": \"Added error handling for edge cases\",\n    \"breaking_changes\": false\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view this SOP\n- 404: SOP not found or version doesn't exist\n\n**Related Endpoints:**\n- GET /sops/{sop_id}/versions - List all versions\n- POST /sops/{sop_id}/rollback - Rollback to this version\n- GET /sops/{sop_id} - Get current version","operationId":"get_sop_version_api_v2_sops__sop_id__versions__version__get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","title":"Version"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPVersion"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions"},"404":{"description":"SOP or version not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/rollback":{"post":{"tags":["sops"],"summary":"Rollback Sop","description":"Rollback SOP to a previous version.\n\nCreates a new version that restores the definition from a previous version.\nThis is a non-destructive operation - the version history is preserved,\nand a new version is created with the old definition.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- Target version must exist\n- User must have editor permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Query Parameters:**\n- `target_version` (required): Version number to restore (e.g., 2)\n\n**Example Request:**\n```\nPOST /api/v2/sops/sop_abc123def456/rollback?target_version=2\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"sop_abc123def456\",\n    \"name\": \"Clinical PRD Generator\",\n    \"current_version\": 5,\n    \"previous_version\": 4,\n    \"updated_at\": \"2025-12-15T18:00:00Z\",\n    \"warnings\": [\n        {\n            \"type\": \"rollback\",\n            \"message\": \"Rolled back to version 2\",\n            \"severity\": \"info\"\n        }\n    ]\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks editor permission\n- 404: SOP or target version not found\n\n**Related Endpoints:**\n- GET /sops/{sop_id}/versions - List all versions\n- GET /sops/{sop_id}/versions/{version} - Preview version before rollback\n- PUT /sops/{sop_id} - Update SOP normally","operationId":"rollback_sop_api_v2_sops__sop_id__rollback_post","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"target_version","in":"query","required":true,"schema":{"type":"integer","description":"Version number to rollback to","title":"Target Version"},"description":"Version number to rollback to"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPUpdateResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (editor required)"},"404":{"description":"SOP or target version not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/permissions":{"post":{"tags":["sops"],"summary":"Grant Permission","description":"Grant permission to another user.\n\nShares an SOP with another user by granting them a specific role.\nMultiple users can have different permission levels. The grantor\nmust have editor permission.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have editor permission (or be owner)\n- Target user must exist\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Request Body (SOPPermissionCreate):**\n- `user_id` (required): User ID to grant permission to\n- `role` (required): Permission level - \"viewer\", \"executor\", or \"editor\"\n\n**Roles:**\n- `viewer`: Can view SOP definition and version history\n- `executor`: Can view and invoke SOP (run executions)\n- `editor`: Can view, invoke, and modify SOP (update definition, grant permissions)\n\n**Example Request:**\n```json\n{\n    \"user_id\": \"usr_teammate123\",\n    \"role\": \"executor\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"perm_xyz789abc\",\n    \"sop_id\": \"sop_abc123def456\",\n    \"user_id\": \"usr_teammate123\",\n    \"role\": \"executor\",\n    \"created_at\": \"2025-12-15T10:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 400: Invalid user_id or role value\n- 401: Missing or invalid API key\n- 403: User lacks editor permission\n- 404: SOP not found\n\n**Related Endpoints:**\n- GET /sops/{sop_id}/permissions - List all permissions\n- DELETE /sops/{sop_id}/permissions/{user_id} - Revoke permission","operationId":"grant_permission_api_v2_sops__sop_id__permissions_post","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPPermissionCreate"}}}},"responses":{"201":{"description":"Permission granted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPPermission"}}}},"400":{"description":"Invalid user_id or role"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (editor required)"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["sops"],"summary":"List Permissions","description":"List all permissions for an SOP.\n\nReturns all users who have been granted access to this SOP and their\npermission levels. The owner is not included in this list (they have\nimplicit full access).\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have viewer permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Example Request:**\n```\nGET /api/v2/sops/sop_abc123def456/permissions\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"perm_xyz789abc\",\n        \"sop_id\": \"sop_abc123def456\",\n        \"user_id\": \"usr_teammate123\",\n        \"role\": \"executor\",\n        \"created_at\": \"2025-12-10T14:00:00Z\"\n    },\n    {\n        \"id\": \"perm_def456ghi\",\n        \"sop_id\": \"sop_abc123def456\",\n        \"user_id\": \"usr_analyst456\",\n        \"role\": \"viewer\",\n        \"created_at\": \"2025-12-12T09:30:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view this SOP\n- 404: SOP not found\n\n**Related Endpoints:**\n- POST /sops/{sop_id}/permissions - Grant permission to a user\n- DELETE /sops/{sop_id}/permissions/{user_id} - Revoke permission","operationId":"list_permissions_api_v2_sops__sop_id__permissions_get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SOPPermission"},"title":"Response List Permissions Api V2 Sops  Sop Id  Permissions Get"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/permissions/{user_id}":{"delete":{"tags":["sops"],"summary":"Revoke Permission","description":"Revoke permission from a user.\n\nRemoves a user's access to an SOP by deleting their permission record.\nThe user account itself is not affected - only their access to this\nspecific SOP is removed.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have editor permission (or be owner)\n- Permission record must exist for the target user\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n- `user_id` (required): User ID whose permission to revoke\n\n**Example Request:**\n```\nDELETE /api/v2/sops/sop_abc123def456/permissions/usr_teammate123\n```\n\n**Example Response:**\n```\n204 No Content\n(empty body)\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks editor permission\n- 404: SOP or permission not found\n\n**Related Endpoints:**\n- GET /sops/{sop_id}/permissions - List all permissions\n- POST /sops/{sop_id}/permissions - Grant permission to a user","operationId":"revoke_permission_api_v2_sops__sop_id__permissions__user_id__delete","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Permission revoked successfully"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (editor required)"},"404":{"description":"SOP or permission not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/stats":{"get":{"tags":["sops"],"summary":"Get Usage Stats","description":"Get usage statistics for an SOP.\n\nReturns aggregate usage metrics including invocation counts, success/failure\nrates, average execution duration, and cost data. Stats are computed from\nall historical run records.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist (no permission check - stats are public for accessible SOPs)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Example Request:**\n```\nGET /api/v2/sops/sop_abc123def456/stats\n```\n\n**Example Response:**\n```json\n{\n    \"sop_id\": \"sop_abc123def456\",\n    \"total_invocations\": 127,\n    \"successful_runs\": 119,\n    \"failed_runs\": 8,\n    \"avg_duration_seconds\": 145.3,\n    \"avg_cost_usd\": 0.42,\n    \"last_used_at\": \"2025-12-14T16:30:00Z\",\n    \"updated_at\": \"2025-12-15T10:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: SOP not found\n\n**Related Endpoints:**\n- GET /sops/{sop_id} - Get SOP details\n- GET /sops/{sop_id}/runs - List execution history","operationId":"get_usage_stats_api_v2_sops__sop_id__stats_get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPUsageStats"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/validate":{"post":{"tags":["sops"],"summary":"Validate Sop Data","description":"Validate data completeness without executing SOP.\n\nChecks whether the provided data (from files and/or manual inputs) satisfies\nthe SOP's required_fields schema. Returns a completeness score, extracted\ndata, and suggestions for missing fields. This is a dry-run operation that\ndoes not create any runs or jobs.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have executor permission (or be owner)\n- Project must exist and be accessible\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Request Body (SOPValidateRequest):**\n- `project_id` (required): Project containing input files\n- `input_file_ids` (optional): Specific file IDs to validate against\n- `input_paths` (optional): Paths within project to scan for files\n- `use_all_files` (optional): If true, use all project files (default: false)\n- `manual_data` (optional): Manual values for required fields (overrides file data)\n\n**Example Request:**\n```json\n{\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"input_file_ids\": [\"fil_aaa111\", \"fil_bbb222\"],\n    \"manual_data\": {\n        \"indication\": \"ulcerative colitis\",\n        \"phase\": \"Phase II\"\n    }\n}\n```\n\n**Example Response:**\n```json\n{\n    \"is_complete\": false,\n    \"completeness_score\": 0.75,\n    \"extracted_data\": {\n        \"indication\": \"ulcerative colitis\",\n        \"phase\": \"Phase II\",\n        \"duration_weeks\": 12\n    },\n    \"data_sources\": {\n        \"indication\": {\n            \"value\": \"ulcerative colitis\",\n            \"source\": \"manual\",\n            \"confidence\": 1.0\n        },\n        \"duration_weeks\": {\n            \"value\": 12,\n            \"source\": \"fil_aaa111\",\n            \"confidence\": 0.92\n        }\n    },\n    \"missing_fields\": [\n        {\n            \"field\": \"primary_endpoint\",\n            \"description\": \"Primary efficacy endpoint\",\n            \"type\": \"string\",\n            \"required\": true,\n            \"searched_in\": [\"fil_aaa111\", \"fil_bbb222\"],\n            \"suggestions\": [\"Add to manual_data or upload trial protocol\"]\n        }\n    ]\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks executor permission\n- 404: SOP or project not found\n\n**Related Endpoints:**\n- POST /sops/{sop_id}/invoke - Execute SOP after validation passes\n- GET /sops/{sop_id} - View SOP required_fields schema","operationId":"validate_sop_data_api_v2_sops__sop_id__validate_post","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPValidateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPValidateResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (executor required)"},"404":{"description":"SOP or project not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/invoke":{"post":{"tags":["sops"],"summary":"Invoke Sop","description":"Invoke SOP execution on specified data.\n\nStarts asynchronous execution of an SOP on provided input data. Validates\ndata completeness first, then creates a background job. Returns immediately\nwith a run_id to track progress. Use the Idempotency-Key header to prevent\nduplicate executions.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have executor permission (or be owner)\n- Project must exist and be accessible\n- Input files must belong to the project\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Request Body (SOPInvokeRequest):**\n- `project_id` (required): Project containing input files\n- `sop_ref` (optional): Specific version reference (e.g., \"sop_abc:3\")\n- `version` (optional): Version number (alternative to sop_ref)\n- `input_file_ids` (optional): Specific file IDs to process\n- `input_paths` (optional): Paths within project to scan\n- `use_all_files` (optional): Process all project files (default: false)\n- `manual_data` (optional): Manual field values (overrides extracted data)\n- `output_path` (required): Workspace directory for outputs (e.g., \"/outputs/prd\")\n- `execution_mode` (optional): \"strict\", \"relaxed\", or \"custom\" (default: \"strict\")\n- `execution_config` (optional): Custom config for execution_mode=\"custom\"\n\n**Headers:**\n- `Idempotency-Key` (optional): Unique key to prevent duplicate runs\n\n**Execution Modes:**\n- `strict`: All required_fields must be present, SLAF audit enabled (default)\n- `relaxed`: Allows partial data, uses placeholders for missing fields\n- `custom`: Fine-grained control via execution_config\n\n**Timeout and Retry Behavior:**\nSOP execution is subject to tier-based timeout limits:\n- `free` tier: 300 seconds (5 minutes) - allows 2-3 LLM calls with retries\n- `pro` tier: 600 seconds (10 minutes)\n- `enterprise` tier: 3600 seconds (1 hour)\n\nIndividual LLM calls within the SOP have:\n- Per-call timeout: 90 seconds (prevents hung requests)\n- Automatic retry: 2 retries with exponential backoff (1s, 2s delays)\n- Retries only on transient errors: timeouts, rate limits (429), server errors (500-504)\n\nIf a job times out, check the job status for details and consider:\n- Simplifying the SOP (fewer nodes)\n- Upgrading to a higher tier for longer timeouts\n- Breaking complex SOPs into smaller, sequential runs\n\n**Example Request:**\n```json\nPOST /api/v2/sops/sop_abc123def456/invoke\nHeaders: {\n    \"Idempotency-Key\": \"run-2025-12-15-001\"\n}\nBody: {\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"input_file_ids\": [\"fil_aaa111\", \"fil_bbb222\"],\n    \"manual_data\": {\n        \"indication\": \"ulcerative colitis\"\n    },\n    \"output_path\": \"/outputs/clinical-prd\",\n    \"execution_mode\": \"strict\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"run_id\": \"run_xyz789abc456\",\n    \"job_id\": \"job_def123ghi789\",\n    \"status\": \"pending\",\n    \"message\": \"SOP execution queued. Data completeness score: 1.0.\",\n    \"completeness_score\": 1.0,\n    \"missing_fields\": [],\n    \"estimated_duration\": \"2-3 minutes\",\n    \"blocks_total\": 5\n}\n```\n\n**Error Responses:**\n- 400: Invalid file IDs or they don't belong to the project\n- 401: Missing or invalid API key\n- 403: User lacks executor permission or project access\n- 404: SOP or project not found\n- 409: Idempotency key already used (check existing run)\n- 422: Missing required fields in strict mode\n\n**Related Endpoints:**\n- POST /sops/{sop_id}/validate - Validate data before invoking\n- GET /sops/runs/{run_id} - Check run status\n- GET /sops/runs/{run_id}/trace - View execution trace\n- DELETE /sops/runs/{run_id} - Cancel running execution","operationId":"invoke_sop_api_v2_sops__sop_id__invoke_post","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPInvokeRequest"}}}},"responses":{"202":{"description":"SOP execution started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPInvokeResponse"}}}},"400":{"description":"Invalid file IDs or request"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions (executor required)"},"404":{"description":"SOP or project not found"},"409":{"description":"Duplicate idempotency key"},"422":{"description":"Missing required fields (strict mode)"}}}},"/api/v2/sops/{sop_id}/runs":{"get":{"tags":["sops"],"summary":"List Sop Runs","description":"List execution history for an SOP.\n\nReturns all runs the authenticated user has initiated for this SOP,\nordered by creation time (newest first). Supports filtering by status\nand pagination.\n\n**Prerequisites:**\n- Valid API key for authentication\n- SOP must exist\n- User must have viewer permission (or be owner)\n\n**Path Parameters:**\n- `sop_id` (required): SOP identifier (sop_ prefix)\n\n**Query Parameters:**\n- `status` (optional): Filter by status - \"pending\", \"running\", \"completed\", \"failed\", \"cancelled\"\n- `limit` (optional): Maximum results per page, 1-100 (default: 50)\n- `offset` (optional): Pagination offset (default: 0)\n\n**Example Request:**\n```\nGET /api/v2/sops/sop_abc123def456/runs?status=completed&limit=10\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"run_xyz789abc456\",\n        \"sop_id\": \"sop_abc123def456\",\n        \"user_id\": \"usr_current\",\n        \"job_id\": \"job_def123ghi789\",\n        \"project_id\": \"prj_1234567890abcdef\",\n        \"status\": \"completed\",\n        \"execution_mode\": \"strict\",\n        \"created_at\": \"2025-12-14T10:30:00Z\",\n        \"completed_at\": \"2025-12-14T10:32:45Z\"\n    },\n    {\n        \"id\": \"run_aaa111bbb222\",\n        \"sop_id\": \"sop_abc123def456\",\n        \"user_id\": \"usr_current\",\n        \"job_id\": \"job_ccc333ddd444\",\n        \"project_id\": \"prj_9876543210fedcba\",\n        \"status\": \"running\",\n        \"execution_mode\": \"strict\",\n        \"created_at\": \"2025-12-15T09:15:00Z\",\n        \"completed_at\": null\n    }\n]\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 403: User lacks permission to view this SOP\n- 404: SOP not found\n\n**Related Endpoints:**\n- GET /sops/runs/{run_id} - Get specific run details\n- GET /sops/runs/{run_id}/trace - View execution trace\n- POST /sops/{sop_id}/invoke - Start a new run","operationId":"list_sop_runs_api_v2_sops__sop_id__runs_get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Sop Runs Api V2 Sops  Sop Id  Runs Get"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions"},"404":{"description":"SOP not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/runs/{run_id}/trace":{"get":{"tags":["sops"],"summary":"Get Sop Run Trace","description":"Get block-level execution trace for an SOP run.\n\nReturns detailed execution trace showing what happened at each block/node\nduring the run. Includes timing, status, input/output snapshots, and error\ndetails. The trace is available during execution for real-time progress\nmonitoring and after completion for debugging.\n\n**Prerequisites:**\n- Valid API key for authentication\n- Run must exist and belong to the user\n\n**Path Parameters:**\n- `run_id` (required): Run identifier (run_ prefix)\n\n**Example Request:**\n```\nGET /api/v2/sops/runs/run_xyz789abc456/trace\n```\n\n**Example Response:**\n```json\n{\n    \"run_id\": \"run_xyz789abc456\",\n    \"sop_id\": \"sop_abc123def456\",\n    \"status\": \"completed\",\n    \"total_visits\": 5,\n    \"block_trace\": [\n        {\n            \"id\": \"trace_aaa111\",\n            \"block_id\": \"extract_data\",\n            \"visit_number\": 1,\n            \"status\": \"completed\",\n            \"started_at\": \"2025-12-14T10:30:05Z\",\n            \"completed_at\": \"2025-12-14T10:30:12Z\",\n            \"duration_ms\": 7000,\n            \"input_snapshot\": {\n                \"indication\": \"ulcerative colitis\"\n            },\n            \"output_snapshot\": {\n                \"extracted_fields\": [\"phase\", \"duration\"]\n            },\n            \"skip_reason\": null,\n            \"error_details\": null,\n            \"created_at\": \"2025-12-14T10:30:05Z\"\n        },\n        {\n            \"id\": \"trace_bbb222\",\n            \"block_id\": \"generate_prd\",\n            \"visit_number\": 1,\n            \"status\": \"completed\",\n            \"started_at\": \"2025-12-14T10:30:13Z\",\n            \"completed_at\": \"2025-12-14T10:32:40Z\",\n            \"duration_ms\": 147000,\n            \"input_snapshot\": {\n                \"phase\": \"Phase II\",\n                \"duration\": 12\n            },\n            \"output_snapshot\": {\n                \"document\": \"# Clinical PRD...\"\n            },\n            \"skip_reason\": null,\n            \"error_details\": null,\n            \"created_at\": \"2025-12-14T10:30:13Z\"\n        }\n    ],\n    \"wip_context\": {\n        \"extract_data\": {\n            \"phase\": \"Phase II\",\n            \"duration\": 12\n        }\n    },\n    \"summary\": {\n        \"total_blocks\": 5,\n        \"completed\": 5,\n        \"skipped\": 0,\n        \"failed\": 0,\n        \"pending\": 0,\n        \"running\": 0,\n        \"total_duration_ms\": 165000\n    }\n}\n```\n\n**Trace Entry Fields:**\n- `status`: \"pending\", \"running\", \"completed\", \"skipped\", \"failed\"\n- `visit_number`: Increments for state-machine loops (same block revisited)\n- `duration_ms`: Execution time in milliseconds\n- `input_snapshot`: Block inputs at execution time\n- `output_snapshot`: Block outputs after execution\n- `skip_reason`: Why block was skipped (conditional logic)\n- `error_details`: Error information if status is \"failed\"\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: Run not found or user has no access\n\n**Related Endpoints:**\n- GET /sops/runs/{run_id} - Get run summary\n- GET /sops/{sop_id}/runs - List all runs for an SOP\n- POST /sops/{sop_id}/invoke - Start a new run","operationId":"get_sop_run_trace_api_v2_sops_runs__run_id__trace_get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SOPRunTraceResponse"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Run not found or no access"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/runs/{run_id}":{"get":{"tags":["sops"],"summary":"Get Sop Run","description":"Get specific SOP run details including outputs and costs.\n\nReturns complete information about a single SOP run including status,\nextracted data, output files, error messages, and execution metadata.\nUse this endpoint to check run completion and retrieve results.\n\n**Prerequisites:**\n- Valid API key for authentication\n- Run must exist and belong to the user\n\n**Path Parameters:**\n- `run_id` (required): Run identifier (run_ prefix)\n\n**Example Request:**\n```\nGET /api/v2/sops/runs/run_xyz789abc456\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"run_xyz789abc456\",\n    \"sop_id\": \"sop_abc123def456\",\n    \"sop_name\": \"Clinical PRD Generator\",\n    \"user_id\": \"usr_current\",\n    \"job_id\": \"job_def123ghi789\",\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"input_file_ids\": [\"fil_aaa111\", \"fil_bbb222\"],\n    \"input_paths\": [\"/inputs/trials\"],\n    \"output_path\": \"/outputs/clinical-prd\",\n    \"status\": \"completed\",\n    \"execution_mode\": \"strict\",\n    \"missing_fields\": [],\n    \"extracted_data\": {\n        \"indication\": \"ulcerative colitis\",\n        \"phase\": \"Phase II\",\n        \"duration_weeks\": 12\n    },\n    \"output_files\": [\n        {\n            \"id\": \"fil_output123\",\n            \"filename\": \"clinical-prd.md\",\n            \"path\": \"/outputs/clinical-prd/clinical-prd.md\",\n            \"block_id\": \"generate_prd\",\n            \"type\": \"markdown\",\n            \"is_final\": true,\n            \"download_url\": \"/api/v2/files/fil_output123/download\"\n        }\n    ],\n    \"output_summary\": {\n        \"total_blocks\": 5,\n        \"completed\": 5,\n        \"duration_seconds\": 165\n    },\n    \"error_message\": null,\n    \"created_at\": \"2025-12-14T10:30:00Z\",\n    \"completed_at\": \"2025-12-14T10:32:45Z\"\n}\n```\n\n**Status Values:**\n- `pending`: Queued, waiting to start\n- `validating`: Checking data completeness\n- `running`: Currently executing blocks\n- `completed`: Finished successfully\n- `failed`: Error occurred during execution\n- `cancelled`: User cancelled the run\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 404: Run not found or user has no access\n\n**Related Endpoints:**\n- GET /sops/runs/{run_id}/trace - View detailed execution trace\n- GET /sops/{sop_id}/runs - List all runs for an SOP\n- DELETE /sops/runs/{run_id} - Cancel running execution","operationId":"get_sop_run_api_v2_sops_runs__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Sop Run Api V2 Sops Runs  Run Id  Get"}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"Run not found or no access"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["sops"],"summary":"Cancel Sop Run","description":"Cancel a running SOP execution.\n\nStops an in-progress SOP run. Only runs with status \"running\" or \"validating\"\ncan be cancelled. The run status will be updated to \"cancelled\" and any\nassociated background job will be terminated. Completed or failed runs\ncannot be cancelled.\n\n**Prerequisites:**\n- Valid API key for authentication\n- Run must exist and belong to the user\n- Run status must be \"running\" or \"validating\"\n\n**Path Parameters:**\n- `run_id` (required): Run identifier (run_ prefix)\n\n**Example Request:**\n```\nDELETE /api/v2/sops/runs/run_xyz789abc456\n```\n\n**Example Response:**\n```\n204 No Content\n(empty body)\n```\n\n**Cancellable Statuses:**\n- `validating`: Data validation in progress\n- `running`: Blocks executing\n\n**Non-Cancellable Statuses:**\n- `pending`: Not yet started (will start soon)\n- `completed`: Already finished\n- `failed`: Already failed\n- `cancelled`: Already cancelled\n\n**Error Responses:**\n- 400: Run status is not \"running\" or \"validating\"\n- 401: Missing or invalid API key\n- 404: Run not found or user has no access\n\n**Related Endpoints:**\n- GET /sops/runs/{run_id} - Check run status before cancelling\n- GET /sops/runs/{run_id}/trace - View what was completed before cancellation\n- POST /sops/{sop_id}/invoke - Start a new run","operationId":"cancel_sop_run_api_v2_sops_runs__run_id__delete","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Run cancelled successfully"},"400":{"description":"Run cannot be cancelled (wrong status)"},"401":{"description":"Missing or invalid API key"},"404":{"description":"Run not found or no access"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/sops/{sop_id}/schedules":{"get":{"tags":["sops"],"summary":"List Sop Schedules","description":"List schedules for a given SOP.\n\n**Status: Not yet implemented.** Scheduling infrastructure exists\n(Celery beat, schedule manager worker) but per-SOP schedule management\nis not yet exposed through this endpoint. Use the jobs API for\none-off async execution in the meantime.\n\nReturns 501 until the full scheduling API is available.","operationId":"list_sop_schedules_api_v2_sops__sop_id__schedules_get","parameters":[{"name":"sop_id","in":"path","required":true,"schema":{"type":"string","title":"Sop Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of schedules for this SOP","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid API key"},"404":{"description":"SOP not found or no access"},"501":{"description":"Scheduling not yet available for SOPs"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents":{"post":{"tags":["agents"],"summary":"Create Agent","description":"Create a new agent.\n\nCreates a user-facing orchestration workflow that compiles to an SOP\nfor reliable execution. The agent YAML definition is validated and\ncompiled upon creation.\n\n**Prerequisites:**\n- User must be authenticated\n- Agent name and version combination must be unique for this user\n\n**Request Body:**\n- `name` (string, required): Agent name (1-255 characters)\n- `version` (string, required): Semantic version (e.g., \"1.0.0\")\n- `yaml_definition` (string, required): Complete YAML definition defining inputs, steps, and workflow\n- `description` (string, optional): Human-readable description of the agent's purpose\n- `is_public` (boolean, optional): Make agent publicly accessible (default: false)\n- `tags` (array[string], optional): Searchable tags for categorization\n\n**Example Request:**\n```json\n{\n    \"name\": \"Research Assistant\",\n    \"version\": \"1.0.0\",\n    \"yaml_definition\": \"name: Research Assistant\\nversion: 1.0.0\\ngoal: Research and summarize topics\\ninputs:\\n  - name: topic\\n    type: text\\n    description: Topic to research\\n    required: true\\n    source: user-provided\\nsteps:\\n  - id: search\\n    description: Search web for topic\\n    type: tool\\n    tool: web_search\\n    input: topic\\n    output: search_results\\n  - id: summarize\\n    description: Summarize findings\\n    type: llm\\n    prompt: 'Summarize: {{search_results}}'\\n    input: search_results\\n    output: summary\",\n    \"description\": \"Automated research agent that searches and summarizes topics\",\n    \"is_public\": false,\n    \"tags\": [\"research\", \"automation\"]\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"name\": \"Research Assistant\",\n    \"version\": \"1.0.0\",\n    \"description\": \"Automated research agent that searches and summarizes topics\",\n    \"is_public\": false,\n    \"yaml_definition\": \"name: Research Assistant\\nversion: 1.0.0\\n...\",\n    \"compiled_sop_id\": \"sop_xyz789abc\",\n    \"goal\": \"Research and summarize topics\",\n    \"tags\": [\"research\", \"automation\"],\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T10:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required (missing or invalid API key)\n- 409: Agent with same name and version already exists for this user\n- 422: Invalid YAML definition or compilation error\n- 500: Internal server error\n\n**Related Endpoints:**\n- POST /agents/validate - Validate YAML definition before creating\n- GET /agents - List all agents\n- GET /agents/{agent_id} - Get agent details","operationId":"create_agent_api_v2_agents_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCreate"}}}},"responses":{"201":{"description":"Agent created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Agent"}}}},"401":{"description":"Authentication required"},"409":{"description":"Agent with same name and version already exists"},"422":{"description":"Invalid YAML definition or compilation error"},"500":{"description":"Internal server error"}}},"get":{"tags":["agents"],"summary":"List Agents","description":"List all agents accessible to the user.\n\nReturns agents the user owns, has been granted access to, or are public.\nSupports filtering by public status, shared status, tags, and text search.\n\n**Prerequisites:**\n- User must be authenticated\n\n**Query Parameters:**\n- `public_only` (boolean, optional): If true, return only public agents (default: false)\n- `shared_with_me` (boolean, optional): If true, return only agents shared with user (default: false)\n- `tags` (array[string], optional): Filter by tags (agents must have all specified tags)\n- `search` (string, optional): Search by agent name or description (case-insensitive)\n- `limit` (integer, optional): Maximum results per page (1-100, default: 50)\n- `offset` (integer, optional): Pagination offset (default: 0)\n\n**Example Request:**\n```\nGET /agents?tags=research&tags=automation&limit=20&offset=0\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n        \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n        \"name\": \"Research Assistant\",\n        \"version\": \"1.0.0\",\n        \"description\": \"Automated research agent\",\n        \"is_public\": false,\n        \"yaml_definition\": \"name: Research Assistant\\n...\",\n        \"compiled_sop_id\": \"sop_xyz789abc\",\n        \"goal\": \"Research and summarize topics\",\n        \"tags\": [\"research\", \"automation\"],\n        \"created_at\": \"2025-11-27T10:00:00Z\",\n        \"updated_at\": \"2025-11-27T10:00:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 500: Internal server error\n\n**Related Endpoints:**\n- POST /agents - Create a new agent\n- GET /agents/{agent_id} - Get specific agent details\n- GET /agents/{agent_id}/stats - Get agent usage statistics","operationId":"list_agents_api_v2_agents_get","parameters":[{"name":"public_only","in":"query","required":false,"schema":{"type":"boolean","description":"List only public agents","default":false,"title":"Public Only"},"description":"List only public agents"},{"name":"shared_with_me","in":"query","required":false,"schema":{"type":"boolean","description":"List only agents shared with me","default":false,"title":"Shared With Me"},"description":"List only agents shared with me"},{"name":"tags","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter by tags","title":"Tags"},"description":"Filter by tags"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search by name or description","title":"Search"},"description":"Search by name or description"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results per page","default":50,"title":"Limit"},"description":"Max results per page"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset","default":0,"title":"Offset"},"description":"Pagination offset"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of agents retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Agent"},"title":"Response List Agents Api V2 Agents Get"}}}},"401":{"description":"Authentication required"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/webhook-callback-spec":{"get":{"tags":["agents"],"summary":"[Reference] Agent run completion webhook payload","description":"Return a **sample** `AgentRunWebhookPayload` -- the JSON body Taiso POSTs to your `webhook_url`.\n\n**This is documentation for integrators.** It is not a webhook receiver; it shows the\nexact JSON your server will receive when an agent run reaches a terminal state.\n\n**Payload fields:**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `event` | `\"agent_run.completed\"` \\| `\"agent_run.failed\"` | Event type |\n| `run_id` | string | Agent run identifier |\n| `agent_id` | string | Agent identifier |\n| `agent_version` | string | Agent version used for this run |\n| `project_id` | string | Project that owns the run |\n| `status` | `\"completed\"` \\| `\"failed\"` | Terminal status |\n| `completed_at` | string (ISO 8601) | Timestamp when the run reached terminal state |\n| `error_message` | string \\| null | Error summary on failure; null on success |\n| `output_preview` | object \\| null | Agent outputs inline if JSON < 4KB; null otherwise |\n\n**Delivery details:**\n- Method: `POST` to your `webhook_url`\n- Content-Type: `application/json`\n- Timeout: 10 seconds -- respond with `2xx` quickly, process async\n- Retries: Up to 3 attempts (30s, 120s, 300s backoff)\n- Guarantee: At-least-once -- deduplicate on `(run_id, status)` tuple\n- Failure: Delivery failure does **not** change run status\n\nPass `?event_type=failed` to see the failure payload shape.","operationId":"get_agent_webhook_callback_spec_api_v2_agents_webhook_callback_spec_get","parameters":[{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Return a `\"failed\"` sample instead of the default `\"completed\"` sample.","enum":["completed","failed"],"title":"Event Type"},"description":"Return a `\"failed\"` sample instead of the default `\"completed\"` sample."},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Sample webhook payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunWebhookPayload"},"examples":{"completed":{"summary":"Successful agent run completion","value":{"event":"agent_run.completed","run_id":"arun_01JDX2M1Z6G2T7Y8","agent_id":"agt_01JDX2LZ8Q9R0STU","agent_version":"1.0.0","project_id":"prj_1a2b3c4d5e6f","status":"completed","completed_at":"2026-06-14T12:00:00Z","output_preview":{"summary":"Research findings..."}}},"failed":{"summary":"Failed agent run notification","value":{"event":"agent_run.failed","run_id":"arun_01JDX2M1KLMNOPQR","agent_id":"agt_01JDX2LZ8Q9R0STU","agent_version":"1.0.0","project_id":"prj_1a2b3c4d5e6f","status":"failed","completed_at":"2026-06-14T12:05:00Z","error_message":"Agent execution failed: SOP execution failed"}}}}}},"401":{"description":"Missing or invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}":{"get":{"tags":["agents"],"summary":"Get Agent","description":"Get agent definition by ID.\n\nReturns complete agent definition including YAML source, compiled SOP ID,\nmetadata, and tags. Requires at least viewer permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer, executor, or editor permission on the agent\n- Agent must exist and not be deleted\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Example Request:**\n```\nGET /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"name\": \"Research Assistant\",\n    \"version\": \"1.0.0\",\n    \"description\": \"Automated research agent that searches and summarizes topics\",\n    \"is_public\": false,\n    \"yaml_definition\": \"name: Research Assistant\\nversion: 1.0.0\\ngoal: Research and summarize topics\\ninputs:\\n  - name: topic\\n    type: text\\n    description: Topic to research\\n    required: true\\n    source: user-provided\\nsteps:\\n  - id: search\\n    description: Search web\\n    type: tool\\n    tool: web_search\\n    input: topic\\n    output: search_results\",\n    \"compiled_sop_id\": \"sop_xyz789abc\",\n    \"goal\": \"Research and summarize topics\",\n    \"tags\": [\"research\", \"automation\"],\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T10:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Access denied (user lacks viewer permission on this agent)\n- 404: Agent not found or user does not have access\n\n**Related Endpoints:**\n- GET /agents - List all agents\n- PUT /agents/{agent_id} - Update agent definition\n- DELETE /agents/{agent_id} - Delete agent\n- POST /agents/{agent_id}/invoke - Invoke agent execution","operationId":"get_agent_api_v2_agents__agent_id__get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Agent details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Agent"}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied (insufficient permissions)"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["agents"],"summary":"Update Agent","description":"Update agent definition.\n\nUpdates agent metadata, YAML definition, public visibility, or tags.\nUpdating the YAML definition triggers automatic recompilation to a new SOP.\nRequires editor permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the agent (or be the owner)\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Request Body:**\nAll fields are optional - only provided fields will be updated.\n- `yaml_definition` (string, optional): Updated YAML definition (triggers recompilation)\n- `description` (string, optional): Updated description\n- `is_public` (boolean, optional): Updated public visibility flag\n- `tags` (array[string], optional): Updated tags (replaces existing tags)\n\n**Example Request:**\n```json\n{\n    \"description\": \"Enhanced research agent with citation support\",\n    \"tags\": [\"research\", \"automation\", \"citations\"],\n    \"is_public\": true\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"owner_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"name\": \"Research Assistant\",\n    \"version\": \"1.0.0\",\n    \"description\": \"Enhanced research agent with citation support\",\n    \"is_public\": true,\n    \"yaml_definition\": \"name: Research Assistant\\n...\",\n    \"compiled_sop_id\": \"sop_xyz789abc\",\n    \"goal\": \"Research and summarize topics\",\n    \"tags\": [\"research\", \"automation\", \"citations\"],\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T14:30:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Editor permission required\n- 404: Agent not found\n- 422: Invalid YAML definition or compilation error\n\n**Related Endpoints:**\n- GET /agents/{agent_id} - Get current agent definition\n- POST /agents/validate - Validate YAML definition before updating\n- POST /agents/{agent_id}/compile - Force recompile agent\n- DELETE /agents/{agent_id} - Delete agent","operationId":"update_agent_api_v2_agents__agent_id__put","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUpdate"}}}},"responses":{"200":{"description":"Agent updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Agent"}}}},"401":{"description":"Authentication required"},"403":{"description":"Editor permission required"},"404":{"description":"Agent not found"},"422":{"description":"Invalid YAML definition or compilation error"}}},"delete":{"tags":["agents"],"summary":"Delete Agent","description":"Delete agent.\n\nPermanently deletes an agent and all associated resources including runs,\nschedules, and permissions. Only the agent owner can delete an agent.\nThis action cannot be undone.\n\n**Prerequisites:**\n- User must be authenticated\n- User must be the agent owner\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Example Request:**\n```\nDELETE /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234\n```\n\n**Example Response:**\n```\nHTTP/1.1 204 No Content\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Only the owner can delete an agent (not editors or executors)\n- 404: Agent not found or user does not have access\n\n**Related Endpoints:**\n- GET /agents/{agent_id} - Get agent details before deletion\n- GET /agents/{agent_id}/runs - View agent runs before deletion\n- GET /agents/{agent_id}/schedules - View agent schedules before deletion","operationId":"delete_agent_api_v2_agents__agent_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Agent deleted successfully"},"401":{"description":"Authentication required"},"403":{"description":"Only the owner can delete an agent"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/validate":{"post":{"tags":["agents"],"summary":"Validate Agent","description":"Validate agent YAML definition without creating the agent.\n\nPerforms dry-run validation of agent YAML definition, checking syntax,\nstep dependencies, tool availability, and SOP/agent references. Returns\nvalidation errors, warnings, and the compiled SOP if valid. Safe to call\nmultiple times (idempotent).\n\n**Prerequisites:**\n- User must be authenticated\n\n**Request Body:**\n- `yaml_definition` (string, required): Complete YAML definition string to validate\n\n**Example Request:**\n```json\n{\n    \"yaml_definition\": \"name: Research Agent\\nversion: 1.0.0\\ngoal: Research topics\\ninputs:\\n  - name: topic\\n    type: text\\n    description: Topic to research\\n    required: true\\n    source: user-provided\\nsteps:\\n  - id: search\\n    description: Search web\\n    type: tool\\n    tool: web_search\\n    input: topic\\n    output: results\"\n}\n```\n\n**Example Response (Valid):**\n```json\n{\n    \"is_valid\": true,\n    \"errors\": [],\n    \"warnings\": [],\n    \"compiled_sop\": {\n        \"name\": \"Research Agent\",\n        \"version\": \"1.0.0\",\n        \"nodes\": [...]\n    }\n}\n```\n\n**Example Response (Invalid):**\n```json\n{\n    \"is_valid\": false,\n    \"errors\": [\n        {\n            \"type\": \"validation_error\",\n            \"message\": \"Step 'search' references undefined input 'topic_name'\"\n        }\n    ],\n    \"warnings\": [\n        {\n            \"type\": \"warning\",\n            \"message\": \"Tool 'web_search' may have rate limits\"\n        }\n    ],\n    \"compiled_sop\": null\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n\n**Related Endpoints:**\n- POST /agents - Create agent after validation\n- PUT /agents/{agent_id} - Update agent with validated YAML\n- POST /agents/{agent_id}/compile - Force recompile existing agent","operationId":"validate_agent_api_v2_agents_validate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"200":{"description":"Validation completed (check is_valid field for result)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentValidateResponse"}}}},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}/compile":{"post":{"tags":["agents"],"summary":"Compile Agent","description":"Force recompile agent to SOP.\n\nRegenerates the compiled SOP from the current YAML definition without\nmodifying the agent. Useful after fixing compilation issues, updating\nthe compiler, or when the compiled SOP was lost. Requires at least\nviewer permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer, executor, or editor permission on the agent\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Example Request:**\n```\nPOST /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/compile\n```\n\n**Example Response:**\n```json\n{\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"compiled_sop_id\": \"sop_xyz789abc\",\n    \"message\": \"Agent recompiled successfully\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Access denied (user lacks viewer permission)\n- 404: Agent not found\n- 422: Compilation error (invalid YAML definition)\n\n**Related Endpoints:**\n- POST /agents/validate - Validate YAML definition before compiling\n- PUT /agents/{agent_id} - Update agent (automatically recompiles)\n- GET /agents/{agent_id} - Get agent with compiled SOP ID","operationId":"compile_agent_api_v2_agents__agent_id__compile_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Agent recompiled successfully","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Compile Agent Api V2 Agents  Agent Id  Compile Post"}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied"},"404":{"description":"Agent not found"},"422":{"description":"Compilation error"}}}},"/api/v2/agents/{agent_id}/invoke":{"post":{"tags":["agents"],"summary":"Invoke Agent","description":"Invoke agent execution.\n\nTriggers asynchronous agent execution within a project context. The agent\ncompiles to an SOP and executes via the job system. Returns immediately with\nrun and job IDs for tracking. Use Idempotency-Key header to prevent duplicate\nruns. Requires executor permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have executor permission on the agent (or be the owner)\n- Agent must exist and be compiled\n- Project must exist and user must have access\n- All required inputs must be provided\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Request Headers:**\n- `Idempotency-Key` (string, optional): Prevents duplicate runs if request is retried\n\n**Request Body:**\n- `project_id` (string, required): Project ID for execution context\n- `inputs` (object, required): Input data matching agent's input schema\n- `idempotency_key` (string, optional): Alternative to header-based idempotency key\n- `parent_run_id` (string, optional): Parent run ID for sub-agent invocations\n- `depth` (integer, optional): Nesting depth (0 = top-level, max 5, default: 0)\n\n**Example Request:**\n```json\n{\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"inputs\": {\n        \"topic\": \"artificial intelligence ethics\",\n        \"max_sources\": 10\n    },\n    \"idempotency_key\": \"invoke-2025-11-27T10:15:00Z\",\n    \"depth\": 0\n}\n```\n\n**Example Response:**\n```json\n{\n    \"run_id\": \"run_01JDX2M1Z6G2T7Y8Z9ABCD12EF\",\n    \"job_id\": \"job_01JDX2M2HXQ3R4S5T6UVWX78YZ\",\n    \"status\": \"pending\",\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"agent_version\": \"1.0.0\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Executor permission required or project access denied\n- 404: Agent or project not found\n- 409: Idempotency key already used (returns existing run_id in error details)\n- 422: Invalid inputs (missing required fields) or max depth exceeded (max 5)\n\n**Related Endpoints:**\n- GET /agents/runs/{run_id} - Get run status and results\n- GET /agents/{agent_id}/runs - List all runs for this agent\n- DELETE /agents/runs/{run_id} - Cancel running execution\n- GET /jobs/{job_id} - Get job status via jobs API","operationId":"invoke_agent_api_v2_agents__agent_id__invoke_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentInvokeRequest"}}}},"responses":{"202":{"description":"Agent execution started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentInvokeResponse"}}}},"401":{"description":"Authentication required"},"403":{"description":"Executor permission required or project access denied"},"404":{"description":"Agent or project not found"},"409":{"description":"Idempotency key already used"},"422":{"description":"Invalid inputs, max depth exceeded, or referenced resource not found"}}}},"/api/v2/agents/{agent_id}/runs":{"get":{"tags":["agents"],"summary":"List Agent Runs","description":"List execution history for an agent.\n\nReturns all runs for an agent that the user has access to, ordered by\ncreation time (most recent first). Supports filtering by status and\ntrigger type, with pagination.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer, executor, or editor permission on the agent\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Query Parameters:**\n- `status` (string, optional): Filter by status (pending, running, completed, failed, cancelled)\n- `trigger_type` (string, optional): Filter by trigger type (manual, schedule, webhook, api)\n- `limit` (integer, optional): Maximum results per page (1-100, default: 50)\n- `offset` (integer, optional): Pagination offset (default: 0)\n\n**Example Request:**\n```\nGET /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/runs?status=completed&limit=20&offset=0\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"run_01JDX2M1Z6G2T7Y8Z9ABCD12EF\",\n        \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n        \"agent_version\": \"1.0.0\",\n        \"user_id\": \"usr_01HXYZ1234567890ABCDEF\",\n        \"project_id\": \"prj_1234567890abcdef\",\n        \"job_id\": \"job_01JDX2M2HXQ3R4S5T6UVWX78YZ\",\n        \"sop_run_id\": \"srun_01JDX2M3ABCDEF\",\n        \"trigger_type\": \"manual\",\n        \"parent_run_id\": null,\n        \"depth\": 0,\n        \"inputs\": {\n            \"topic\": \"AI ethics\",\n            \"max_sources\": 10\n        },\n        \"outputs\": {\n            \"summary\": \"AI ethics encompasses...\"\n        },\n        \"status\": \"completed\",\n        \"error_message\": null,\n        \"created_at\": \"2025-11-27T10:15:00Z\",\n        \"started_at\": \"2025-11-27T10:15:02Z\",\n        \"completed_at\": \"2025-11-27T10:15:20Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Access denied (user lacks viewer permission)\n- 404: Agent not found\n\n**Related Endpoints:**\n- GET /agents/runs/{run_id} - Get specific run details\n- POST /agents/{agent_id}/invoke - Create new run\n- DELETE /agents/runs/{run_id} - Cancel running execution","operationId":"list_agent_runs_api_v2_agents__agent_id__runs_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"trigger_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by trigger type","title":"Trigger Type"},"description":"Filter by trigger type"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results per page","default":50,"title":"Limit"},"description":"Max results per page"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset","default":0,"title":"Offset"},"description":"Pagination offset"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of agent runs retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AgentRun"},"title":"Response List Agent Runs Api V2 Agents  Agent Id  Runs Get"}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/runs/{run_id}":{"get":{"tags":["agents"],"summary":"Get Agent Run","description":"Get specific agent run details.\n\nReturns complete details for a single agent run including inputs, outputs,\nstatus, timing, errors, and associated job/SOP run IDs. Requires viewer\npermission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer permission on the agent (or be the run creator)\n- Run must exist\n\n**Path Parameters:**\n- `run_id` (string, required): Agent run identifier (prefixed with 'run_')\n\n**Example Request:**\n```\nGET /agents/runs/run_01JDX2M1Z6G2T7Y8Z9ABCD12EF\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"run_01JDX2M1Z6G2T7Y8Z9ABCD12EF\",\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"agent_version\": \"1.0.0\",\n    \"user_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"job_id\": \"job_01JDX2M2HXQ3R4S5T6UVWX78YZ\",\n    \"sop_run_id\": \"srun_01JDX2M3ABCDEF\",\n    \"trigger_type\": \"manual\",\n    \"parent_run_id\": null,\n    \"depth\": 0,\n    \"inputs\": {\n        \"topic\": \"mucosal barrier protection\",\n        \"max_sources\": 10\n    },\n    \"outputs\": {\n        \"summary\": \"The mucosal barrier is supported by...\",\n        \"sources\": [\"https://...\", \"https://...\"]\n    },\n    \"status\": \"completed\",\n    \"error_message\": null,\n    \"created_at\": \"2025-11-27T10:15:00Z\",\n    \"started_at\": \"2025-11-27T10:15:02Z\",\n    \"completed_at\": \"2025-11-27T10:15:20Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Access denied (user lacks viewer permission on the agent)\n- 404: Run not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id}/runs - List all runs for an agent\n- DELETE /agents/runs/{run_id} - Cancel this run (if pending/running)\n- GET /jobs/{job_id} - Get associated job details","operationId":"get_agent_run_api_v2_agents_runs__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"include_output","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Output"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Agent run details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRun"}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied"},"404":{"description":"Run not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["agents"],"summary":"Cancel Agent Run","description":"Cancel a running agent execution.\n\nCancels an agent run that is currently pending or running. Completed,\nfailed, or already cancelled runs cannot be cancelled. Requires executor\npermission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have executor permission on the agent (or be the owner)\n- Run must exist and be in 'pending' or 'running' status\n\n**Path Parameters:**\n- `run_id` (string, required): Agent run identifier (prefixed with 'run_')\n\n**Example Request:**\n```\nDELETE /agents/runs/run_01JDX2M1Z6G2T7Y8Z9ABCD12EF\n```\n\n**Example Response:**\n```\nHTTP/1.1 204 No Content\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Executor permission required\n- 404: Run not found\n- 422: Cannot cancel completed/failed/cancelled runs\n\n**Related Endpoints:**\n- GET /agents/runs/{run_id} - Check run status before cancelling\n- GET /agents/{agent_id}/runs - List all runs to find cancellable ones\n- POST /agents/{agent_id}/invoke - Create new run","operationId":"cancel_agent_run_api_v2_agents_runs__run_id__delete","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Agent run cancelled successfully"},"401":{"description":"Authentication required"},"403":{"description":"Executor permission required"},"404":{"description":"Run not found"},"422":{"description":"Cannot cancel completed/failed/cancelled runs"}}}},"/api/v2/agents/runs/{run_id}/output":{"get":{"tags":["agents"],"summary":"Get Agent Run Output","description":"Get the output payload of an agent run.\n\nReturns only the outputs dict for a completed agent run. Use this endpoint\nto fetch large output data separately instead of including it inline via\n``GET /agents/runs/{run_id}``.\n\nReturns 404 if the run has no output (still pending/running or had no output).","operationId":"get_agent_run_output_api_v2_agents_runs__run_id__output_get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Agent run output retrieved successfully","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied"},"404":{"description":"Run not found or run has no output yet"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}/permissions":{"post":{"tags":["agents"],"summary":"Grant Agent Permission","description":"Grant permission to another user.\n\nGrants access to an agent for another user with a specific role. Only\nagent owners or users with editor permission can grant permissions.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the agent (or be the owner)\n- Agent must exist\n- Target user must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Request Body:**\n- `user_id` (string, required): User ID to grant permission to\n- `role` (string, required): Permission role (viewer, executor, or editor)\n\n**Roles:**\n- `viewer`: Can view agent definition and runs\n- `executor`: Can view, invoke agent, and view runs\n- `editor`: Can view, invoke, modify agent, and manage permissions\n\n**Example Request:**\n```json\n{\n    \"user_id\": \"usr_98765fedcba\",\n    \"role\": \"executor\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"user_id\": \"usr_98765fedcba\",\n    \"role\": \"executor\",\n    \"message\": \"Permission granted successfully\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Editor permission required\n- 404: Agent not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id}/permissions - List all permissions\n- DELETE /agents/{agent_id}/permissions/{user_id} - Revoke permission","operationId":"grant_agent_permission_api_v2_agents__agent_id__permissions_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentPermissionCreate"}}}},"responses":{"201":{"description":"Permission granted successfully","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Grant Agent Permission Api V2 Agents  Agent Id  Permissions Post"}}}},"401":{"description":"Authentication required"},"403":{"description":"Editor permission required"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["agents"],"summary":"List Agent Permissions","description":"List all permissions for an agent.\n\nReturns all users who have been granted access to this agent and their\nroles. Requires at least viewer permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer, executor, or editor permission on the agent\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Example Request:**\n```\nGET /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/permissions\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"aperm_01JDX2N1ABCDEF\",\n        \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n        \"user_id\": \"usr_98765fedcba\",\n        \"role\": \"executor\",\n        \"created_at\": \"2025-11-27T11:00:00Z\"\n    },\n    {\n        \"id\": \"aperm_01JDX2N2GHIJKL\",\n        \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n        \"user_id\": \"usr_11111222333\",\n        \"role\": \"viewer\",\n        \"created_at\": \"2025-11-27T12:00:00Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Access denied (user lacks viewer permission)\n- 404: Agent not found\n\n**Related Endpoints:**\n- POST /agents/{agent_id}/permissions - Grant permission to user\n- DELETE /agents/{agent_id}/permissions/{user_id} - Revoke permission","operationId":"list_agent_permissions_api_v2_agents__agent_id__permissions_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of permissions retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Agent Permissions Api V2 Agents  Agent Id  Permissions Get"}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}/permissions/{user_id}":{"delete":{"tags":["agents"],"summary":"Revoke Agent Permission","description":"Revoke permission from a user.\n\nRemoves a user's access to an agent. This deletes the permission record,\nnot the user. Requires editor permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the agent (or be the owner)\n- Agent must exist\n- Target user must have an existing permission on the agent\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n- `user_id` (string, required): User ID to revoke permission from\n\n**Example Request:**\n```\nDELETE /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/permissions/usr_98765fedcba\n```\n\n**Example Response:**\n```\nHTTP/1.1 204 No Content\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Editor permission required\n- 404: Agent or permission not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id}/permissions - List all permissions\n- POST /agents/{agent_id}/permissions - Grant permission to user","operationId":"revoke_agent_permission_api_v2_agents__agent_id__permissions__user_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Permission revoked successfully"},"401":{"description":"Authentication required"},"403":{"description":"Editor permission required"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}/stats":{"get":{"tags":["agents"],"summary":"Get Agent Usage Stats","description":"Get usage statistics for an agent.\n\nReturns aggregated metrics across all runs for an agent including invocation\ncounts, success rates, average execution duration, and average cost.\nAvailable for public agents without authentication.\n\n**Prerequisites:**\n- For private agents: User must be authenticated\n- For public agents: No authentication required\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Example Request:**\n```\nGET /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/stats\n```\n\n**Example Response:**\n```json\n{\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"total_runs\": 42,\n    \"successful_runs\": 39,\n    \"failed_runs\": 3,\n    \"avg_duration_seconds\": 18.5,\n    \"avg_cost_usd\": 0.0123,\n    \"last_used_at\": \"2025-11-27T10:15:20Z\",\n    \"updated_at\": \"2025-11-27T10:15:20Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required (for private agents)\n- 404: Agent not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id} - Get agent details\n- GET /agents/{agent_id}/runs - Get detailed run history","operationId":"get_agent_usage_stats_api_v2_agents__agent_id__stats_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Usage statistics retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUsageStats"}}}},"401":{"description":"Authentication required"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}/schedules":{"post":{"tags":["agents"],"summary":"Create Agent Schedule","description":"Create a cron-based schedule for automatic agent execution.\n\nCreates a recurring schedule that automatically invokes the agent at\nspecified times using a cron expression. Requires editor permission\non the agent and access to the project.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the agent (or be the owner)\n- Agent must exist\n- Project must exist and user must have access\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Request Body:**\n- `cron_expression` (string, required): Cron expression (e.g., '0 9 * * 1-5' for weekdays at 9am UTC)\n- `timezone` (string, optional): Timezone (currently must be 'UTC', default: 'UTC')\n- `project_id` (string, required): Project context for scheduled runs\n- `input_config` (object, optional): Input configuration for scheduled runs (default: {})\n\n**Example Request:**\n```json\n{\n    \"cron_expression\": \"0 9 * * 1-5\",\n    \"timezone\": \"UTC\",\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"input_config\": {\n        \"topic\": \"ulcerative colitis mucosal healing\",\n        \"max_sources\": 5\n    }\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"sched_01JDX2N4PQRS\",\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"user_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"cron_expression\": \"0 9 * * 1-5\",\n    \"timezone\": \"UTC\",\n    \"is_active\": true,\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"input_config\": {\n        \"topic\": \"ulcerative colitis mucosal healing\",\n        \"max_sources\": 5\n    },\n    \"last_run_at\": null,\n    \"next_run_at\": \"2025-11-28T09:00:00Z\",\n    \"total_runs\": 0,\n    \"created_at\": \"2025-11-27T10:00:00Z\",\n    \"updated_at\": \"2025-11-27T10:00:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Editor permission required or project access denied\n- 404: Agent or project not found\n- 422: Invalid cron expression format or referenced resource not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id}/schedules - List all schedules\n- PUT /agents/{agent_id}/schedules/{schedule_id} - Update schedule\n- DELETE /agents/{agent_id}/schedules/{schedule_id} - Delete schedule","operationId":"create_agent_schedule_api_v2_agents__agent_id__schedules_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentScheduleCreate"}}}},"responses":{"201":{"description":"Schedule created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSchedule"}}}},"401":{"description":"Authentication required"},"403":{"description":"Editor permission required or project access denied"},"404":{"description":"Agent or project not found"},"422":{"description":"Invalid cron expression or referenced resource not found"}}},"get":{"tags":["agents"],"summary":"List Agent Schedules","description":"List all schedules for an agent.\n\nReturns all cron-based schedules configured for automatic agent execution.\nRequires at least viewer permission on the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have viewer, executor, or editor permission on the agent\n- Agent must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n\n**Example Request:**\n```\nGET /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/schedules\n```\n\n**Example Response:**\n```json\n[\n    {\n        \"id\": \"sched_01JDX2N4PQRS\",\n        \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n        \"user_id\": \"usr_01HXYZ1234567890ABCDEF\",\n        \"cron_expression\": \"0 9 * * 1-5\",\n        \"timezone\": \"UTC\",\n        \"is_active\": true,\n        \"project_id\": \"prj_1234567890abcdef\",\n        \"input_config\": {\n            \"topic\": \"IBD treatment pipeline\",\n            \"max_sources\": 5\n        },\n        \"last_run_at\": \"2025-11-27T09:00:01Z\",\n        \"next_run_at\": \"2025-11-28T09:00:00Z\",\n        \"total_runs\": 12,\n        \"created_at\": \"2025-11-01T08:00:00Z\",\n        \"updated_at\": \"2025-11-27T09:00:01Z\"\n    }\n]\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Access denied (user lacks viewer permission)\n- 404: Agent not found\n\n**Related Endpoints:**\n- POST /agents/{agent_id}/schedules - Create new schedule\n- PUT /agents/{agent_id}/schedules/{schedule_id} - Update schedule\n- DELETE /agents/{agent_id}/schedules/{schedule_id} - Delete schedule","operationId":"list_agent_schedules_api_v2_agents__agent_id__schedules_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of schedules retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AgentSchedule"},"title":"Response List Agent Schedules Api V2 Agents  Agent Id  Schedules Get"}}}},"401":{"description":"Authentication required"},"403":{"description":"Access denied"},"404":{"description":"Agent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/{agent_id}/schedules/{schedule_id}":{"put":{"tags":["agents"],"summary":"Update Agent Schedule","description":"Update an existing agent schedule.\n\nUpdates schedule configuration including cron expression, timezone, active\nstatus, project context, or input configuration. Requires editor permission\non the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the agent (or be the owner)\n- Agent must exist\n- Schedule must exist\n- If updating project_id, user must have access to new project\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n- `schedule_id` (string, required): Schedule identifier (prefixed with 'sched_')\n\n**Request Body:**\nAll fields are optional - only provided fields will be updated.\n- `cron_expression` (string, optional): Updated cron expression\n- `timezone` (string, optional): Updated timezone (currently must be 'UTC')\n- `is_active` (boolean, optional): Enable/disable the schedule\n- `project_id` (string, optional): Updated project context\n- `input_config` (object, optional): Updated input configuration for scheduled runs\n\n**Example Request:**\n```json\n{\n    \"is_active\": false,\n    \"cron_expression\": \"0 10 * * 1-5\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id\": \"sched_01JDX2N4PQRS\",\n    \"agent_id\": \"agt_01JDX2LZ8Q9R0STUVWXYZ1234\",\n    \"user_id\": \"usr_01HXYZ1234567890ABCDEF\",\n    \"cron_expression\": \"0 10 * * 1-5\",\n    \"timezone\": \"UTC\",\n    \"is_active\": false,\n    \"project_id\": \"prj_1234567890abcdef\",\n    \"input_config\": {\n        \"topic\": \"IBD treatment pipeline\",\n        \"max_sources\": 5\n    },\n    \"last_run_at\": \"2025-11-27T09:00:01Z\",\n    \"next_run_at\": \"2025-11-28T10:00:00Z\",\n    \"total_runs\": 12,\n    \"created_at\": \"2025-11-01T08:00:00Z\",\n    \"updated_at\": \"2025-11-27T14:30:00Z\"\n}\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Editor permission required or project access denied\n- 404: Agent, schedule, or project not found\n- 422: Invalid cron expression format or referenced resource not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id}/schedules - List all schedules\n- POST /agents/{agent_id}/schedules - Create new schedule\n- DELETE /agents/{agent_id}/schedules/{schedule_id} - Delete schedule","operationId":"update_agent_schedule_api_v2_agents__agent_id__schedules__schedule_id__put","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string","title":"Schedule Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentScheduleUpdate"}}}},"responses":{"200":{"description":"Schedule updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSchedule"}}}},"401":{"description":"Authentication required"},"403":{"description":"Editor permission required or project access denied"},"404":{"description":"Agent, schedule, or project not found"},"422":{"description":"Invalid cron expression or referenced resource not found"}}},"delete":{"tags":["agents"],"summary":"Delete Agent Schedule","description":"Delete an agent schedule.\n\nPermanently deletes a cron-based schedule. The agent will no longer\nexecute automatically at the scheduled times. Requires editor permission\non the agent.\n\n**Prerequisites:**\n- User must be authenticated\n- User must have editor permission on the agent (or be the owner)\n- Agent must exist\n- Schedule must exist\n\n**Path Parameters:**\n- `agent_id` (string, required): Agent identifier (prefixed with 'agt_')\n- `schedule_id` (string, required): Schedule identifier (prefixed with 'sched_')\n\n**Example Request:**\n```\nDELETE /agents/agt_01JDX2LZ8Q9R0STUVWXYZ1234/schedules/sched_01JDX2N4PQRS\n```\n\n**Example Response:**\n```\nHTTP/1.1 204 No Content\n```\n\n**Error Responses:**\n- 401: Authentication required\n- 403: Editor permission required\n- 404: Agent or schedule not found\n\n**Related Endpoints:**\n- GET /agents/{agent_id}/schedules - List all schedules\n- POST /agents/{agent_id}/schedules - Create new schedule\n- PUT /agents/{agent_id}/schedules/{schedule_id} - Update schedule (use is_active=false to disable without deleting)","operationId":"delete_agent_schedule_api_v2_agents__agent_id__schedules__schedule_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string","title":"Schedule Id"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"204":{"description":"Schedule deleted successfully"},"401":{"description":"Authentication required"},"403":{"description":"Editor permission required"},"404":{"description":"Agent or schedule not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/agents/generate-agent-schema":{"post":{"tags":["agents"],"summary":"Generate Agent Schema","description":"Generate an agent YAML definition from a natural language description (SES-170).\n\nUses LLM to convert workflow descriptions into valid agent YAML definitions.\nValidates output against the AgentYAML schema using a SLAF retry loop\n(up to 5 attempts). Supports iterative refinement via conversation_id.\n\n**Prerequisites:**\n- Valid API key required\n\n**Request Body (GenerateAgentSchemaRequest):**\n- `description` (required): Natural language description of the agent workflow\n- `hints` (optional): Generation hints (inputs, tools, step_types, goal)\n- `conversation_id` (optional): ID to continue refining a previous generation\n- `feedback` (optional): Feedback on previous generation for refinement\n\n**Hints Object:**\n```json\n{\n    \"inputs\": [\"topic\", \"max_sources\"],\n    \"tools\": [\"web-search\"],\n    \"step_types\": [\"tool\", \"llm\"],\n    \"goal\": \"Research and summarize a topic\"\n}\n```\n\n**Example Request (new generation):**\n```json\n{\n    \"description\": \"An agent that searches the web for a topic and summarizes findings\",\n    \"hints\": {\n        \"inputs\": [\"topic\"],\n        \"tools\": [\"web-search\"]\n    }\n}\n```\n\n**Example Request (refinement):**\n```json\n{\n    \"conversation_id\": \"conv_abc123\",\n    \"feedback\": \"Add a step to extract key facts before summarizing\"\n}\n```\n\n**Example Response (200):**\n```json\n{\n    \"yaml_definition\": \"name: Research Agent\\nversion: \\\"1.0.0\\\"\\n...\",\n    \"parsed_definition\": {\"name\": \"Research Agent\", \"version\": \"1.0.0\", ...},\n    \"validation\": {\"is_valid\": true, \"errors\": [], \"warnings\": []},\n    \"conversation_id\": \"conv_abc123\",\n    \"suggestions\": [\"Consider adding error handling for empty search results\"],\n    \"model_used\": \"anthropic/claude-3.5-sonnet\",\n    \"tokens_used\": 1250,\n    \"generation_time_ms\": 3200\n}\n```\n\n**Error Responses:**\n- 401: Missing or invalid API key\n- 422: Generation failed after all SLAF retry attempts\n\n**Related Endpoints:**\n- POST /agents/validate — Validate an agent YAML definition\n- POST /agents — Create an agent from a YAML definition\n- POST /sops/generate-sop-schema — Generate an SOP definition","operationId":"generate_agent_schema_api_v2_agents_generate_agent_schema_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateAgentSchemaRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateAgentSchemaResponse"}}}},"422":{"description":"Generation failed after all SLAF retry attempts","content":{"application/json":{"example":{"detail":{"error":"GENERATION_FAILED","message":"LLM returned invalid JSON after 5 attempts","last_error":"Expecting ',' delimiter: line 8 column 3","attempts":5,"stage":"json_syntax"}}}}}}}},"/api/v2/code/stateless/python/lint":{"post":{"tags":["code","code"],"summary":"Lint Python Code Endpoint","description":"Validate stateless Python code without executing it.\n\nPerforms comprehensive security and style validation on Python code intended\nfor stateless execution. Optionally runs sample execution to verify functionality.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Request Body:**\n- `code` (required): Python code defining `def run(params)` (imports not allowed)\n- `sample_params` (optional): Dict to test with `run(params)` if validation passes\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/code/stateless/python/lint       -H \"Authorization: Bearer YOUR_API_KEY\"       -H \"Content-Type: application/json\"       -d '{\n    \"code\": \"def run(params):\\n    values = params.get(\"values\", [])\\n    return {\"sum\": sum(values), \"count\": len(values)}\",\n    \"sample_params\": {\"values\": [1, 2, 3, 4, 5]}\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"valid\": true,\n  \"errors\": [],\n  \"warnings\": [],\n  \"available_modules\": [\"json\", \"math\", \"datetime\", \"re\", \"collections\"],\n  \"sample_result\": {\"sum\": 15, \"count\": 5}\n}\n```\n\n**Validation Checks:**\n- No `import` statements (pre-imported safe modules available)\n- Must define `def run(params)` function\n- No blocked builtins (`eval`, `exec`, `compile`, `__import__`)\n- AST-based security analysis\n- Ruff linter checks (if available)\n\n**Available Pre-imported Modules:**\n- `json`, `math`, `datetime`, `re`, `collections`, `itertools`, `functools`\n\n**Error Response Examples:**\n```json\n{\n  \"valid\": false,\n  \"errors\": [\n    {\n      \"type\": \"missing_run_function\",\n      \"message\": \"Code must define a 'run(params)' function\",\n      \"line\": null\n    }\n  ],\n  \"warnings\": [],\n  \"available_modules\": [\"json\", \"math\", \"datetime\", \"re\", \"collections\"],\n  \"sample_result\": null\n}\n```\n\n**Related Endpoints:**\n- `POST /api/v2/code/stateless/python/exec` - Execute validated code with parameters","operationId":"lint_python_code_endpoint_api_v2_code_stateless_python_lint_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PythonLintRequest"}}}},"responses":{"200":{"description":"Code validation results with errors and warnings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PythonLintResponse"}}}},"401":{"description":"Invalid or missing API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/code/stateless/python/exec":{"post":{"tags":["code","code"],"summary":"Exec Python Code Endpoint","description":"Execute validated stateless Python code with parameters in isolated environment.\n\nValidates and executes Python code in a secure subprocess with pre-imported\nsafe modules and enforced timeout limits.\n\n**Prerequisites:**\n- Valid API key with any scope\n\n**Request Body:**\n- `code` (required): Python code defining `def run(params)` (imports not allowed)\n- `params` (optional): Dict passed to `run(params)` (default: empty dict)\n- `timeout` (optional): Execution timeout in seconds, 1-30 (default: 5)\n\n**Example Request:**\n```bash\ncurl -X POST https://api.taiso.ai/api/v2/code/stateless/python/exec       -H \"Authorization: Bearer YOUR_API_KEY\"       -H \"Content-Type: application/json\"       -d '{\n    \"code\": \"def run(params):\\n    numbers = params.get(\"numbers\", [])\\n    return {\\n        \"sum\": sum(numbers),\\n        \"avg\": sum(numbers) / len(numbers) if numbers else 0,\\n        \"max\": max(numbers) if numbers else None\\n    }\",\n    \"params\": {\"numbers\": [10, 20, 30, 40, 50]},\n    \"timeout\": 10\n  }'\n```\n\n**Example Response:**\n```json\n{\n  \"result\": {\n    \"sum\": 150,\n    \"avg\": 30.0,\n    \"max\": 50\n  },\n  \"execution_time_ms\": 12.5,\n  \"stdout\": \"\",\n  \"stderr\": \"\"\n}\n```\n\n**Security Features:**\n- Isolated subprocess execution\n- No import statements allowed\n- Pre-imported safe modules only\n- Blocked dangerous builtins (`eval`, `exec`, `compile`)\n- Enforced timeout limits\n- AST-based code validation\n\n**Available Pre-imported Modules:**\n- `json`, `math`, `datetime`, `re`, `collections`, `itertools`, `functools`\n\n**Error Responses:**\n\nValidation Failed (422):\n```json\n{\n  \"error\": \"validation_failed\",\n  \"message\": \"Code validation failed\",\n  \"validation_errors\": [\n    {\n      \"type\": \"blocked_builtin\",\n      \"message\": \"Use of blocked builtin: eval\",\n      \"line\": 3\n    }\n  ]\n}\n```\n\nTimeout Exceeded (408):\n```json\n{\n  \"error\": \"timeout\",\n  \"message\": \"Execution timeout after 5 seconds\"\n}\n```\n\nExecution Error (500):\n```json\n{\n  \"error\": \"execution_failed\",\n  \"message\": \"division by zero\"\n}\n```\n\n**Related Endpoints:**\n- `POST /api/v2/code/stateless/python/lint` - Validate code without executing","operationId":"exec_python_code_endpoint_api_v2_code_stateless_python_exec_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PythonExecRequest"}}}},"responses":{"200":{"description":"Execution result with output and timing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PythonExecResponse"}}}},"401":{"description":"Invalid or missing API key"},"408":{"description":"Execution timeout exceeded"},"422":{"description":"Code validation failed"},"500":{"description":"Execution error"}}}},"/api/v2/docs/list":{"get":{"tags":["documentation"],"summary":"List Documentation","description":"List all available documentation.\n\nReturns a list of documentation files that can be requested via\nthe `/docs/{doc_name}` endpoint.\n\n**Prerequisites:**\n- Valid API key\n\n**Example Request:**\n```bash\ncurl https://api.taiso.ai/api/v2/docs/list \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Example Response:**\n```json\n{\n  \"documents\": [\n    {\"name\": \"developer-tutorial\", \"title\": \"Taiso API Developer Tutorial\"},\n    {\"name\": \"sops-guide\", \"title\": \"SOPs Guide\"}\n  ],\n  \"total\": 2\n}\n```","operationId":"list_documentation_api_v2_docs_list_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"List of available documentation","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Documentation Api V2 Docs List Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/docs/{doc_name}":{"get":{"tags":["documentation"],"summary":"Get Documentation","description":"Get documentation by name in specified format.\n\nRetrieve developer documentation in JSON, Markdown, or HTML format.\nHTML output includes syntax highlighting and a table of contents.\n\n**Prerequisites:**\n- Valid API key\n\n**Path Parameters:**\n- `doc_name`: Name of the documentation (e.g., \"developer-tutorial\")\n\n**Query Parameters:**\n- `format`: Output format - \"json\" (default), \"markdown\"/\"md\", or \"html\"\n\n**Example Requests:**\n```bash\n# Get as JSON (includes metadata)\ncurl \"https://api.taiso.ai/api/v2/docs/developer-tutorial?format=json\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Get as raw Markdown\ncurl \"https://api.taiso.ai/api/v2/docs/developer-tutorial?format=md\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Get as rendered HTML\ncurl \"https://api.taiso.ai/api/v2/docs/developer-tutorial?format=html\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**JSON Response:**\n```json\n{\n  \"name\": \"developer-tutorial\",\n  \"title\": \"Taiso API Developer Tutorial\",\n  \"version\": \"1.0.0\",\n  \"markdown\": \"# Taiso API Developer Tutorial...\",\n  \"html\": \"<!DOCTYPE html>...\",\n  \"size_bytes\": 45678\n}\n```\n\n**Markdown Response:**\nReturns raw markdown content with `Content-Type: text/markdown`.\n\n**HTML Response:**\nReturns rendered HTML with embedded CSS, syntax highlighting,\nand table of contents. Supports light/dark mode.","operationId":"get_documentation_api_v2_docs__doc_name__get","parameters":[{"name":"doc_name","in":"path","required":true,"schema":{"type":"string","title":"Doc Name"}},{"name":"format","in":"query","required":false,"schema":{"$ref":"#/components/schemas/DocFormat","description":"Response format: json (metadata + content), markdown/md (raw), or html (rendered)","default":"json"},"description":"Response format: json (metadata + content), markdown/md (raw), or html (rendered)"},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Documentation content","content":{"application/json":{"schema":{}}}},"404":{"description":"Documentation not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","description":"Root endpoint - Shows basic API information and environment.\n\nUseful for quick verification of which environment you're hitting.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"Agent":{"properties":{"id":{"type":"string","title":"Id","description":"Agent ID (prefixed with 'agt_')"},"owner_id":{"type":"string","title":"Owner Id","description":"User ID of the agent owner"},"name":{"type":"string","title":"Name","description":"Agent name"},"version":{"type":"string","title":"Version","description":"Semantic version"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Agent description"},"is_public":{"type":"boolean","title":"Is Public","description":"Public visibility flag"},"yaml_definition":{"type":"string","title":"Yaml Definition","description":"YAML definition source"},"compiled_sop_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compiled Sop Id","description":"Compiled SOP ID (ephemeral)"},"goal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Goal","description":"Agent goal extracted from YAML"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"Searchable tags"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last update timestamp"}},"type":"object","required":["id","owner_id","name","version","is_public","yaml_definition","created_at","updated_at"],"title":"Agent","description":"Response model for an agent.\n\nRepresents a complete agent with metadata and definition."},"AgentCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Agent name"},"version":{"type":"string","title":"Version","description":"Semantic version (e.g., '1.0.0')"},"yaml_definition":{"type":"string","title":"Yaml Definition","description":"Complete YAML definition as string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Agent description"},"is_public":{"type":"boolean","title":"Is Public","description":"Make agent publicly accessible","default":false},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags","description":"Searchable tags"}},"type":"object","required":["name","version","yaml_definition"],"title":"AgentCreate","description":"Request model for creating a new agent.\n\nThe agent YAML definition will be parsed, validated, and compiled\nto an SOP definition upon creation.","example":{"description":"Multi-step research and summarization agent","is_public":false,"name":"Research Assistant","tags":["research","demo"],"version":"1.0.0","yaml_definition":"name: Research Assistant\nversion: \"1.0.0\"\ngoal: Research a topic and summarize key findings\ninputs:\n  - name: topic\n    type: text\n    description: Topic to research\n    required: true\n    source: user-provided\nsteps:\n  - id: web_search\n    description: Search web for the topic\n    type: tool\n    tool: web_search\n    input: topic\n    output: search_results\n  - id: summarize\n    description: Summarize findings\n    type: llm\n    prompt: \"Summarize the following search results: {{search_results}}\"\n    input: search_results\n    output: summary\n"}},"AgentInvokeRequest":{"properties":{"project_id":{"type":"string","title":"Project Id","description":"Project ID for execution context"},"inputs":{"additionalProperties":true,"type":"object","title":"Inputs","description":"Input data matching agent's input schema"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","description":"Idempotency key to prevent duplicate runs"},"parent_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Run Id","description":"Parent run ID for sub-agent invocations"},"depth":{"type":"integer","title":"Depth","description":"Nesting depth (0 = top-level, max 5)","default":0},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url","description":"URL to receive POST callback when agent run completes or fails. Must be HTTPS in production (HTTP allowed in test/dev)."}},"type":"object","required":["project_id","inputs"],"title":"AgentInvokeRequest","description":"Request model for invoking an agent.\n\nTriggers agent execution within a project context. The agent will\ncompile to an SOP and execute asynchronously via the job system.","example":{"depth":0,"idempotency_key":"invoke-2025-11-27T10:15:00Z","inputs":{"max_sources":10,"topic":"mechanisms of mucosal barrier protection"},"project_id":"prj-1234567890abcdef","webhook_url":"https://example.com/webhooks/agent-status"}},"AgentInvokeResponse":{"properties":{"run_id":{"type":"string","title":"Run Id","description":"Agent run ID (prefixed with 'arun_')"},"job_id":{"type":"string","title":"Job Id","description":"Job ID for tracking execution"},"status":{"type":"string","title":"Status","description":"Initial status: 'pending', 'running'"},"agent_id":{"type":"string","title":"Agent Id","description":"Agent ID"},"agent_version":{"type":"string","title":"Agent Version","description":"Agent version"}},"type":"object","required":["run_id","job_id","status","agent_id","agent_version"],"title":"AgentInvokeResponse","description":"Response model for agent invocation.\n\nReturns immediately with run and job IDs for tracking execution.","example":{"agent_id":"agt-01JDX2LZ8Q9R0STUVWXYZ1234","agent_version":"1.0.0","job_id":"job-01JDX2M2HXQ3R4S5T6UVWX78YZ","run_id":"arun-01JDX2M1Z6G2T7Y8Z9ABCD12EF","status":"pending"}},"AgentPermissionCreate":{"properties":{"user_id":{"type":"string","title":"User Id","description":"User ID"},"role":{"type":"string","pattern":"^(viewer|executor|editor)$","title":"Role","description":"Permission role"}},"type":"object","required":["user_id","role"],"title":"AgentPermissionCreate","description":"Request model for granting agent permission."},"AgentRun":{"properties":{"id":{"type":"string","title":"Id","description":"Run ID (prefixed with 'arun_')"},"agent_id":{"type":"string","title":"Agent Id","description":"Agent ID"},"agent_version":{"type":"string","title":"Agent Version","description":"Agent version used for this run"},"user_id":{"type":"string","title":"User Id","description":"User who triggered the run"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Project context"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Associated job ID"},"sop_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sop Run Id","description":"Underlying SOP run ID"},"trigger_type":{"type":"string","title":"Trigger Type","description":"Trigger: 'manual', 'schedule', 'webhook', 'api'"},"parent_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Run Id","description":"Parent run ID if this is a sub-agent"},"depth":{"type":"integer","title":"Depth","description":"Nesting depth (0 = top-level)"},"inputs":{"additionalProperties":true,"type":"object","title":"Inputs","description":"Input data"},"outputs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Outputs","description":"Output data (when completed). Omitted when include_output=false."},"output_file_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Output File Id","description":"File ID of the saved output artifact (from linked job), if any."},"output_size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Output Size Bytes","description":"Size of the output in bytes (from linked job), if known."},"status":{"type":"string","title":"Status","description":"Status: 'pending', 'running', 'completed', 'failed', 'cancelled'"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Error message if failed"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Run creation timestamp"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At","description":"Run start timestamp"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"Run completion timestamp"}},"type":"object","required":["id","agent_id","agent_version","user_id","trigger_type","depth","inputs","status","created_at"],"title":"AgentRun","description":"Response model for an agent run.\n\nRepresents a single execution of an agent with inputs, outputs,\nand status tracking.","example":{"agent_id":"agt-01JDX2LZ8Q9R0STUVWXYZ1234","agent_version":"1.0.0","completed_at":"2025-11-27T10:15:20Z","created_at":"2025-11-27T10:15:00Z","depth":0,"id":"arun-01JDX2M1Z6G2T7Y8Z9ABCD12EF","inputs":{"max_sources":10,"topic":"mucosal barrier protection"},"job_id":"job-01JDX2M2HXQ3R4S5T6UVWX78YZ","outputs":{"summary":"The mucosal barrier is supported by..."},"project_id":"prj-1234567890abcdef","sop_run_id":"srun-01JDX2M3ABCDEF...","started_at":"2025-11-27T10:15:02Z","status":"completed","trigger_type":"manual","user_id":"usr-01HXYZ..."}},"AgentRunWebhookPayload":{"properties":{"event":{"type":"string","enum":["agent_run.completed","agent_run.failed"],"title":"Event","description":"Event type matching terminal status."},"run_id":{"type":"string","title":"Run Id","description":"Agent run identifier."},"agent_id":{"type":"string","title":"Agent Id","description":"Agent identifier."},"agent_version":{"type":"string","title":"Agent Version","description":"Agent version used for this run."},"project_id":{"type":"string","title":"Project Id","description":"Project that owns the run."},"status":{"type":"string","enum":["completed","failed"],"title":"Status","description":"Terminal run status that triggered this notification."},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At","description":"ISO 8601 timestamp when the run reached this terminal state."},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Human-readable error summary when failed; null on success."},"output_preview":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Output Preview","description":"Agent outputs inline if JSON < 4KB; null otherwise or on failure."}},"type":"object","required":["event","run_id","agent_id","agent_version","project_id","status"],"title":"AgentRunWebhookPayload","description":"JSON body POSTed to your `webhook_url` when an agent run finishes (outbound callback, SES-261).\n\nThis is **not** a request you send to Taiso -- it is the payload **your server receives**\nwhen agent run execution reaches a terminal state.\n\n**When fired:** Exactly once per terminal transition -- `completed` or `failed` only.\n\n**Delivery:** HTTP POST, `Content-Type: application/json`, 10s timeout, up to 3 retries\n(30s, 120s, 300s backoff). Respond with any 2xx quickly. At-least-once delivery -- dedupe\non `(run_id, status)`.","examples":[{"agent_id":"agt_01JDX2LZ8Q9R0STU","agent_version":"1.0.0","completed_at":"2026-06-14T12:00:00Z","event":"agent_run.completed","output_preview":{"summary":"Research findings..."},"project_id":"prj_1a2b3c4d5e6f","run_id":"arun_01JDX2M1Z6G2T7Y8","status":"completed"},{"agent_id":"agt_01JDX2LZ8Q9R0STU","agent_version":"1.0.0","completed_at":"2026-06-14T12:05:00Z","error_message":"Agent execution failed: SOP execution failed","event":"agent_run.failed","project_id":"prj_1a2b3c4d5e6f","run_id":"arun_01JDX2M1KLMNOPQR","status":"failed"}]},"AgentSchedule":{"properties":{"id":{"type":"string","title":"Id","description":"Schedule ID (prefixed with 'sched_')"},"agent_id":{"type":"string","title":"Agent Id","description":"Agent ID"},"user_id":{"type":"string","title":"User Id","description":"User who created the schedule"},"cron_expression":{"type":"string","title":"Cron Expression","description":"Cron expression"},"timezone":{"type":"string","title":"Timezone","description":"Timezone"},"is_active":{"type":"boolean","title":"Is Active","description":"Whether schedule is active"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Project context"},"input_config":{"additionalProperties":true,"type":"object","title":"Input Config","description":"Input configuration used for scheduled runs"},"last_run_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Run At","description":"Last execution timestamp"},"next_run_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Run At","description":"Next scheduled execution"},"total_runs":{"type":"integer","title":"Total Runs","description":"Total number of executions"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Schedule creation timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last update timestamp"}},"type":"object","required":["id","agent_id","user_id","cron_expression","timezone","is_active","total_runs","created_at","updated_at"],"title":"AgentSchedule","description":"Response model for an agent schedule.\n\nRepresents a scheduled recurring execution of an agent.","example":{"agent_id":"agt-01JDX2LZ8Q9R0STUVWXYZ1234","created_at":"2025-11-01T08:00:00Z","cron_expression":"0 9 * * 1-5","id":"sched-01JDX2N4PQRS...","input_config":{"max_sources":5,"topic":"IBD treatment pipeline"},"is_active":true,"last_run_at":"2025-11-27T09:00:01Z","next_run_at":"2025-11-28T09:00:00Z","project_id":"prj-1234567890abcdef","timezone":"UTC","total_runs":12,"updated_at":"2025-11-27T09:00:01Z","user_id":"usr-01HXYZ..."}},"AgentScheduleCreate":{"properties":{"cron_expression":{"type":"string","title":"Cron Expression","description":"Cron expression (e.g., '0 9 * * 1-5' for weekdays at 9am, evaluated in UTC)"},"timezone":{"type":"string","title":"Timezone","description":"Timezone for schedule (currently must be 'UTC'; future-proofed for other zones).","default":"UTC"},"project_id":{"type":"string","title":"Project Id","description":"Project context for scheduled runs"},"input_config":{"additionalProperties":true,"type":"object","title":"Input Config","description":"Input configuration for scheduled runs (JSONB input_config column)"}},"type":"object","required":["cron_expression","project_id"],"title":"AgentScheduleCreate","description":"Request model for creating an agent schedule.\n\nSchedules periodic execution of an agent with specified inputs.","example":{"cron_expression":"0 9 * * 1-5","input_config":{"max_sources":5,"topic":"ulcerative colitis mucosal healing"},"project_id":"prj-1234567890abcdef","timezone":"UTC"}},"AgentScheduleUpdate":{"properties":{"cron_expression":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cron Expression","description":"Updated cron expression"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone","description":"Updated timezone"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Enable/disable the schedule"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Updated project context"},"input_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input Config","description":"Updated input configuration for scheduled runs"}},"type":"object","title":"AgentScheduleUpdate","description":"Request model for updating an agent schedule.\n\nAll fields are optional - only provided fields will be updated."},"AgentUpdate":{"properties":{"yaml_definition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Yaml Definition","description":"Updated YAML definition"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Updated description"},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public","description":"Updated public visibility"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags","description":"Updated tags"}},"type":"object","title":"AgentUpdate","description":"Request model for updating an existing agent.\n\nUpdating the YAML definition will trigger recompilation.\nAll fields are optional - only provided fields will be updated."},"AgentUsageStats":{"properties":{"agent_id":{"type":"string","title":"Agent Id","description":"Agent ID"},"total_runs":{"type":"integer","title":"Total Runs","description":"Total number of runs"},"successful_runs":{"type":"integer","title":"Successful Runs","description":"Number of successful runs"},"failed_runs":{"type":"integer","title":"Failed Runs","description":"Number of failed runs"},"avg_duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Duration Seconds","description":"Average run duration in seconds"},"avg_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Cost Usd","description":"Average cost per run in USD"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At","description":"Last execution timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Stats last updated timestamp"}},"type":"object","required":["agent_id","total_runs","successful_runs","failed_runs","updated_at"],"title":"AgentUsageStats","description":"Response model for agent usage statistics.\n\nTracks execution metrics for an agent across all runs.","example":{"agent_id":"agt-01JDX2LZ8Q9R0STUVWXYZ1234","avg_cost_usd":0.0123,"avg_duration_seconds":18.5,"failed_runs":3,"last_used_at":"2025-11-27T10:15:20Z","successful_runs":39,"total_runs":42,"updated_at":"2025-11-27T10:15:20Z"}},"AgentValidateResponse":{"properties":{"is_valid":{"type":"boolean","title":"Is Valid","description":"Whether the agent definition is valid"},"errors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Errors","description":"Validation errors"},"warnings":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Warnings","description":"Validation warnings"},"compiled_sop":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Compiled Sop","description":"Compiled SOP definition (if valid)"}},"type":"object","required":["is_valid"],"title":"AgentValidateResponse","description":"Response model for agent validation.\n\nValidates agent YAML definition and compilation without creating\nthe agent or executing it."},"AskRequest":{"properties":{"question":{"type":"string","minLength":1,"title":"Question","description":"Question about SOP Engine API"},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context","description":"Optional context to guide the response (e.g., 'I am a Python developer working on a REST API')"},"response_format":{"type":"string","enum":["json","yaml","markdown"],"title":"Response Format","description":"Response format","default":"json"},"include_code_examples":{"type":"boolean","title":"Include Code Examples","description":"Include code examples in response","default":true},"code_languages":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Code Languages","description":"Preferred code languages (e.g., ['curl', 'python'])"}},"additionalProperties":false,"type":"object","required":["question"],"title":"AskRequest","description":"Request for single-turn Q&A."},"AuditConfig":{"properties":{"classifications":{"items":{"type":"string","enum":["supported","disputed","unclear","not_found"]},"type":"array","title":"Classifications","description":"Which classifications to check for","default":["supported","disputed","unclear","not_found"]},"require_citations":{"type":"boolean","title":"Require Citations","description":"Whether to extract and return citations","default":true},"max_citations_per_field":{"anyOf":[{"type":"integer","maximum":10.0,"minimum":1.0},{"type":"null"}],"title":"Max Citations Per Field","description":"Maximum citations per field (null = unlimited)","default":3},"confidence_threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence Threshold","description":"Minimum confidence to classify as 'supported'","default":0.7},"include_character_positions":{"type":"boolean","title":"Include Character Positions","description":"Include char_start/char_end in citations","default":true},"check_contradictions":{"type":"boolean","title":"Check Contradictions","description":"Look for contradictory evidence","default":true},"verbosity":{"type":"string","enum":["minimal","standard","detailed"],"title":"Verbosity","description":"Response detail level","default":"standard"},"completeness_check":{"type":"boolean","title":"Completeness Check","description":"Enable completeness checking mode","default":false},"required_fields":{"items":{"type":"string"},"type":"array","title":"Required Fields","description":"List of required fields for completeness check"},"treat_empty_as_unclear":{"type":"boolean","title":"Treat Empty As Unclear","description":"Treat null/empty values as 'unclear' vs 'not_found'","default":true}},"type":"object","title":"AuditConfig","description":"Configuration for audit behavior."},"AuditMetadata":{"properties":{"audit_duration_ms":{"type":"integer","title":"Audit Duration Ms","description":"Audit execution time in milliseconds"},"llm_model":{"type":"string","title":"Llm Model","description":"LLM model used for auditing"},"tokens_used":{"additionalProperties":{"type":"integer"},"type":"object","title":"Tokens Used","description":"Token usage breakdown (input, output, total)"},"cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cost Usd","description":"Estimated cost in USD"}},"type":"object","required":["audit_duration_ms","llm_model","tokens_used"],"title":"AuditMetadata","description":"Metadata about audit execution."},"AuditRequest":{"properties":{"extracted_data":{"additionalProperties":true,"type":"object","title":"Extracted Data","description":"The structured data to audit"},"source_documents":{"items":{"$ref":"#/components/schemas/SourceDocument"},"type":"array","minItems":1,"title":"Source Documents","description":"Source documents used for extraction"},"audit_config":{"anyOf":[{"$ref":"#/components/schemas/AuditConfig"},{"type":"null"}],"description":"Audit configuration (uses defaults if not provided)"},"fields_to_audit":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fields To Audit","description":"Specific fields to audit (null = audit all fields)"}},"type":"object","required":["extracted_data","source_documents"],"title":"AuditRequest","description":"Request model for SLAF audit."},"AuditResponse":{"properties":{"audit_results":{"additionalProperties":{"$ref":"#/components/schemas/FieldAuditResult"},"type":"object","title":"Audit Results","description":"Audit results for each field"},"summary":{"$ref":"#/components/schemas/AuditSummary","description":"Summary statistics and assessment"},"metadata":{"$ref":"#/components/schemas/AuditMetadata","description":"Metadata about audit execution"}},"type":"object","required":["audit_results","summary","metadata"],"title":"AuditResponse","description":"Response model for SLAF audit."},"AuditSummary":{"properties":{"total_fields":{"type":"integer","title":"Total Fields","description":"Total number of fields audited"},"total_fields_audited":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Fields Audited","description":"Number of fields actually audited (if selective)"},"fields_skipped":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fields Skipped","description":"List of fields skipped (if selective auditing)"},"by_classification":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Classification","description":"Count by classification type"},"by_completeness":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"By Completeness","description":"Count by completeness status (if enabled)"},"overall_confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Overall Confidence","description":"Average confidence across all fields"},"hallucination_risk":{"type":"string","enum":["low","medium","high"],"title":"Hallucination Risk","description":"Overall hallucination risk assessment"},"recommendation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recommendation","description":"Human-readable recommendation for next steps"},"pass_fail":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Pass Fail","description":"Pass/fail assessment with reason"},"completeness_check":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Completeness Check","description":"Completeness check results (if enabled)"},"evidence_statistics":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Evidence Statistics","description":"Statistics about evidence found"}},"type":"object","required":["total_fields","by_classification","overall_confidence","hallucination_risk"],"title":"AuditSummary","description":"Summary statistics for audit results."},"Body_upload_file_api_v2_files_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"path":{"type":"string","title":"Path","default":"/"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"tags":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tags"}},"type":"object","required":["file"],"title":"Body_upload_file_api_v2_files_post"},"CalcRequest":{"properties":{"expression":{"type":"string","title":"Expression"}},"type":"object","required":["expression"],"title":"CalcRequest"},"CalcResponse":{"properties":{"expression":{"type":"string","title":"Expression"},"result":{"type":"number","title":"Result"},"message":{"type":"string","title":"Message","default":"Calculation successful"}},"type":"object","required":["expression","result"],"title":"CalcResponse"},"Chat":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"project_id":{"type":"string","title":"Project Id"},"id":{"type":"string","title":"Id"},"owner_id":{"type":"string","title":"Owner Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["name","project_id","id","owner_id","created_at","updated_at"],"title":"Chat","description":"Response model for a chat."},"ChatCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"}},"type":"object","required":["name"],"title":"ChatCreate","description":"Request model for creating a chat."},"ChatMessage-Input":{"properties":{"role":{"type":"string","enum":["user","assistant","system"],"title":"Role","description":"Message role"},"content":{"type":"string","minLength":1,"title":"Content","description":"Message content"}},"type":"object","required":["role","content"],"title":"ChatMessage","description":"Single message in a conversation."},"ChatMessage-Output":{"properties":{"content":{"type":"string","minLength":1,"title":"Content","description":"Message content"},"role":{"type":"string","pattern":"^(system|user|assistant|tool)$","title":"Role","default":"user"},"tool_calls":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Tool Calls"},"tool_call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tool Call Id"},"id":{"type":"string","title":"Id"},"chat_id":{"type":"string","title":"Chat Id"},"message_index":{"type":"integer","title":"Message Index"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["content","id","chat_id","message_index","created_at"],"title":"ChatMessage","description":"Response model for a chat message."},"ChatMessageCreate":{"properties":{"content":{"type":"string","minLength":1,"title":"Content","description":"Message content"},"role":{"type":"string","pattern":"^(system|user|assistant|tool)$","title":"Role","default":"user"},"tool_calls":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Tool Calls"},"tool_call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tool Call Id"},"use_rag":{"type":"boolean","title":"Use Rag","default":false},"rag_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Rag Config"}},"type":"object","required":["content"],"title":"ChatMessageCreate","description":"Request model for creating a chat message."},"ChatUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"}},"type":"object","title":"ChatUpdate","description":"Request model for updating chat metadata."},"CitationsConfig":{"properties":{"max_citations_per_field":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Citations Per Field","description":"Maximum citations per field (null = unlimited)"},"include_relevance_scores":{"type":"boolean","title":"Include Relevance Scores","description":"Include relevance scores in response","default":true},"group_by_source":{"type":"boolean","title":"Group By Source","description":"Group by source document instead of by field","default":false},"include_context":{"type":"boolean","title":"Include Context","description":"Include surrounding text context","default":true}},"type":"object","title":"CitationsConfig","description":"Configuration for citations endpoint."},"CitationsRequest":{"properties":{"extracted_data":{"additionalProperties":true,"type":"object","title":"Extracted Data"},"source_documents":{"items":{"$ref":"#/components/schemas/SourceDocument"},"type":"array","title":"Source Documents"},"config":{"anyOf":[{"$ref":"#/components/schemas/CitationsConfig"},{"type":"null"}]}},"type":"object","required":["extracted_data","source_documents"],"title":"CitationsRequest","description":"Request model for citations endpoint."},"CitationsResponse":{"properties":{"citations_by_field":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/FieldCitations"},"type":"object"},{"type":"null"}],"title":"Citations By Field"},"citations_by_source":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/SourceCitations"},"type":"object"},{"type":"null"}],"title":"Citations By Source"},"summary":{"additionalProperties":true,"type":"object","title":"Summary"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/AuditMetadata"},{"type":"null"}]}},"type":"object","required":["summary"],"title":"CitationsResponse","description":"Response model for citations endpoint (grouped by field)."},"ConvertToPDFRequest":{"properties":{"file_id":{"type":"string","title":"File Id","description":"File ID to convert to PDF"},"project_id":{"type":"string","title":"Project Id","description":"Project containing the file"},"output_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Output Path","description":"Path to save converted PDF","default":"/converted"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Options","description":"PDF conversion options"}},"type":"object","required":["file_id","project_id"],"title":"ConvertToPDFRequest","description":"Request model for PDF conversion."},"ConvertToPDFResponse":{"properties":{"file_id":{"type":"string","title":"File Id"},"filename":{"type":"string","title":"Filename"},"path":{"type":"string","title":"Path"},"size_bytes":{"type":"integer","title":"Size Bytes"},"pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pages"},"download_url":{"type":"string","title":"Download Url"}},"type":"object","required":["file_id","filename","path","size_bytes","download_url"],"title":"ConvertToPDFResponse","description":"Response model for PDF conversion."},"CrawlSimpleRequest":{"properties":{"url":{"type":"string","title":"Url","description":"URL to crawl"},"extract_formats":{"items":{"type":"string"},"type":"array","title":"Extract Formats","description":"Content formats: html, text, markdown","default":["markdown"]},"include_metadata":{"type":"boolean","title":"Include Metadata","description":"Extract page metadata","default":true},"include_links":{"type":"boolean","title":"Include Links","description":"Extract links from page","default":true},"timeout":{"type":"integer","title":"Timeout","description":"Timeout in seconds","default":10}},"type":"object","required":["url"],"title":"CrawlSimpleRequest","description":"Request model for simple web crawling."},"CrawlSimpleResponse":{"properties":{"url":{"type":"string","title":"Url"},"final_url":{"type":"string","title":"Final Url"},"status_code":{"type":"integer","title":"Status Code"},"load_time_ms":{"type":"integer","title":"Load Time Ms"},"size_bytes":{"type":"integer","title":"Size Bytes"},"content":{"$ref":"#/components/schemas/PageContent"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/PageMetadata"},{"type":"null"}]},"links":{"anyOf":[{"$ref":"#/components/schemas/PageLinks"},{"type":"null"}]}},"type":"object","required":["url","final_url","status_code","load_time_ms","size_bytes","content"],"title":"CrawlSimpleResponse","description":"Response model for simple web crawling."},"CreateAPIKeyRequest":{"properties":{"name":{"type":"string","title":"Name"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes"},"max_cost_cents_per_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Cost Cents Per Month"}},"type":"object","required":["name"],"title":"CreateAPIKeyRequest"},"CreateAPIKeyResponse":{"properties":{"api_key":{"type":"string","title":"Api Key"},"key_id":{"type":"string","title":"Key Id"},"key_suffix":{"type":"string","title":"Key Suffix"},"warning":{"type":"string","title":"Warning","default":"Save this key now. It won't be shown again."}},"type":"object","required":["api_key","key_id","key_suffix"],"title":"CreateAPIKeyResponse"},"DOCXExtractRequest":{"properties":{"docx_data":{"type":"string","minLength":100,"title":"Docx Data","description":"Base64-encoded DOCX file data"},"include_tables":{"type":"boolean","title":"Include Tables","description":"Include table text in extraction","default":true},"include_headers":{"type":"boolean","title":"Include Headers","description":"Include header text in extraction","default":true},"include_footers":{"type":"boolean","title":"Include Footers","description":"Include footer text in extraction","default":true}},"type":"object","required":["docx_data"],"title":"DOCXExtractRequest","description":"Request model for basic DOCX text extraction."},"DOCXExtractResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Whether extraction succeeded"},"text":{"type":"string","title":"Text","description":"Extracted text content"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"Document metadata (author, title, dates, etc.)"},"structure":{"additionalProperties":{"type":"integer"},"type":"object","title":"Structure","description":"Document structure (paragraph count, table count, etc.)"},"tables":{"items":{"$ref":"#/components/schemas/DOCXTableInfo"},"type":"array","title":"Tables","description":"Information about tables in document"}},"type":"object","required":["success","text"],"title":"DOCXExtractResponse","description":"Response model for basic DOCX extraction."},"DOCXExtractWithSchemaRequest":{"properties":{"docx_data":{"type":"string","minLength":100,"title":"Docx Data","description":"Base64-encoded DOCX file data"},"schema":{"additionalProperties":true,"type":"object","title":"Schema","description":"Schema definition with field extraction patterns"},"include_tables":{"type":"boolean","title":"Include Tables","description":"Search within tables","default":true},"search_headers":{"type":"boolean","title":"Search Headers","description":"Search within headers","default":true},"search_footers":{"type":"boolean","title":"Search Footers","description":"Search within footers","default":false}},"type":"object","required":["docx_data","schema"],"title":"DOCXExtractWithSchemaRequest","description":"Request model for schema-based DOCX extraction."},"DOCXExtractWithSchemaResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Whether extraction succeeded"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Overall confidence score"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"Document metadata"},"fields":{"additionalProperties":true,"type":"object","title":"Fields","description":"Extracted field values matching schema"}},"type":"object","required":["success","confidence"],"title":"DOCXExtractWithSchemaResponse","description":"Response model for schema-based DOCX extraction."},"DOCXTableInfo":{"properties":{"table_index":{"type":"integer","title":"Table Index","description":"Zero-based table index"},"rows":{"type":"integer","title":"Rows","description":"Number of rows"},"columns":{"type":"integer","title":"Columns","description":"Number of columns"},"text":{"type":"string","title":"Text","description":"Table content as text"}},"type":"object","required":["table_index","rows","columns","text"],"title":"DOCXTableInfo","description":"Information about a table in the document."},"DataSource":{"properties":{"value":{"title":"Value"},"source":{"type":"string","title":"Source"},"confidence":{"type":"number","title":"Confidence"}},"type":"object","required":["value","source","confidence"],"title":"DataSource","description":"Source information for extracted data."},"DecisionChoice":{"properties":{"choice_id":{"anyOf":[{"type":"string","maxLength":50,"minLength":1,"pattern":"^[a-z][a-z0-9_]*$"},{"type":"null"}],"title":"Choice Id","description":"Canonical unique identifier for this choice"},"id":{"anyOf":[{"type":"string","maxLength":50,"minLength":1,"pattern":"^[a-z][a-z0-9_]*$"},{"type":"null"}],"title":"Id","description":"Deprecated alias for choice_id. Use choice_id instead."},"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label","description":"Human-readable label for the choice"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description","description":"Optional detailed description of when to pick this choice"},"criteria":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":10},{"type":"null"}],"title":"Criteria","description":"Optional list of specific criteria that favor this choice (LLM strategy)"},"is_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Default","description":"Deprecated. Use top-level 'default' block instead."},"when_true":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"When True","description":"Deprecated. Code strategy now uses scalar matching against choice_id."},"when":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"When","description":"Deprecated. Use code_expression with scalar matching instead."}},"type":"object","required":["label"],"title":"DecisionChoice","description":"A single choice option for the decision.\n\nSES-161: choice_id is canonical. id is accepted as a deprecated alias.\nis_default, when, when_true are removed in v2.\n\nExample:\n    {\n        \"choice_id\": \"billing\",\n        \"label\": \"Billing Issue\",\n        \"description\": \"Questions about invoices, payments, charges\",\n        \"criteria\": [\"Invoice mentions\", \"Payment issues\", \"Refund requests\"]\n    }"},"DecisionDefault":{"properties":{"choice_id":{"anyOf":[{"type":"string","maxLength":50,"minLength":1,"pattern":"^[a-z][a-z0-9_]*$"},{"type":"null"}],"title":"Choice Id","description":"Canonical unique identifier for the default choice"},"id":{"anyOf":[{"type":"string","maxLength":50,"minLength":1,"pattern":"^[a-z][a-z0-9_]*$"},{"type":"null"}],"title":"Id","description":"Deprecated alias for choice_id"},"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label","description":"Human-readable label for the default choice"}},"type":"object","required":["label"],"title":"DecisionDefault","description":"Top-level default block for the decision.\n\nSES-161: default is now a separate top-level block, not inline is_default.\n\nExample:\n    {\n        \"choice_id\": \"other\",\n        \"label\": \"Other\"\n    }"},"DecisionRequest":{"properties":{"strategy":{"type":"string","enum":["llm","code","hybrid","expr"],"title":"Strategy","description":"Execution strategy: 'llm' (default), 'code', 'expr' (alias of code), 'hybrid'","default":"llm"},"document":{"anyOf":[{"type":"string","maxLength":100000},{"type":"null"}],"title":"Document","description":"The context/document to analyze (required for 'llm' and 'hybrid' strategies)"},"inputs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Inputs","description":"Structured inputs for code/expr evaluation"},"code_expression":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Code Expression","description":"Expression returning scalar matched against choice_id. For 'hybrid', result is passed to LLM (not matched)."},"choices":{"items":{"$ref":"#/components/schemas/DecisionChoice"},"type":"array","maxItems":10,"minItems":2,"title":"Choices","description":"List of possible choices"},"default":{"anyOf":[{"$ref":"#/components/schemas/DecisionDefault"},{"type":"null"}],"description":"Default choice used when no match or on failure. Required."},"prompt":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Prompt","description":"Prompt/context for LLM/hybrid strategies (required for llm/hybrid)"},"decision_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Context","description":"Deprecated. Use 'prompt' instead."},"model":{"type":"string","title":"Model","description":"LLM model to use (only for 'llm' and 'hybrid' strategies)","default":"anthropic/claude-3.5-sonnet"}},"type":"object","required":["choices"],"title":"DecisionRequest","description":"Request for the Decision Tool.\n\nSES-161 v2 grammar:\n- strategy: llm | code | expr | hybrid (expr is alias of code)\n- choice_id canonical (id deprecated alias)\n- Top-level default block required\n- prompt field (decision_context deprecated alias)\n- Code/expr: scalar matching against choice_id\n\nExample (LLM strategy):\n    {\n        \"strategy\": \"llm\",\n        \"document\": \"Customer complaint about billing issue...\",\n        \"prompt\": \"Classify the input into one of the choices.\",\n        \"choices\": [\n            {\"choice_id\": \"billing\", \"label\": \"Billing\"},\n            {\"choice_id\": \"support\", \"label\": \"Support\"}\n        ],\n        \"default\": {\"choice_id\": \"other\", \"label\": \"Other\"}\n    }\n\nExample (Code strategy - scalar matching):\n    {\n        \"strategy\": \"code\",\n        \"inputs\": {\"risk\": 0.85},\n        \"code_expression\": \"'high_risk' if risk > 0.8 else 'low_risk'\",\n        \"choices\": [\n            {\"choice_id\": \"high_risk\", \"label\": \"High Risk\"},\n            {\"choice_id\": \"low_risk\", \"label\": \"Low Risk\"}\n        ],\n        \"default\": {\"choice_id\": \"unknown\", \"label\": \"Unknown\"}\n    }"},"DisputingEvidence":{"properties":{"source_id":{"type":"string","title":"Source Id","description":"ID of the source document"},"source_type":{"type":"string","title":"Source Type","description":"Type of source (file, chat_message, etc.)"},"quote":{"type":"string","title":"Quote","description":"Exact text from source that provides evidence"},"char_start":{"type":"integer","title":"Char Start","description":"Starting character position in source"},"char_end":{"type":"integer","title":"Char End","description":"Ending character position in source"},"relevance_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Relevance Score","description":"How relevant this evidence is (0.0-1.0)"},"context_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Before","description":"Text before the quote (for context)"},"context_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context After","description":"Text after the quote (for context)"},"discrepancy":{"type":"string","title":"Discrepancy","description":"Explanation of how this contradicts the extracted value"}},"type":"object","required":["source_id","source_type","quote","char_start","char_end","relevance_score","discrepancy"],"title":"DisputingEvidence","description":"Evidence that contradicts the extracted value."},"DocFormat":{"type":"string","enum":["json","markdown","md","html"],"title":"DocFormat","description":"Supported documentation formats."},"EnhanceTier":{"type":"string","enum":["off","floor","verify","full"],"title":"EnhanceTier","description":"How much enhancement to apply.\n\nDeliberately not a boolean — see module docstring.\n\nAttributes:\n    OFF: Passthrough. Identical to an unenhanced call.\n    FLOOR: Capture the one-shot answer and return it. Near-zero overhead.\n        Establishes the floor guarantee: never worse than unenhanced.\n    VERIFY: Floor plus evidence/structural checks. Ships the floor unless a\n        check fails.\n    FULL: The engaged loop — plan, execute against a verifier, retry."},"Evidence":{"properties":{"source_id":{"type":"string","title":"Source Id","description":"ID of the source document"},"source_type":{"type":"string","title":"Source Type","description":"Type of source (file, chat_message, etc.)"},"quote":{"type":"string","title":"Quote","description":"Exact text from source that provides evidence"},"char_start":{"type":"integer","title":"Char Start","description":"Starting character position in source"},"char_end":{"type":"integer","title":"Char End","description":"Ending character position in source"},"relevance_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Relevance Score","description":"How relevant this evidence is (0.0-1.0)"},"context_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Before","description":"Text before the quote (for context)"},"context_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context After","description":"Text after the quote (for context)"}},"type":"object","required":["source_id","source_type","quote","char_start","char_end","relevance_score"],"title":"Evidence","description":"A piece of evidence (citation) from a source document."},"ExecutorPolicy":{"properties":{"auto_execute_threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Auto Execute Threshold","description":"Minimum confidence to allow auto-execute. Below this, 'execute' is downgraded to 'confirm'. Default: 0.7","default":0.7},"reject_unsafe":{"type":"boolean","title":"Reject Unsafe","description":"When true (default), all unsafe intents are rejected.","default":true},"require_confirmation_for_review":{"type":"boolean","title":"Require Confirmation For Review","description":"When true (default), review-safety intents require confirmation.","default":true}},"type":"object","title":"ExecutorPolicy","description":"Policy overrides for the decision matrix."},"ExpressionRequest":{"properties":{"expression":{"type":"string","maxLength":1000,"title":"Expression","description":"Boolean expression to evaluate","examples":["score >= 80 and score < 95","x > 5 or y < 10","not is_expired"]},"variables":{"additionalProperties":true,"type":"object","title":"Variables","description":"Variables to use in the expression","examples":[{"score":85},{"is_expired":false,"x":7,"y":3}]}},"type":"object","required":["expression","variables"],"title":"ExpressionRequest","description":"Request model for expression evaluation."},"ExpressionResponse":{"properties":{"result":{"anyOf":[{"type":"boolean"},{"type":"integer"},{"type":"number"},{"type":"string"},{"type":"null"}],"title":"Result","description":"The evaluated result (typically bool for comparisons)"},"expression":{"type":"string","title":"Expression","description":"The original expression"},"variables_used":{"items":{"type":"string"},"type":"array","title":"Variables Used","description":"Variables that were referenced in the expression"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if evaluation failed"}},"type":"object","required":["result","expression"],"title":"ExpressionResponse","description":"Response model for expression evaluation."},"ExtractedIntent":{"properties":{"id":{"type":"string","title":"Id","description":"Stable unique identifier for this intent (int_<hex12>)"},"type":{"type":"string","title":"Type","description":"Intent type: action, question, reference, requirement, constraint, goal"},"actor":{"type":"string","title":"Actor","description":"Who should perform this intent.  'caller' when the actor is implicit (imperative sentences like 'Fix the bug').  Otherwise a specific role extracted from context (e.g. 'QA team', 'DevOps')."},"verb":{"type":"string","title":"Verb","description":"The core action verb in infinitive form (e.g. 'fix', 'add', 'test', 'answer')"},"target":{"type":"string","title":"Target","description":"The specific entity, artifact, or system component being acted upon (e.g. 'login page authentication bug', 'discount service', 'test coverage')."},"direction":{"type":"string","title":"Direction","description":"How the caller should handle this intent.  One of: 'do' (act on it), 'answer' (respond to a question), 'note' (acknowledge a fact/reference), 'enforce' (apply a constraint/requirement)."},"safety":{"type":"string","title":"Safety","description":"Safety classification for autonomous execution.  One of: 'safe' (normal business intent, no risk), 'review' (ambiguous or impactful — gate behind human approval), 'unsafe' (destructive, malicious, or dangerous — never auto-execute)."},"original_text":{"type":"string","title":"Original Text","description":"Exact substring from the source turn that this intent was derived from"},"enriched_text":{"type":"string","title":"Enriched Text","description":"Clearer restatement that resolves pronouns and makes the actor/verb/target explicit"},"reasoning":{"type":"string","title":"Reasoning","description":"Explanation of why this type was chosen and how the actor was identified"},"citation":{"type":"string","title":"Citation","description":"Exact quote from the input that justifies this intent"},"source_turn":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Turn","description":"Which conversation turn this intent was extracted from.  Format: 'user:1', 'assistant:2', or 'current' (the main text).  Present when conversation history is provided."},"context":{"anyOf":[{"$ref":"#/components/schemas/IntentContext"},{"type":"null"}],"description":"Optional inferred context (priority, complexity, timing, deadline, tags)"},"quality_score":{"anyOf":[{"$ref":"#/components/schemas/IntentQualityScore"},{"type":"null"}],"description":"Rubric-based quality score (present only when include_quality_score=true)"},"audit_result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Audit Result","description":"SLAF audit result (present only when mode='robust').  Contains classification (supported/disputed/unclear/not_found), supporting_evidence, and notes."}},"additionalProperties":true,"type":"object","required":["id","type","actor","verb","target","direction","safety","original_text","enriched_text","reasoning","citation"],"title":"ExtractedIntent","description":"A single structured intent extracted from the input text."},"FieldAuditResult":{"properties":{"classification":{"type":"string","enum":["supported","disputed","unclear","not_found"],"title":"Classification","description":"Classification based on evidence found"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Confidence score (0.0-1.0)"},"supporting_evidence":{"items":{"$ref":"#/components/schemas/Evidence"},"type":"array","title":"Supporting Evidence","description":"Evidence supporting the extracted value"},"disputing_evidence":{"items":{"$ref":"#/components/schemas/DisputingEvidence"},"type":"array","title":"Disputing Evidence","description":"Evidence contradicting the extracted value"},"notes":{"type":"string","title":"Notes","description":"Human-readable explanation of classification","default":""},"completeness":{"anyOf":[{"type":"string","enum":["complete","missing","partial"]},{"type":"null"}],"title":"Completeness","description":"Completeness status (if completeness_check enabled)"}},"type":"object","required":["classification","confidence"],"title":"FieldAuditResult","description":"Audit result for a single field."},"FieldCitations":{"properties":{"classification":{"type":"string","enum":["supported","disputed","unclear","not_found"],"title":"Classification"},"total_citations":{"type":"integer","title":"Total Citations"},"supporting":{"items":{"$ref":"#/components/schemas/Evidence"},"type":"array","title":"Supporting"},"disputing":{"items":{"$ref":"#/components/schemas/DisputingEvidence"},"type":"array","title":"Disputing"}},"type":"object","required":["classification","total_citations"],"title":"FieldCitations","description":"All citations for a single field."},"FileListResponse":{"properties":{"files":{"items":{"$ref":"#/components/schemas/FileResponse"},"type":"array","title":"Files"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["files","total","limit","offset"],"title":"FileListResponse","description":"Response for file listing."},"FileProcessRequest":{"properties":{"rag_chunking":{"type":"boolean","title":"Rag Chunking","description":"Enable RAG chunking, embedding generation, and Elasticsearch indexing","default":true},"extract_metadata":{"type":"boolean","title":"Extract Metadata","description":"Extract metadata (title, author, dates, etc.) from file","default":true},"chunk_size":{"type":"integer","maximum":2048.0,"minimum":128.0,"title":"Chunk Size","description":"Chunk size for RAG processing (characters)","default":512},"overlap":{"type":"integer","maximum":512.0,"minimum":0.0,"title":"Overlap","description":"Overlap between chunks (characters)","default":50},"embedding_model":{"type":"string","title":"Embedding Model","description":"Embedding model for RAG","default":"text-embedding-3-small"}},"type":"object","title":"FileProcessRequest","description":"Request to process a file (SES-105).\n\nProcessing includes RAG chunking (with automatic Elasticsearch indexing)\nand metadata extraction. All processing is synchronous and idempotent.\n\nNote: Elasticsearch indexing happens automatically when rag_chunking=True.\nThe index_file() function handles both PostgreSQL (embeddings) and\nElasticsearch (BM25 text search) simultaneously.\n\nExample:\n    ```json\n    {\n        \"rag_chunking\": true,\n        \"extract_metadata\": true,\n        \"chunk_size\": 512,\n        \"overlap\": 50\n    }\n    ```"},"FileProcessResponse":{"properties":{"file_id":{"type":"string","title":"File Id","description":"File ID that was processed"},"status":{"type":"string","enum":["completed","indexed","success","skipped","failed","partial_failure"],"title":"Status","description":"Processing status - see docstring for values"},"chunks_created":{"type":"integer","title":"Chunks Created","description":"Number of chunks created for RAG","default":0},"elasticsearch_indexed":{"type":"boolean","title":"Elasticsearch Indexed","description":"Whether file was indexed in Elasticsearch","default":false},"metadata_extracted":{"type":"boolean","title":"Metadata Extracted","description":"Whether metadata was extracted","default":false},"processing_time_seconds":{"type":"number","title":"Processing Time Seconds","description":"Processing duration in seconds","default":0.0},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Additional status message or warnings"}},"type":"object","required":["file_id","status"],"title":"FileProcessResponse","description":"Response from file processing operation.\n\nIndicates processing status and statistics.\n\nStatus values (SES-199):\n- completed: All requested operations succeeded\n- indexed: Alias for completed (legacy)\n- success: Alias for completed (legacy)\n- skipped: File was empty or nothing was requested\n- failed: Critical failure (e.g., RAG was requested but failed completely)\n- partial_failure: Some operations failed (e.g., RAG failed but metadata succeeded)"},"FileResponse":{"properties":{"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"filename":{"type":"string","title":"Filename"},"path":{"type":"string","title":"Path"},"full_path":{"type":"string","title":"Full Path"},"file_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Type"},"mime_type":{"type":"string","title":"Mime Type"},"size_bytes":{"type":"integer","title":"Size Bytes"},"storage_path":{"type":"string","title":"Storage Path"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"created_by_job":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By Job"},"updated_by_job":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated By Job"},"job_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Type"},"run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"processing_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Processing Status"},"processing_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Processing Message"},"processing_time_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Processing Time Seconds"},"chunks_created":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chunks Created"},"elasticsearch_indexed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Elasticsearch Indexed"},"metadata_extracted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Metadata Extracted"}},"type":"object","required":["id","project_id","filename","path","full_path","file_type","mime_type","size_bytes","storage_path","created_at","updated_at"],"title":"FileResponse","description":"File metadata response."},"GenerateAgentSchemaRequest":{"properties":{"description":{"type":"string","minLength":10,"title":"Description","description":"Natural language description of the agent workflow"},"hints":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Hints","description":"Optional hints: inputs, tools, step_types, goal"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id","description":"ID of ongoing conversation for iterative refinement"},"feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback","description":"Feedback on previous generation for refinement"}},"type":"object","required":["description"],"title":"GenerateAgentSchemaRequest","description":"Request to generate an agent YAML definition from a natural language description.\n\nUses LLM with SLAF retry loop to produce a valid AgentYAML definition.\nSupports iterative refinement via conversation_id.","example":{"description":"An agent that searches the web for a topic, extracts key facts from the results, and produces a summary report with citations.","hints":{"goal":"Research and summarize a topic with citations","inputs":["topic","max_sources"],"tools":["web-search"]}}},"GenerateAgentSchemaResponse":{"properties":{"yaml_definition":{"type":"string","title":"Yaml Definition","description":"Generated agent YAML definition as a string"},"parsed_definition":{"additionalProperties":true,"type":"object","title":"Parsed Definition","description":"Parsed agent definition as a dict"},"validation":{"$ref":"#/components/schemas/AgentValidateResponse","description":"Validation result of the generated definition"},"conversation_id":{"type":"string","title":"Conversation Id","description":"Conversation ID for iterative refinement"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions","description":"Suggestions for improvement"},"model_used":{"type":"string","title":"Model Used","description":"LLM model used for generation"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total tokens consumed"},"generation_time_ms":{"type":"integer","title":"Generation Time Ms","description":"Generation time in milliseconds"}},"type":"object","required":["yaml_definition","parsed_definition","validation","conversation_id","model_used","tokens_used","generation_time_ms"],"title":"GenerateAgentSchemaResponse","description":"Response from agent generation endpoint."},"GenerateBlockSchemaRequest":{"properties":{"description":{"type":"string","minLength":10,"title":"Description","description":"Natural language description of what data to extract"},"hints":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Hints","description":"Optional hints: fields, source_type, output_format"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id","description":"ID of ongoing conversation for iterative refinement"},"feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback","description":"Feedback on previous generation for refinement"}},"type":"object","required":["description"],"title":"GenerateBlockSchemaRequest","description":"Request to generate a structured block pipeline config from a description.\n\nUses LLM with SLAF retry loop to produce a valid JSON schema and extraction\ninstructions for the structured blocks pipeline.","example":{"description":"Extract patient demographics, diagnosis codes, and medication list from clinical notes.","hints":{"fields":["patient_name","dob","diagnosis_codes","medications"],"output_format":"json","source_type":"clinical_notes"}}},"GenerateBlockSchemaResponse":{"properties":{"json_schema":{"additionalProperties":true,"type":"object","title":"Json Schema","description":"Generated JSON schema for structured data extraction"},"extraction_instructions":{"type":"string","title":"Extraction Instructions","description":"Natural language instructions for the extraction LLM"},"example_output":{"additionalProperties":true,"type":"object","title":"Example Output","description":"Example output conforming to the schema"},"conversation_id":{"type":"string","title":"Conversation Id","description":"Conversation ID for iterative refinement"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions","description":"Suggestions for improvement"},"model_used":{"type":"string","title":"Model Used","description":"LLM model used for generation"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total tokens consumed"},"generation_time_ms":{"type":"integer","title":"Generation Time Ms","description":"Generation time in milliseconds"}},"type":"object","required":["json_schema","extraction_instructions","example_output","conversation_id","model_used","tokens_used","generation_time_ms"],"title":"GenerateBlockSchemaResponse","description":"Response from block schema generation endpoint."},"GenerateDecisionBlockRequest":{"properties":{"description":{"type":"string","minLength":10,"title":"Description","description":"Natural language description of the decision (e.g., 'Route customer tickets by category')"},"hints":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Hints","description":"Optional hints: strategy, num_choices, input_fields"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id","description":"ID of ongoing conversation for iterative refinement"},"feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback","description":"Feedback on previous generation for refinement"}},"type":"object","required":["description"],"title":"GenerateDecisionBlockRequest","description":"Request to generate a decision block configuration from natural language."},"GenerateDecisionBlockResponse":{"properties":{"decision_block":{"additionalProperties":true,"type":"object","title":"Decision Block","description":"Generated decision block configuration (valid DecisionRequest format)"},"conversation_id":{"type":"string","title":"Conversation Id","description":"Conversation ID for iterative refinement"},"valid":{"type":"boolean","title":"Valid","description":"Whether the generated block passed validation"},"validation_errors":{"items":{"type":"string"},"type":"array","title":"Validation Errors","description":"Validation errors (if any)"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions","description":"Suggestions for improvement"},"model_used":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Used","description":"LLM model used"},"tokens_used":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tokens Used","description":"Tokens consumed"},"generation_time_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Generation Time Ms","description":"Generation time in ms"}},"type":"object","required":["decision_block","conversation_id","valid"],"title":"GenerateDecisionBlockResponse","description":"Response from decision block generation endpoint."},"GenerateMetadata":{"properties":{"model_used":{"type":"string","title":"Model Used","description":"LLM model used"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total tokens used"},"execution_time_ms":{"type":"integer","title":"Execution Time Ms","description":"Execution time in milliseconds"},"retry_count":{"type":"integer","title":"Retry Count","description":"Number of retries performed","default":0}},"type":"object","required":["model_used","tokens_used","execution_time_ms"],"title":"GenerateMetadata","description":"Metadata about data generation."},"GenerateRequest":{"properties":{"data":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}],"title":"Data","description":"Unstructured input text or list of text documents"},"json_schema":{"additionalProperties":true,"type":"object","title":"Json Schema","description":"JSON schema that the output must conform to"},"temperature":{"type":"number","maximum":2.0,"minimum":0.0,"title":"Temperature","description":"LLM temperature (0.0 = deterministic, 2.0 = creative)","default":0.3},"max_retries":{"type":"integer","maximum":5.0,"minimum":0.0,"title":"Max Retries","description":"Maximum validation retry attempts","default":3},"include_reasoning":{"type":"boolean","title":"Include Reasoning","description":"Include LLM reasoning in response","default":false}},"additionalProperties":false,"type":"object","required":["data","json_schema"],"title":"GenerateRequest","description":"Request model for generating schema-compliant structured data.\n\nExtracts or generates structured data from unstructured input text\naccording to the provided JSON schema."},"GenerateResponse":{"properties":{"output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Output","description":"Generated structured data (if successful)"},"success":{"type":"boolean","title":"Success","description":"Whether generation succeeded"},"validation_passed":{"type":"boolean","title":"Validation Passed","description":"Whether output passed schema validation"},"messages":{"items":{"type":"string"},"type":"array","title":"Messages","description":"Status messages"},"validation_errors":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Validation Errors","description":"Schema validation errors (if validation failed)"},"attempts":{"type":"integer","title":"Attempts","description":"Number of generation attempts made"},"reasoning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reasoning","description":"LLM reasoning (if include_reasoning=True)"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/GenerateMetadata"},{"type":"null"}],"description":"Metadata about generation"}},"type":"object","required":["success","validation_passed","attempts"],"title":"GenerateResponse","description":"Response model for structured data generation."},"GenerateSOPSchemaRequest":{"properties":{"description":{"type":"string","minLength":10,"title":"Description","description":"Natural language description of the workflow"},"hints":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Hints","description":"Optional hints for generation (inputs, tools, output_format)"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id","description":"ID of ongoing conversation for iterative refinement"},"feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback","description":"Feedback on previous generation for refinement"},"slaf_strategy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slaf Strategy","description":"SLAF retry strategy. 'compact' (default): on retry, send only the original prompt + latest error summary (constant context size). 'full-context': preserve full conversation history across retries (higher quality but slower and more expensive)."}},"type":"object","required":["description"],"title":"GenerateSOPSchemaRequest","description":"Request to generate an SOP definition from description.","example":{"description":"A workflow that searches the web for a topic, extracts key facts, and creates a summary report.","hints":{"inputs":["topic","max_results"],"output_format":"markdown","tools":["web-search"]}}},"GenerateSOPSchemaResponse":{"properties":{"definition":{"additionalProperties":true,"type":"object","title":"Definition","description":"Generated SOP definition"},"conversation_id":{"type":"string","title":"Conversation Id","description":"Conversation ID for iterative refinement"},"validation":{"$ref":"#/components/schemas/SOPValidationResult","description":"Validation result of generated definition"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions","description":"Suggestions for improvement"},"model_used":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Used","description":"LLM model used for generation"},"tokens_used":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tokens Used","description":"Tokens consumed"},"generation_time_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Generation Time Ms","description":"Time to generate (ms)"}},"type":"object","required":["definition","conversation_id","validation"],"title":"GenerateSOPSchemaResponse","description":"Response from SOP generation endpoint.","example":{"conversation_id":"conv_abc123","definition":{"blocks":[],"required_fields":{"properties":{"topic":{"type":"string"}},"required":["topic"],"type":"object"},"sop_version":"2.0"},"generation_time_ms":3200,"model_used":"openai/gpt-5.2","suggestions":["Consider adding error handling for web search failures","You could add a 'when' condition to skip summarization if no results"],"tokens_used":1250,"validation":{"block_count":3,"errors":[],"execution_mode":"dag","is_valid":true,"sop_version":"2.0","warnings":[]}}},"GenerateSchemaRequest":{"properties":{"data":{"anyOf":[{"type":"string"},{"additionalProperties":true,"type":"object"},{"items":{"additionalProperties":true,"type":"object"},"type":"array"}],"title":"Data","description":"Example data to analyze (JSON object, array, or annotated JSON string)"},"additional_instructions":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Additional Instructions","description":"Additional instructions for schema generation (e.g., 'make age required')"}},"additionalProperties":false,"type":"object","required":["data"],"title":"GenerateSchemaRequest","description":"Request model for generating a JSON schema from example data.\n\nThe data can be:\n- A dict/list with example values\n- A JSON string with inline comments describing constraints\n- Multiple example objects to infer patterns"},"GenerateSchemaResponse":{"properties":{"json_schema":{"additionalProperties":true,"type":"object","title":"Json Schema","description":"Generated JSON Schema (draft 2020-12)"},"success":{"type":"boolean","title":"Success","description":"Whether schema generation succeeded"},"messages":{"items":{"type":"string"},"type":"array","title":"Messages","description":"Status messages or warnings"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/SchemaMetadata"},{"type":"null"}],"description":"Metadata about schema generation"}},"type":"object","required":["json_schema","success"],"title":"GenerateSchemaResponse","description":"Response model for schema generation."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IntentAlignRequest":{"properties":{"intents":{"items":{"$ref":"#/components/schemas/ExtractedIntent"},"type":"array","minItems":1,"title":"Intents","description":"List of extracted intents to evaluate against goals."},"goals":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Goals","description":"List of goal strings to align intents against.","examples":[["Improve user onboarding","Reduce support tickets by 30%"]]},"policies":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Policies","description":"Optional list of policy strings to check for violations (e.g. 'No deployments on Fridays', 'All changes require PR review')."},"project_context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Project Context","description":"Optional project metadata providing additional context (project name, phase, team, etc.)."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model","description":"Override the default LLM model for this request."}},"type":"object","required":["intents","goals"],"title":"IntentAlignRequest","description":"Request body for intent alignment.\n\nProvide extracted intents and goals to evaluate alignment.\nOptionally supply policies and project context."},"IntentAlignResponse":{"properties":{"alignments":{"items":{"$ref":"#/components/schemas/IntentAlignment"},"type":"array","title":"Alignments","description":"Per-intent alignment assessments"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total LLM tokens consumed for alignment"},"model_used":{"type":"string","title":"Model Used","description":"LLM model that performed the alignment assessment"}},"type":"object","required":["alignments","tokens_used","model_used"],"title":"IntentAlignResponse","description":"Response from the intent alignment endpoint."},"IntentAlignment":{"properties":{"intent_id":{"type":"string","title":"Intent Id","description":"ID of the intent being assessed"},"alignment":{"type":"string","title":"Alignment","description":"Alignment classification: 'aligned' (supports goals), 'unaligned' (conflicts with goals), 'unclear' (ambiguous), 'not_applicable' (neutral — neither supports nor conflicts)."},"aligned_goals":{"items":{"type":"string"},"type":"array","title":"Aligned Goals","description":"Goal strings this intent supports"},"conflicting_goals":{"items":{"type":"string"},"type":"array","title":"Conflicting Goals","description":"Goal strings this intent conflicts with"},"violated_policies":{"items":{"type":"string"},"type":"array","title":"Violated Policies","description":"Policy strings this intent violates"},"reasoning":{"type":"string","title":"Reasoning","description":"Brief explanation of the alignment assessment"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Confidence in the assessment (0.0-1.0)"}},"additionalProperties":true,"type":"object","required":["intent_id","alignment","reasoning","confidence"],"title":"IntentAlignment","description":"Alignment assessment for a single intent."},"IntentContext":{"properties":{"priority":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Priority","description":"Inferred priority: high, medium, low"},"complexity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Complexity","description":"Inferred complexity: high, medium, low"},"timing":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timing","description":"Temporal reference (e.g. 'immediately', 'next sprint')"},"deadline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deadline","description":"Specific deadline if mentioned (e.g. 'Friday', '2025-03-01')"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"Topical tags inferred from intent content"}},"type":"object","title":"IntentContext","description":"Contextual metadata inferred from the intent text."},"IntentDecideRequest":{"properties":{"intents":{"items":{"$ref":"#/components/schemas/IntentForDecision"},"type":"array","minItems":1,"title":"Intents","description":"List of intents with safety, alignment, and confidence fields."},"executor_policy":{"anyOf":[{"$ref":"#/components/schemas/ExecutorPolicy"},{"type":"null"}],"description":"Optional policy overrides for the decision matrix."},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model","description":"Accepted for API consistency but unused (no LLM call)."}},"type":"object","required":["intents"],"title":"IntentDecideRequest","description":"Request body for intent decision.\n\nProvide intents with safety and alignment fields.\nOptionally supply an executor policy to override defaults."},"IntentDecideResponse":{"properties":{"decisions":{"items":{"$ref":"#/components/schemas/IntentDecision"},"type":"array","title":"Decisions","description":"Per-intent action decisions"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Always 0 — decision is deterministic, no LLM call"},"model_used":{"type":"string","title":"Model Used","description":"Always 'none (deterministic)' — no LLM call"}},"type":"object","required":["decisions","tokens_used","model_used"],"title":"IntentDecideResponse","description":"Response from the intent decision endpoint."},"IntentDecision":{"properties":{"intent_id":{"type":"string","title":"Intent Id","description":"ID of the intent"},"action":{"type":"string","title":"Action","description":"Decided action: 'execute', 'confirm', 'ask_why', 'confirm_and_ask_why', or 'reject'"},"reasoning":{"type":"string","title":"Reasoning","description":"Human-readable explanation of the decision"},"clarification_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Clarification Prompt","description":"Prompt to present to the user for non-execute actions"}},"type":"object","required":["intent_id","action","reasoning"],"title":"IntentDecision","description":"Decision for a single intent."},"IntentExtractRequest":{"properties":{"text":{"type":"string","maxLength":50000,"minLength":1,"title":"Text","description":"Natural-language text to extract intents from.  Can be a single sentence or a multi-paragraph document.","examples":["Fix the login bug and update the docs by Friday"]},"messages":{"anyOf":[{"items":{"$ref":"#/components/schemas/IntentMessage"},"type":"array"},{"type":"null"}],"title":"Messages","description":"Optional prior conversation messages.  Supply these so the model can resolve pronouns (e.g. 'Fix it' → 'Fix the login bug')."},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Free-form metadata providing additional context (project name, sprint, team, etc.).  Keys and values are passed to the LLM as context."},"mode":{"type":"string","title":"Mode","description":"Extraction quality mode.  'quick' performs a single LLM extraction pass.  'robust' adds a SLAF verification pass that classifies each intent as supported/disputed/unclear/not_found.","default":"quick","examples":["quick","robust"]},"include_quality_score":{"type":"boolean","title":"Include Quality Score","description":"When true, runs an additional LLM pass to score each intent against a 5-dimension rubric (citation_support, specificity, completeness, type_correctness, unambiguity).  Each dimension is scored 0.0-1.0 and an overall_score is computed as the mean.","default":false},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model","description":"Override the default LLM model for this request.  If omitted, uses the server default (e.g. anthropic/claude-sonnet-4.5)."}},"type":"object","required":["text"],"title":"IntentExtractRequest","description":"Request body for intent extraction.\n\nProvide the `text` to decompose into intents.  Optionally supply\n`messages` (conversation history) so the LLM can resolve pronouns\nlike *\"Fix it\"* back to a specific subject mentioned earlier."},"IntentExtractResponse":{"properties":{"schema_version":{"type":"string","title":"Schema Version","description":"Schema version of this response (e.g. '1.0').  Use GET /api/v2/tools/intents/schema to discover supported versions."},"message_type":{"type":"string","title":"Message Type","description":"'single' if all intents share one type, 'mixed' if multiple types present"},"intents":{"items":{"$ref":"#/components/schemas/ExtractedIntent"},"type":"array","title":"Intents","description":"List of structured intents extracted from the input text"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total LLM tokens consumed across all passes (extract + audit + score)"},"model_used":{"type":"string","title":"Model Used","description":"LLM model that performed the extraction"},"mode":{"type":"string","title":"Mode","description":"Quality mode that was applied: 'quick' or 'robust'"},"quality_scoring_applied":{"type":"boolean","title":"Quality Scoring Applied","description":"Whether rubric-based quality scoring was applied"}},"type":"object","required":["schema_version","message_type","intents","tokens_used","model_used","mode","quality_scoring_applied"],"title":"IntentExtractResponse","description":"Response from the intent extraction endpoint."},"IntentForDecision":{"properties":{"intent_id":{"type":"string","title":"Intent Id","description":"ID of the intent"},"safety":{"type":"string","title":"Safety","description":"Safety classification: 'safe', 'review', or 'unsafe'"},"alignment":{"type":"string","title":"Alignment","description":"Alignment classification: 'aligned', 'unaligned', 'unclear', or 'not_applicable'"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Confidence in the alignment assessment (0.0-1.0)"},"enriched_text":{"type":"string","title":"Enriched Text","description":"Enriched text of the intent (used in clarification prompts)","default":""}},"type":"object","required":["intent_id","safety","alignment","confidence"],"title":"IntentForDecision","description":"Minimal intent fields needed for the decision matrix."},"IntentMessage":{"properties":{"role":{"type":"string","title":"Role","description":"The role of the message author. One of 'user', 'assistant', or 'system'.","examples":["user","assistant"]},"content":{"type":"string","title":"Content","description":"The text content of the message.","examples":["The login page is broken"]}},"type":"object","required":["role","content"],"title":"IntentMessage","description":"A single message in a conversation history, used to resolve pronouns and context."},"IntentQualityScore":{"properties":{"overall_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Overall Score","description":"Mean of all dimension scores, normalised 0.0-1.0"},"dimensions":{"additionalProperties":{"$ref":"#/components/schemas/QualityDimension"},"type":"object","title":"Dimensions","description":"Per-dimension scores.  Keys: citation_support, specificity, completeness, type_correctness, unambiguity"}},"type":"object","required":["overall_score","dimensions"],"title":"IntentQualityScore","description":"Rubric-based quality assessment for a single intent."},"IntentSchemaResponse":{"properties":{"current_version":{"type":"string","title":"Current Version","description":"The current (default) schema version"},"supported_versions":{"items":{"type":"string"},"type":"array","title":"Supported Versions","description":"All supported schema version identifiers"},"versions":{"additionalProperties":{"$ref":"#/components/schemas/IntentSchemaVersion"},"type":"object","title":"Versions","description":"Detailed info for each supported version"},"json_schema":{"additionalProperties":true,"type":"object","title":"Json Schema","description":"JSON Schema for the current version's response object (IntentExtractResponse)"}},"type":"object","required":["current_version","supported_versions","versions","json_schema"],"title":"IntentSchemaResponse","description":"Response from the intent schema discovery endpoint."},"IntentSchemaVersion":{"properties":{"version":{"type":"string","title":"Version","description":"Schema version identifier (e.g. '1.0')"},"status":{"type":"string","title":"Status","description":"Version status: 'current' (active), 'deprecated', or 'planned'"},"description":{"type":"string","title":"Description","description":"Human-readable description of this schema version"},"intent_types":{"items":{"type":"string"},"type":"array","title":"Intent Types","description":"Intent types supported in this version"},"quality_dimensions":{"items":{"type":"string"},"type":"array","title":"Quality Dimensions","description":"Quality rubric dimensions in this version"},"modes":{"items":{"type":"string"},"type":"array","title":"Modes","description":"Extraction modes available in this version"}},"type":"object","required":["version","status","description","intent_types","quality_dimensions","modes"],"title":"IntentSchemaVersion","description":"Description of a single schema version."},"JobCreate":{"properties":{"project_id":{"type":"string","title":"Project Id","description":"Project that owns the job and its outputs"},"job_type":{"type":"string","enum":["http_call","agent_execution"],"title":"Job Type","description":"Type of background job. Use 'http_call' for generic async execution of any API endpoint. Use 'agent_execution' only for agent runs with scheduling/nested jobs."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Optional human-readable job name for easier identification"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","description":"Optional idempotency key to avoid creating duplicate jobs."},"run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id","description":"Required for agent_execution jobs. Optional for http_call."},"priority":{"type":"integer","maximum":10.0,"minimum":-10.0,"title":"Priority","description":"Relative queue priority (-10 lowest, 0 normal, 10 highest).","default":0},"parameters":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Parameters","description":"Job parameters. For http_call: must include 'method' and 'endpoint', optional 'body' and 'headers'. For agent_execution: agent-specific parameters."},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url","description":"Optional URL on **your** server to receive a completion notification (SES-259). When this job reaches `completed` or `failed`, Taiso POSTs a `JobWebhookPayload` JSON body to this URL so you can stop polling. Omit to use polling only (`GET /jobs/{job_id}`). In-flight progress is not pushed — poll while status is `queued` or `running`. See `GET /jobs/webhook-callback-spec` and schema `JobWebhookPayload` in OpenAPI. HTTPS required in production; HTTP allowed in dev/test."}},"type":"object","required":["project_id","job_type"],"title":"JobCreate","description":"Request model for creating a job.\n\nSES-105: Only two job types are supported:\n- http_call: Generic wrapper for any API endpoint (async execution)\n- agent_execution: Special case for agent runs (scheduling, nested jobs, conversation state)\n\nRemoved job types (use http_call instead):\n- file_processing → use http_call to POST /files/{id}/process\n- rag_indexing → use http_call to POST /files/{id}/process\n- export → use http_call to POST /projects/{id}/export","examples":[{"job_type":"http_call","name":"Process file async","parameters":{"body":{"chunk_size":512,"rag_chunking":true},"endpoint":"/api/v2/files/file-123/process","method":"POST"},"priority":0,"project_id":"prj-1234567890abcdef","webhook_url":"https://example.com/webhooks/job-status"},{"job_type":"agent_execution","name":"Execute agent run","parameters":{},"priority":5,"project_id":"prj-1234567890abcdef","run_id":"run-abc123"}]},"JobListResponse":{"properties":{"jobs":{"items":{"$ref":"#/components/schemas/JobResponse"},"type":"array","title":"Jobs"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["jobs","total","limit","offset"],"title":"JobListResponse","description":"Response model for job list.","example":{"jobs":[{"created_at":"2025-11-27T10:17:00Z","id":"job-01JDX2Q6KLMNO...","idempotency_key":"index-files-2025-11-27","job_type":"rag_indexing","parameters":{"file_ids":["fil-111","fil-222","fil-333"]},"priority":0,"project_id":"prj-1234567890abcdef","status":"pending","updated_at":"2025-11-27T10:17:00Z","user_id":"usr-01HXYZ..."}],"limit":100,"offset":0,"total":1}},"JobResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"project_id":{"type":"string","title":"Project Id"},"run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key"},"job_type":{"type":"string","title":"Job Type"},"status":{"type":"string","title":"Status"},"priority":{"type":"integer","title":"Priority"},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"error_traceback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Traceback"},"retryable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Retryable"},"output_file_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Output File Id"},"parameters":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Parameters"},"results":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Results"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url","description":"Echo of the callback URL supplied at job creation. When set, Taiso POSTs a `JobWebhookPayload` to this URL on `completed` or `failed`."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["id","user_id","project_id","job_type","status","priority","created_at","updated_at"],"title":"JobResponse","description":"Response model for a job.","example":{"created_at":"2025-11-27T10:17:00Z","id":"job-01JDX2Q6KLMNO...","idempotency_key":"index-files-2025-11-27","job_type":"rag_indexing","parameters":{"file_ids":["fil-111","fil-222","fil-333"]},"priority":0,"project_id":"prj-1234567890abcdef","status":"pending","updated_at":"2025-11-27T10:17:00Z","user_id":"usr-01HXYZ..."}},"JobWebhookPayload":{"properties":{"event":{"type":"string","enum":["job.completed","job.failed"],"title":"Event","description":"Event type matching terminal status."},"job_id":{"type":"string","title":"Job Id","description":"Job identifier — use to fetch full job state and results."},"status":{"type":"string","enum":["completed","failed"],"title":"Status","description":"Terminal job status that triggered this notification."},"job_type":{"type":"string","title":"Job Type","description":"Job type at completion time (e.g. `http_call`, `agent_execution`)."},"project_id":{"type":"string","title":"Project Id","description":"Project that owns the job."},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At","description":"ISO 8601 timestamp when the job reached this terminal state."},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code","description":"Machine-readable error code when `status` is `failed`; null on success."},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Human-readable error summary when failed; null on success."},"result_preview":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Result Preview","description":"Small job `results` inline when serialized JSON is under 4KB; otherwise null. Always fetch full output via Jobs API when null or when you need files/binary."}},"type":"object","required":["event","job_id","status","job_type","project_id"],"title":"JobWebhookPayload","description":"JSON body POSTed to your `webhook_url` when a job finishes (outbound callback, SES-259).\n\nThis is **not** a request you send to Taiso — it is the payload **your server receives**\nwhen background job execution reaches a terminal state.\n\n**Why webhooks exist:** Avoid a poll loop waiting for `completed`/`failed`. Set `webhook_url`\non `POST /jobs`; keep using `GET /jobs/{job_id}` for in-progress status or to fetch full\nresults after the callback.\n\n**When fired:** Exactly once per terminal transition — `completed` or `failed` only.\nNot sent for `queued`, `running`, or `cancelled`.\n\n**Delivery:** HTTP POST, `Content-Type: application/json`, 10s timeout, up to 3 retries\n(30s, 120s, 300s backoff). Respond with any 2xx quickly. At-least-once delivery — dedupe\non `(job_id, status)`. Failed delivery does not change job status.\n\n**Full results:** If `result_preview` is null (payload too large or stored as files),\ncall `GET /api/v2/jobs/{job_id}` or `GET /api/v2/jobs/{job_id}/result` with your API key.","examples":[{"completed_at":"2026-06-14T12:00:00Z","event":"job.completed","job_id":"job_2w3e4r5t6y","job_type":"http_call","project_id":"prj_1a2b3c4d5e6f","result_preview":{"chunks_created":42,"status":"success"},"status":"completed"},{"completed_at":"2026-06-14T12:05:00Z","error_code":"TIMEOUT","error_message":"Job did not complete within allowed time","event":"job.failed","job_id":"job_9x8y7z6w5v","job_type":"http_call","project_id":"prj_1a2b3c4d5e6f","status":"failed"}]},"LinkInfo":{"properties":{"href":{"type":"string","title":"Href"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"}},"type":"object","required":["href"],"title":"LinkInfo","description":"Information about a link."},"LodestarRequest":{"properties":{"enhance":{"$ref":"#/components/schemas/EnhanceTier","default":"off"},"budget_tokens":{"type":"integer","minimum":0.0,"title":"Budget Tokens","default":20000},"sources":{"anyOf":[{"items":{"$ref":"#/components/schemas/Source"},"type":"array"},{"type":"null"}],"title":"Sources"},"policy":{"anyOf":[{"$ref":"#/components/schemas/ToolPolicy"},{"type":"null"}]},"return_audit":{"type":"boolean","title":"Return Audit","default":true}},"type":"object","title":"LodestarRequest","description":"The opt-in ``lodestar`` block on an inference request.\n\nArgs:\n    enhance: Which tier to apply. Defaults to ``off`` so that merely\n        mentioning the block changes nothing.\n    budget_tokens: Hard ceiling on total tokens across the enhancement\n        attempt. On exhaustion the best verified artifact so far ships\n        (never below the floor) and ``verdict`` says ``budget_exhausted``.\n    sources: Documents the answer must be checkable against. The\n        highest-value field — grounded checking is what separates this from\n        naive self-arbitration, which measurably makes answers worse.\n    policy: Tool-call gating.\n    return_audit: When False, the ``lodestar`` response block is reduced to\n        metering only (``usage_delta``, ``verdict``). Claim text can restate\n        source material, so callers who route responses somewhere less\n        trusted can suppress it.","example":{"budget_tokens":20000,"enhance":"verify","policy":{"deny_tools":["bash"],"on_violation":"block"},"return_audit":true,"sources":[{"id":"policy-v3","text":"Refunds within 30 days."}]}},"LoremIpsumRequest":{"properties":{"num_chars":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Num Chars"},"start_spot":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Spot"},"start_with_capital_letter":{"type":"boolean","title":"Start With Capital Letter","default":true}},"type":"object","title":"LoremIpsumRequest","description":"Request model for lorem ipsum generator."},"LoremIpsumResponse":{"properties":{"text":{"type":"string","title":"Text"},"length":{"type":"integer","title":"Length"}},"type":"object","required":["text","length"],"title":"LoremIpsumResponse","description":"Response model for lorem ipsum generator."},"Message":{"properties":{"role":{"type":"string","enum":["system","user","assistant","tool"],"title":"Role"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"tool_calls":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Tool Calls"},"tool_call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tool Call Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["role"],"title":"Message","description":"Chat message in OpenAI format."},"MissingField":{"properties":{"field":{"type":"string","title":"Field"},"description":{"type":"string","title":"Description"},"type":{"type":"string","title":"Type"},"required":{"type":"boolean","title":"Required"},"searched_in":{"items":{"type":"string"},"type":"array","title":"Searched In"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions"}},"type":"object","required":["field","description","type","required","searched_in","suggestions"],"title":"MissingField","description":"Information about a missing required field."},"OpenAPIInfo":{"properties":{"version":{"type":"string","title":"Version","description":"API version"},"endpoints":{"type":"integer","title":"Endpoints","description":"Number of endpoints"},"hash":{"type":"string","title":"Hash","description":"SHA-256 hash of spec"}},"type":"object","required":["version","endpoints","hash"],"title":"OpenAPIInfo","description":"Information about OpenAPI spec."},"PDFExtractDigitalRequest":{"properties":{"pdf_data":{"type":"string","title":"Pdf Data","description":"Base64-encoded PDF file data"}},"type":"object","required":["pdf_data"],"title":"PDFExtractDigitalRequest","description":"Request model for digital PDF text extraction."},"PDFExtractDigitalResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"pdf_type":{"type":"string","title":"Pdf Type","default":"digital"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Extraction confidence (0.0-1.0)"},"text":{"type":"string","title":"Text","description":"Extracted plain text from all pages"},"pages":{"type":"integer","title":"Pages","description":"Number of pages processed"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"PDF metadata"}},"type":"object","required":["success","confidence","text","pages"],"title":"PDFExtractDigitalResponse","description":"Response model for digital PDF extraction."},"PDFExtractOCRRequest":{"properties":{"pdf_data":{"type":"string","title":"Pdf Data","description":"Base64-encoded PDF file data"},"dpi":{"type":"integer","maximum":600.0,"minimum":72.0,"title":"Dpi","description":"Resolution for OCR rendering","default":300},"ocr_lang":{"type":"string","minLength":2,"pattern":"^[a-z]{2,}(_[a-z]+)?(\\+[a-z]{2,}(_[a-z]+)?)*$","title":"Ocr Lang","description":"OCR language code(s), e.g. 'eng', 'eng+spa'. Use Tesseract language codes.","default":"eng"},"max_pages":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Max Pages","description":"Maximum pages to process (default 50, max 200). Prevents runaway OCR on large documents.","default":50}},"type":"object","required":["pdf_data"],"title":"PDFExtractOCRRequest","description":"Request model for OCR-based PDF extraction."},"PDFExtractOCRResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"pdf_type":{"type":"string","title":"Pdf Type","default":"scanned"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Extraction confidence (0.0-1.0)"},"text":{"type":"string","title":"Text","description":"OCR-extracted text from all pages"},"pages":{"type":"integer","title":"Pages","description":"Number of pages processed"},"total_pages":{"type":"integer","title":"Total Pages","description":"Total pages in document"},"truncated":{"type":"boolean","title":"Truncated","description":"True if document was truncated due to max_pages limit","default":false},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"PDF metadata"},"ocr_details":{"additionalProperties":true,"type":"object","title":"Ocr Details","description":"OCR run details"}},"type":"object","required":["success","confidence","text","pages","total_pages"],"title":"PDFExtractOCRResponse","description":"Response model for OCR PDF extraction."},"PDFExtractWithSchemaRequest":{"properties":{"pdf_data":{"type":"string","title":"Pdf Data","description":"Base64-encoded PDF file data"},"schema":{"additionalProperties":true,"type":"object","title":"Schema","description":"Schema defining fields to extract"},"mode":{"type":"string","enum":["digital","ocr","auto"],"title":"Mode","description":"Extraction strategy","default":"auto"}},"type":"object","required":["pdf_data","schema"],"title":"PDFExtractWithSchemaRequest","description":"Request model for schema-based PDF extraction."},"PDFExtractWithSchemaResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"pdf_type":{"type":"string","title":"Pdf Type"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Overall extraction confidence"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"PDF metadata"},"extraction_path":{"type":"string","title":"Extraction Path","description":"Extraction method used"},"fields":{"additionalProperties":true,"type":"object","title":"Fields","description":"Extracted fields per schema"}},"type":"object","required":["success","pdf_type","confidence","extraction_path","fields"],"title":"PDFExtractWithSchemaResponse","description":"Response model for schema-based PDF extraction."},"PageContent":{"properties":{"html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Html"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"markdown":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Markdown"}},"type":"object","title":"PageContent","description":"Page content in various formats."},"PageLinks":{"properties":{"internal":{"items":{"$ref":"#/components/schemas/LinkInfo"},"type":"array","title":"Internal","default":[]},"external":{"items":{"$ref":"#/components/schemas/LinkInfo"},"type":"array","title":"External","default":[]},"total_count":{"type":"integer","title":"Total Count","default":0}},"type":"object","title":"PageLinks","description":"Links found on the page."},"PageMetadata":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"charset":{"type":"string","title":"Charset","default":"utf-8"},"content_type":{"type":"string","title":"Content Type","default":"text/html"}},"type":"object","title":"PageMetadata","description":"Page metadata extracted from HTML."},"Project":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"id":{"type":"string","title":"Id"},"owner_id":{"type":"string","title":"Owner Id"},"is_default":{"type":"boolean","title":"Is Default"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["name","id","owner_id","is_default","created_at","updated_at"],"title":"Project","description":"Response model for a project.","examples":[{"created_at":"2026-01-29T10:30:00Z","description":"Market analysis and competitor research","id":"prj_1a2b3c4d5e6f","is_default":false,"name":"Research Project","owner_id":"usr_9z8y7x6w5v","updated_at":"2026-01-29T15:45:00Z"}]},"ProjectCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_default":{"type":"boolean","title":"Is Default","description":"Mark as default project (one per user)","default":false}},"type":"object","required":["name"],"title":"ProjectCreate","description":"Request model for creating a project.","examples":[{"description":"Market analysis and competitor research","is_default":false,"name":"Research Project"}]},"ProjectExportResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"format":{"type":"string","title":"Format"},"files":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Files"},"chats":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Chats"},"file_count":{"type":"integer","title":"File Count","default":0},"chat_count":{"type":"integer","title":"Chat Count","default":0},"message_count":{"type":"integer","title":"Message Count","default":0}},"type":"object","required":["project_id","project_name","format"],"title":"ProjectExportResponse","description":"Response model for project export."},"ProjectPermission":{"properties":{"user_id":{"type":"string","title":"User Id"},"role":{"type":"string","pattern":"^(owner|editor|viewer)$","title":"Role"},"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"granted_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Granted By"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["user_id","role","id","project_id","granted_by","created_at"],"title":"ProjectPermission","description":"Response model for a project permission.","examples":[{"created_at":"2026-01-29T16:45:00Z","granted_by":"usr_9z8y7x6w5v","id":"perm_9x8c7v6b5n","project_id":"prj_1a2b3c4d5e6f","role":"editor","user_id":"usr_5t4r3e2w1q"}]},"ProjectPermissionCreate":{"properties":{"user_id":{"type":"string","title":"User Id"},"role":{"type":"string","pattern":"^(owner|editor|viewer)$","title":"Role"}},"type":"object","required":["user_id","role"],"title":"ProjectPermissionCreate","description":"Request model for granting project permission.","examples":[{"role":"editor","user_id":"usr_5t4r3e2w1q"}]},"ProjectPermissionUpdate":{"properties":{"role":{"type":"string","pattern":"^(owner|editor|viewer)$","title":"Role"}},"type":"object","required":["role"],"title":"ProjectPermissionUpdate","description":"Request model for updating project permission.","examples":[{"role":"viewer"}]},"ProjectUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"ProjectUpdate","description":"Request model for updating a project.","examples":[{"description":"Expanded to include competitive analysis and market trends","name":"Updated Research Project"}]},"PubMedArticle":{"properties":{"pmid":{"type":"string","title":"Pmid"},"title":{"type":"string","title":"Title"},"abstract":{"type":"string","title":"Abstract"},"authors":{"items":{"type":"string"},"type":"array","title":"Authors"},"journal":{"type":"string","title":"Journal"},"publication_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Publication Date"},"doi":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Doi"},"pmc_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pmc Id"},"pmc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pmc Url"},"publication_types":{"items":{"type":"string"},"type":"array","title":"Publication Types"},"mesh_terms":{"items":{"type":"string"},"type":"array","title":"Mesh Terms"},"pubmed_url":{"type":"string","title":"Pubmed Url"},"references":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"References"},"related_articles":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Related Articles"}},"type":"object","required":["pmid","title","abstract","authors","journal","publication_date","doi","publication_types","mesh_terms","pubmed_url"],"title":"PubMedArticle","description":"PubMed article metadata."},"PubMedDateRange":{"properties":{"start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start","description":"Start date (YYYY/MM/DD)"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End","description":"End date (YYYY/MM/DD)"}},"additionalProperties":false,"type":"object","title":"PubMedDateRange","description":"Date range filter for PubMed search."},"PubMedFilters":{"properties":{"date_range":{"anyOf":[{"$ref":"#/components/schemas/PubMedDateRange"},{"type":"null"}],"description":"Date range filter"},"sort":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sort","description":"Sort order: relevance, pub_date, first_author, journal","default":"relevance"},"publication_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Publication Types","description":"Filter by publication type"}},"additionalProperties":false,"type":"object","title":"PubMedFilters","description":"Typed filters for PubMed search. Unknown keys are rejected."},"PubMedSearchMetadata":{"properties":{"ncbi_api_calls":{"type":"integer","title":"Ncbi Api Calls"},"cached_results":{"type":"integer","title":"Cached Results"},"rate_limit_remaining":{"type":"number","title":"Rate Limit Remaining"}},"type":"object","required":["ncbi_api_calls","cached_results","rate_limit_remaining"],"title":"PubMedSearchMetadata","description":"Metadata about the search operation."},"PubMedSearchRequest":{"properties":{"query":{"type":"string","minLength":1,"title":"Query","description":"Search query (supports natural language and NCBI syntax)"},"max_results":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Max Results","description":"Maximum number of results (1-100)","default":10},"filters":{"anyOf":[{"$ref":"#/components/schemas/PubMedFilters"},{"type":"null"}],"description":"Optional filters (date_range, sort, publication_types)"}},"type":"object","required":["query"],"title":"PubMedSearchRequest","description":"Request model for PubMed search."},"PubMedSearchResponse":{"properties":{"status":{"type":"string","title":"Status"},"query":{"type":"string","title":"Query"},"total_results":{"type":"integer","title":"Total Results"},"articles":{"items":{"$ref":"#/components/schemas/PubMedArticle"},"type":"array","title":"Articles"},"search_metadata":{"$ref":"#/components/schemas/PubMedSearchMetadata"}},"type":"object","required":["status","query","total_results","articles","search_metadata"],"title":"PubMedSearchResponse","description":"Response model for PubMed search."},"PythonExecRequest":{"properties":{"code":{"type":"string","maxLength":65536,"title":"Code","description":"Python code defining def run(params). Import statements are not allowed."},"params":{"additionalProperties":true,"type":"object","title":"Params","description":"Params dict passed as the single argument to run(params)."},"timeout":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Timeout","description":"Execution timeout in seconds (default: 5, max: 30).","default":5}},"type":"object","required":["code"],"title":"PythonExecRequest","description":"Request body for executing stateless Python code.","example":{"code":"def run(params):\n    return sum(params['values'])","params":{"values":[10,20,30,40,50]},"timeout":10}},"PythonExecResponse":{"properties":{"result":{"title":"Result"},"execution_time_ms":{"type":"number","title":"Execution Time Ms"},"stdout":{"type":"string","title":"Stdout"},"stderr":{"type":"string","title":"Stderr"}},"type":"object","required":["result","execution_time_ms","stdout","stderr"],"title":"PythonExecResponse","description":"Response body for executing stateless Python code."},"PythonLintRequest":{"properties":{"code":{"type":"string","maxLength":65536,"title":"Code","description":"Python code defining def run(params). Import statements are not allowed."},"sample_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Sample Params","description":"Optional sample params dict to run through run(params) if validation succeeds."}},"type":"object","required":["code"],"title":"PythonLintRequest","description":"Request body for linting stateless Python code.","example":{"code":"def run(params):\n    return sum(params['values'])","sample_params":{"values":[1,2,3]}}},"PythonLintResponse":{"properties":{"valid":{"type":"boolean","title":"Valid"},"errors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Errors"},"warnings":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Warnings"},"available_modules":{"items":{"type":"string"},"type":"array","title":"Available Modules"},"sample_result":{"anyOf":[{},{"type":"null"}],"title":"Sample Result"},"sample_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sample Error"}},"type":"object","required":["valid","errors","warnings","available_modules"],"title":"PythonLintResponse","description":"Response body for linting stateless Python code."},"QualityDimension":{"properties":{"category":{"type":"string","title":"Category","description":"LLM-assigned category: not_at_all (0), low (1), partial (2), high (3), exceptional (4)"},"score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Score","description":"Normalised score for this dimension (category value / 4.0)"}},"type":"object","required":["category","score"],"title":"QualityDimension","description":"A single quality dimension with its enum category and normalised score."},"ResourcesResponse":{"properties":{"indexed_at":{"type":"string","format":"date-time","title":"Indexed At","description":"When knowledge base was built"},"sources":{"additionalProperties":{"$ref":"#/components/schemas/SourceInfo"},"type":"object","title":"Sources","description":"Information about each source category (docs, examples)"},"openapi":{"$ref":"#/components/schemas/OpenAPIInfo","description":"OpenAPI spec information"},"total_tokens":{"type":"integer","title":"Total Tokens","description":"Total estimated tokens in knowledge base"}},"type":"object","required":["indexed_at","sources","openapi","total_tokens"],"title":"ResourcesResponse","description":"Response for knowledge base resources inventory."},"SOP":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"SOP name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"SOP description"},"is_public":{"type":"boolean","title":"Is Public","description":"Make SOP publicly accessible","default":false},"id":{"type":"string","title":"Id"},"owner_id":{"type":"string","title":"Owner Id"},"current_version":{"type":"integer","title":"Current Version","description":"Current/latest version number"},"definition":{"$ref":"#/components/schemas/SOPDefinition"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["name","id","owner_id","current_version","definition","created_at","updated_at"],"title":"SOP","description":"Response model for an SOP."},"SOPBlock":{"properties":{"id":{"type":"string","title":"Id","description":"Unique block identifier"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Human-readable block name"},"type":{"type":"string","title":"Type","description":"Block type: tool, llm, decision, goto (v2.1), state (v2.1)","default":"llm"},"order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Order","description":"Execution order (v1 only)"},"depends_on":{"items":{"type":"string"},"type":"array","title":"Depends On","description":"Block IDs this block depends on (v1 only)"},"input_schema":{"anyOf":[{"$ref":"#/components/schemas/SOPBlockSchema"},{"type":"null"}],"description":"JSON Schema for block inputs (v1)"},"output_schema":{"anyOf":[{"$ref":"#/components/schemas/SOPBlockSchema"},{"type":"null"}],"description":"JSON Schema for block outputs (v1)"},"prompt_template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt Template","description":"LLM prompt template (v1)"},"transform_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transform Format","description":"Output format: markdown, html, json, text","default":"markdown"},"audit":{"type":"boolean","title":"Audit","description":"Enable SLAF audit for this block","default":false},"file_inputs":{"anyOf":[{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array"},{"type":"null"}],"title":"File Inputs","description":"Files to read from workspace"},"file_outputs":{"anyOf":[{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array"},{"type":"null"}],"title":"File Outputs","description":"Files to write to workspace"},"retry_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Retry Config","description":"Retry configuration"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Human-readable node label (v2)"},"tool":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tool","description":"Tool name to execute (v2)"},"inputs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Inputs","description":"Template-resolved tool inputs (v2)"},"save":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Save","description":"Save operations: before_tool/after_tool (v2)"},"next":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Next","description":"Routing: {goto: node_id} or {decision: {...}} (v2)"}},"type":"object","required":["id"],"title":"SOPBlock","description":"Block/step definition within an SOP.\n\nSupports two grammar versions:\n\nspec_version 1 (legacy): Uses id, order, depends_on, input_schema, output_schema,\n    prompt_template. Execution via topological sort.\n\nspec_version 2 (SES-161): Uses node_id, tool, inputs, save, next.\n    Execution via next-based routing (goto/decision).\n\nSES-161: Added 'type' field for grammar validation.\nValid types depend on sop_version:\n- v2.0: tool, llm, decision\n- v2.1: tool, llm, decision, goto, state"},"SOPBlockSchema":{"properties":{"type":{"type":"string","title":"Type"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"},"required":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Required"}},"type":"object","required":["type","properties"],"title":"SOPBlockSchema","description":"Schema definition for a block input/output.\n\nEnforces JSON Schema validity for 'required' and 'properties' fields:\n- 'required' must be a list (if present), not a string, number, or null\n- 'properties' must be a dict (if present), not null, list, or string\n\nThis prevents runtime crashes from invalid schemas (SES-142)."},"SOPCatalogItem":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"usage_count":{"type":"integer","title":"Usage Count","default":0},"avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Rating"},"is_public":{"type":"boolean","title":"Is Public"},"version":{"type":"integer","title":"Version"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","description","owner","owner_id","is_public","version","created_at"],"title":"SOPCatalogItem","description":"SOP item in catalog listing."},"SOPCatalogResponse":{"properties":{"sops":{"items":{"$ref":"#/components/schemas/SOPCatalogItem"},"type":"array","title":"Sops"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page","default":1},"page_size":{"type":"integer","title":"Page Size","default":50}},"type":"object","required":["sops","total"],"title":"SOPCatalogResponse","description":"Response model for SOP catalog listing."},"SOPCategory":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"sop_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sop Count","default":0},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","description","icon","created_at"],"title":"SOPCategory","description":"Response model for an SOP category."},"SOPCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"SOP name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"SOP description"},"is_public":{"type":"boolean","title":"Is Public","description":"Make SOP publicly accessible","default":false},"definition":{"$ref":"#/components/schemas/SOPDefinition","description":"Complete SOP definition"},"category_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Category Ids","description":"Category IDs"}},"type":"object","required":["name","definition"],"title":"SOPCreate","description":"Request model for creating an SOP."},"SOPDefinition":{"properties":{"spec_version":{"type":"integer","title":"Spec Version","description":"Spec version: 1 (legacy) or 2 (SES-161 next-based)","default":2},"sop_version":{"type":"string","title":"Sop Version","description":"Grammar version (2.0 or 2.1)","default":"2.1"},"mode":{"type":"string","title":"Mode","description":"Execution mode: state-machine or dag (spec_version 2)","default":"state-machine"},"required_fields":{"anyOf":[{"$ref":"#/components/schemas/SOPBlockSchema"},{"type":"null"}],"description":"Input data schema (JSON Schema)"},"blocks":{"anyOf":[{"items":{"$ref":"#/components/schemas/SOPBlock"},"type":"array"},{"type":"null"}],"title":"Blocks","description":"Processing steps (spec_version 1)"},"nodes":{"anyOf":[{"items":{"$ref":"#/components/schemas/SOPBlock"},"type":"array"},{"type":"null"}],"title":"Nodes","description":"Node definitions (spec_version 2)"},"limits":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Limits","description":"Safety limits for execution (v2)"},"settings":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Settings","description":"LLM and execution settings"},"execution":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Execution","description":"Execution configuration"}},"type":"object","title":"SOPDefinition","description":"Complete SOP definition including blocks and requirements.\n\nSupports two spec versions:\n\nspec_version 1 (legacy/default): Uses blocks array with depends_on/order.\nspec_version 2 (SES-161): Uses nodes array with next-based routing.\n\nSES-161: Added sop_version for grammar versioning:\n- v2.0: Basic capabilities (tool, llm, decision blocks), DAG mode only\n- v2.1: Extended capabilities (adds goto, state blocks), DAG or State Machine mode"},"SOPInvokeRequest":{"properties":{"project_id":{"type":"string","title":"Project Id","description":"Project containing input files"},"sop_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sop Ref","description":"SOP reference: 'sop:<uuid>:<version>' or just '<uuid>' for latest"},"version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version","description":"Specific version number (alternative to sop_ref)"},"input_file_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Input File Ids","description":"Specific file IDs to use as input"},"input_paths":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Input Paths","description":"Paths within project to use as input"},"use_all_files":{"type":"boolean","title":"Use All Files","description":"Use all files in project","default":false},"manual_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Manual Data","description":"Manual data override"},"output_path":{"type":"string","title":"Output Path","description":"Workspace directory path for outputs (e.g. '/outputs/prd')"},"execution_mode":{"type":"string","pattern":"^(strict|relaxed|custom)$","title":"Execution Mode","description":"Execution mode: 'strict' (require all required_fields), 'relaxed' (allow partial), or 'custom'.","default":"strict"},"execution_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Execution Config","description":"Custom execution configuration when execution_mode='custom' (e.g. SLAF/audit tuning)."},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url","description":"URL to receive POST callback when SOP run completes or fails. Must be HTTPS in production (HTTP allowed in test/dev)."}},"type":"object","required":["project_id","output_path"],"title":"SOPInvokeRequest","description":"Request model for invoking an SOP.","example":{"execution_mode":"strict","input_file_ids":["fil-111","fil-222"],"input_paths":["/inputs/papers"],"manual_data":{"disease":"ulcerative colitis","needs_references":true},"output_path":"/outputs/ucolitis-summary","project_id":"prj-1234567890abcdef","sop_ref":"sop-abcdef1234567890:3","use_all_files":false,"webhook_url":"https://example.com/webhooks/sop-status"}},"SOPInvokeResponse":{"properties":{"run_id":{"type":"string","title":"Run Id"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"},"completeness_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Completeness Score"},"missing_fields":{"anyOf":[{"items":{"$ref":"#/components/schemas/MissingField"},"type":"array"},{"type":"null"}],"title":"Missing Fields"},"estimated_duration":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estimated Duration"},"blocks_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blocks Total"}},"type":"object","required":["run_id","status","message"],"title":"SOPInvokeResponse","description":"Response model for SOP invocation.","example":{"blocks_total":5,"completeness_score":0.94,"estimated_duration":"2-3 minutes","job_id":"job-01JDX2P5FGHIJ...","message":"SOP execution queued. Data completeness score: 0.94.","missing_fields":[],"run_id":"srun-01JDX2P4ABCDE...","status":"pending"}},"SOPPermission":{"properties":{"user_id":{"type":"string","title":"User Id"},"role":{"type":"string","pattern":"^(viewer|executor|editor)$","title":"Role"},"id":{"type":"string","title":"Id"},"sop_id":{"type":"string","title":"Sop Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["user_id","role","id","sop_id","created_at"],"title":"SOPPermission","description":"Response model for an SOP permission."},"SOPPermissionCreate":{"properties":{"user_id":{"type":"string","title":"User Id"},"role":{"type":"string","pattern":"^(viewer|executor|editor)$","title":"Role"}},"type":"object","required":["user_id","role"],"title":"SOPPermissionCreate","description":"Request model for granting SOP permission."},"SOPRunTraceEntry":{"properties":{"id":{"type":"string","title":"Id"},"block_id":{"type":"string","title":"Block Id"},"visit_number":{"type":"integer","title":"Visit Number","default":1},"status":{"type":"string","title":"Status"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"input_snapshot":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input Snapshot"},"output_snapshot":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Output Snapshot"},"skip_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skip Reason"},"error_details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Error Details"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","block_id","status","created_at"],"title":"SOPRunTraceEntry","description":"A single block execution trace entry."},"SOPRunTraceResponse":{"properties":{"run_id":{"type":"string","title":"Run Id"},"sop_id":{"type":"string","title":"Sop Id"},"status":{"type":"string","title":"Status"},"total_visits":{"type":"integer","title":"Total Visits"},"block_trace":{"items":{"$ref":"#/components/schemas/SOPRunTraceEntry"},"type":"array","title":"Block Trace"},"wip_context":{"additionalProperties":true,"type":"object","title":"Wip Context"},"summary":{"$ref":"#/components/schemas/SOPRunTraceSummary"}},"type":"object","required":["run_id","sop_id","status","total_visits","block_trace","wip_context","summary"],"title":"SOPRunTraceResponse","description":"Response model for the run trace endpoint.","example":{"block_trace":[{"block_id":"extract_data","completed_at":"2026-01-03T12:00:05Z","created_at":"2026-01-03T12:00:00Z","duration_ms":5000,"id":"strc-abc123","output_snapshot":{"key":"value"},"started_at":"2026-01-03T12:00:00Z","status":"completed","visit_number":1}],"run_id":"srun-01JDX2P4ABCDE...","sop_id":"sop-01JDWXYZ...","status":"completed","summary":{"completed":2,"failed":0,"skipped":1,"total_blocks":3,"total_duration_ms":7000},"total_visits":3,"wip_context":{"extract_data":{"key":"value"}}}},"SOPRunTraceSummary":{"properties":{"total_blocks":{"type":"integer","title":"Total Blocks"},"completed":{"type":"integer","title":"Completed"},"skipped":{"type":"integer","title":"Skipped"},"failed":{"type":"integer","title":"Failed"},"pending":{"type":"integer","title":"Pending","default":0},"running":{"type":"integer","title":"Running","default":0},"total_duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Duration Ms"}},"type":"object","required":["total_blocks","completed","skipped","failed"],"title":"SOPRunTraceSummary","description":"Summary statistics for a run trace."},"SOPRunWebhookPayload":{"properties":{"event":{"type":"string","enum":["sop_run.completed","sop_run.failed"],"title":"Event","description":"Event type matching terminal status."},"run_id":{"type":"string","title":"Run Id","description":"SOP run identifier."},"sop_id":{"type":"string","title":"Sop Id","description":"SOP identifier."},"project_id":{"type":"string","title":"Project Id","description":"Project that owns the run."},"status":{"type":"string","enum":["completed","failed"],"title":"Status","description":"Terminal run status that triggered this notification."},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At","description":"ISO 8601 timestamp when the run reached this terminal state."},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Human-readable error summary when failed; null on success."},"output_file_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Output File Ids","description":"Output file IDs produced by the run (on success); null on failure."}},"type":"object","required":["event","run_id","sop_id","project_id","status"],"title":"SOPRunWebhookPayload","description":"JSON body POSTed to your `webhook_url` when a SOP run finishes (outbound callback, SES-261).\n\nThis is **not** a request you send to Taiso -- it is the payload **your server receives**\nwhen SOP run execution reaches a terminal state.\n\n**When fired:** Exactly once per terminal transition -- `completed` or `failed` only.\n\n**Delivery:** HTTP POST, `Content-Type: application/json`, 10s timeout, up to 3 retries\n(30s, 120s, 300s backoff). Respond with any 2xx quickly. At-least-once delivery -- dedupe\non `(run_id, status)`.","examples":[{"completed_at":"2026-06-14T12:00:00Z","event":"sop_run.completed","output_file_ids":["fil_abc123","fil_def456"],"project_id":"prj_1a2b3c4d5e6f","run_id":"srun_01JDX2P4ABCDE","sop_id":"sop_01JDWXYZ","status":"completed"},{"completed_at":"2026-06-14T12:05:00Z","error_message":"Node 'extract_data' failed: timeout","event":"sop_run.failed","project_id":"prj_1a2b3c4d5e6f","run_id":"srun_01JDX2P4FGHIJ","sop_id":"sop_01JDWXYZ","status":"failed"}]},"SOPUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public"},"definition":{"anyOf":[{"$ref":"#/components/schemas/SOPDefinition"},{"type":"null"}]},"category_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Category Ids"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary","description":"Summary of changes in this version"}},"type":"object","title":"SOPUpdate","description":"Request model for updating an SOP."},"SOPUpdateResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"current_version":{"type":"integer","title":"Current Version"},"previous_version":{"type":"integer","title":"Previous Version"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"warnings":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Warnings","description":"Warnings about breaking changes"}},"type":"object","required":["id","name","current_version","previous_version","updated_at"],"title":"SOPUpdateResponse","description":"Response model for SOP update with version info."},"SOPUsageStats":{"properties":{"sop_id":{"type":"string","title":"Sop Id"},"total_invocations":{"type":"integer","title":"Total Invocations"},"successful_runs":{"type":"integer","title":"Successful Runs"},"failed_runs":{"type":"integer","title":"Failed Runs"},"avg_duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Duration Seconds"},"avg_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Cost Usd"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["sop_id","total_invocations","successful_runs","failed_runs","avg_duration_seconds","avg_cost_usd","last_used_at","updated_at"],"title":"SOPUsageStats","description":"Response model for SOP usage statistics."},"SOPValidateRequest":{"properties":{"project_id":{"type":"string","title":"Project Id","description":"Project containing input files"},"input_file_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Input File Ids","description":"Specific file IDs to consider when validating completeness."},"input_paths":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Input Paths","description":"Paths within the project to scan for input files."},"use_all_files":{"type":"boolean","title":"Use All Files","description":"If true, validate using all files in the project (ignores input_file_ids/paths).","default":false},"manual_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Manual Data","description":"Optional manual values for required fields (used instead of reading from files)."}},"type":"object","required":["project_id"],"title":"SOPValidateRequest","description":"Request model for validating SOP data without executing.","example":{"input_file_ids":["fil-aaa","fil-bbb"],"manual_data":{"author":"Clinical Ops Team","indication":"refractory IBD"},"project_id":"prj-1234567890abcdef","use_all_files":false}},"SOPValidateResponse":{"properties":{"is_complete":{"type":"boolean","title":"Is Complete"},"completeness_score":{"type":"number","title":"Completeness Score"},"extracted_data":{"additionalProperties":true,"type":"object","title":"Extracted Data"},"data_sources":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/DataSource"},"type":"object"},{"type":"null"}],"title":"Data Sources"},"missing_fields":{"items":{"$ref":"#/components/schemas/MissingField"},"type":"array","title":"Missing Fields"}},"type":"object","required":["is_complete","completeness_score","extracted_data","missing_fields"],"title":"SOPValidateResponse","description":"Response model for SOP validation."},"SOPValidationResult":{"properties":{"is_valid":{"type":"boolean","title":"Is Valid","description":"Whether the SOP definition is valid"},"sop_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sop Version","description":"Detected SOP grammar version"},"execution_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Execution Mode","description":"Detected execution mode (dag/state-machine)"},"block_count":{"type":"integer","title":"Block Count","description":"Number of blocks in the SOP","default":0},"execution_order":{"items":{"type":"string"},"type":"array","title":"Execution Order","description":"Topological order of block execution"},"has_loops":{"type":"boolean","title":"Has Loops","description":"Whether the SOP contains loops (goto)","default":false},"has_dynamic_tools":{"type":"boolean","title":"Has Dynamic Tools","description":"Whether tools are referenced dynamically","default":false},"has_conditionals":{"type":"boolean","title":"Has Conditionals","description":"Whether any blocks have 'when' conditions","default":false},"supported_features":{"items":{"type":"string"},"type":"array","uniqueItems":true,"title":"Supported Features","description":"Features used in this SOP"},"errors":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Errors","description":"Validation errors (fatal)"},"warnings":{"items":{"$ref":"#/components/schemas/ValidationWarning"},"type":"array","title":"Warnings","description":"Validation warnings (non-fatal)"},"validation_time_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Validation Time Ms","description":"Time taken to validate (ms)"}},"type":"object","required":["is_valid"],"title":"SOPValidationResult","description":"Complete result of SOP grammar validation.","example":{"block_count":3,"errors":[],"execution_mode":"dag","execution_order":["search","analyze","summarize"],"has_conditionals":true,"has_dynamic_tools":false,"has_loops":false,"is_valid":true,"sop_version":"2.0","supported_features":["dag","when","depends_on","tool","llm"],"validation_time_ms":12,"warnings":[]}},"SOPVersion":{"properties":{"sop_id":{"type":"string","title":"Sop Id"},"version":{"type":"integer","title":"Version"},"definition":{"$ref":"#/components/schemas/SOPDefinition"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"type":"string","title":"Created By"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary"},"breaking_changes":{"type":"boolean","title":"Breaking Changes","default":false}},"type":"object","required":["sop_id","version","definition","created_at","created_by"],"title":"SOPVersion","description":"Response model for a specific SOP version."},"SOPVersionList":{"properties":{"sop_id":{"type":"string","title":"Sop Id"},"name":{"type":"string","title":"Name"},"current_version":{"type":"integer","title":"Current Version"},"versions":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Versions","description":"List of version metadata"}},"type":"object","required":["sop_id","name","current_version","versions"],"title":"SOPVersionList","description":"Response model for listing SOP versions."},"SchemaMetadata":{"properties":{"detected_fields":{"type":"integer","title":"Detected Fields","description":"Number of fields detected"},"required_fields":{"type":"integer","title":"Required Fields","description":"Number of required fields","default":0},"optional_fields":{"type":"integer","title":"Optional Fields","description":"Number of optional fields","default":0},"model_used":{"type":"string","title":"Model Used","description":"LLM model used for generation"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total tokens used"},"execution_time_ms":{"type":"integer","title":"Execution Time Ms","description":"Execution time in milliseconds"}},"type":"object","required":["detected_fields","model_used","tokens_used","execution_time_ms"],"title":"SchemaMetadata","description":"Metadata about schema generation."},"SearchResponse":{"properties":{"query":{"type":"string","title":"Query"},"results":{"items":{"$ref":"#/components/schemas/SearchResult"},"type":"array","title":"Results"},"total":{"type":"integer","title":"Total"},"search_method":{"type":"string","title":"Search Method"},"elasticsearch_available":{"type":"boolean","title":"Elasticsearch Available"}},"type":"object","required":["query","results","total","search_method","elasticsearch_available"],"title":"SearchResponse","description":"Search response.","example":{"elasticsearch_available":true,"query":"mucosal barrier function","results":[{"content":"The intestinal mucosal barrier is maintained by tight junctions, mucus, and immune factors...","created_at":"2025-11-20T12:00:00Z","id":"fchunk-01JDX2R7PQRS...","project_id":"prj-1234567890abcdef","score":0.92,"search_method":"hybrid","title":"mucosal_barrier_review.pdf","type":"chunk"}],"search_method":"hybrid","total":1}},"SearchResult":{"properties":{"type":{"type":"string","enum":["file","message","chunk"],"title":"Type"},"id":{"type":"string","title":"Id"},"project_id":{"type":"string","title":"Project Id"},"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"score":{"type":"number","title":"Score"},"created_at":{"type":"string","title":"Created At"},"search_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search Method"}},"type":"object","required":["type","id","project_id","title","content","score","created_at"],"title":"SearchResult","description":"Search result item."},"SimpleJsonRequest":{"properties":{"prompt":{"type":"string","title":"Prompt"},"input_data":{"type":"string","title":"Input Data"},"model":{"type":"string","title":"Model","description":"LLM model to use (defaults to configured LLM_DEFAULT_MODEL)."},"temperature":{"type":"number","title":"Temperature","default":0.0},"max_tokens":{"type":"integer","title":"Max Tokens","default":1000},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider","description":"Optionally pin the backend that serves this request: `openrouter`, `sambanova`, or `direct` (per-vendor keys). **Omit for normal calls** — when absent, routing is unchanged.\n\nThere is no failover when pinned: you get that backend or an error explaining why not. Use it to route around a provider incident, or to compare the same model across backends. The backend actually used is returned as `provider_type` on the response."},"lodestar":{"anyOf":[{"$ref":"#/components/schemas/LodestarRequest"},{"type":"null"}],"description":"Optional answer verification. **Omit this field for a normal call.**\n\nChecks the extracted values against documents you supply in `sources` and returns a per-claim verdict. If verification produces a corrected extraction, it is only used when it still parses as valid JSON — otherwise your original `data` is returned unchanged."}},"type":"object","required":["prompt","input_data"],"title":"SimpleJsonRequest","description":"Simple JSON extraction request for quick testing.","example":{"input_data":"Patient: Jane Doe, a 47-year-old female, presents with a 6-month history of abdominal pain and weight loss. Final diagnosis: Crohn's disease.","lodestar":{"enhance":"verify","sources":[{"id":"note","text":"Patient: Jane Doe, a 47-year-old female. Final diagnosis: Crohn's disease."}]},"max_tokens":300,"model":"openai/gpt-5.2","prompt":"Extract the patient's name, age, and diagnosis from this note.","temperature":0.0}},"Source":{"properties":{"id":{"type":"string","maxLength":128,"minLength":1,"title":"Id"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Uri"}},"type":"object","required":["id"],"title":"Source","description":"A document the answer must be checkable against.\n\nArgs:\n    id: Caller-chosen identifier, echoed back in claim attribution.\n    text: Inline document text.\n    uri: Remote document location. **Not fetched in v1** — resolving a\n        caller-supplied URI inside a verification path is an SSRF surface\n        and needs an allow-list first (SES-282 open decision). Present in\n        the schema so callers can be rejected with a clear message rather\n        than silently having their grounding ignored."},"SourceCitations":{"properties":{"source_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Source Metadata"},"total_citations":{"type":"integer","title":"Total Citations"},"citations":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Citations"}},"type":"object","required":["total_citations","citations"],"title":"SourceCitations","description":"All citations from a single source."},"SourceDocument":{"properties":{"id":{"type":"string","title":"Id","description":"Unique identifier for this source"},"type":{"type":"string","enum":["file","chat_message","manual_input"],"title":"Type","description":"Type of source document"},"content":{"type":"string","title":"Content","description":"Full text content of the document"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Optional metadata (filename, timestamp, etc.)"}},"type":"object","required":["id","type","content"],"title":"SourceDocument","description":"A source document for audit verification."},"SourceInfo":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of items"},"files":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Files","description":"List of file names"},"total_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Tokens","description":"Estimated total tokens"},"total_lines":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Lines","description":"Total lines of code"}},"type":"object","required":["count"],"title":"SourceInfo","description":"Information about a source category."},"SuggestResponse":{"properties":{"suggestions":{"items":{"$ref":"#/components/schemas/Suggestion"},"type":"array","title":"Suggestions","description":"List of suggestions matching the prefix"},"total":{"type":"integer","title":"Total","description":"Total number of suggestions"}},"type":"object","required":["suggestions","total"],"title":"SuggestResponse","description":"Response for autocomplete suggestions."},"Suggestion":{"properties":{"type":{"type":"string","enum":["endpoint","workflow","topic","example"],"title":"Type","description":"Type of suggestion"},"value":{"type":"string","title":"Value","description":"Suggestion value"},"description":{"type":"string","title":"Description","description":"Brief description"},"keywords":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Keywords","description":"Related keywords"}},"type":"object","required":["type","value","description"],"title":"Suggestion","description":"Single autocomplete suggestion."},"TavilySearchRequest":{"properties":{"query":{"type":"string","minLength":1,"title":"Query","description":"Search query (must not be empty)"},"max_results":{"type":"integer","maximum":20.0,"minimum":1.0,"title":"Max Results","description":"Number of results (1-20)","default":5},"search_depth":{"type":"string","enum":["basic","advanced"],"title":"Search Depth","description":"Search depth: 'basic' or 'advanced'","default":"basic"},"include_answer":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Include Answer","default":true},"include_raw_content":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Include Raw Content","default":false},"include_images":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Include Images","default":false}},"type":"object","required":["query"],"title":"TavilySearchRequest"},"TavilySearchResponse":{"properties":{"query":{"type":"string","title":"Query"},"results":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Results"},"answer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Answer"},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Images"},"response_time":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Response Time"}},"type":"object","required":["query","results"],"title":"TavilySearchResponse"},"TextDiffRequest":{"properties":{"text1":{"type":"string","title":"Text1"},"text2":{"type":"string","title":"Text2"},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format","default":"unified"},"context_lines":{"type":"integer","minimum":0.0,"title":"Context Lines","default":3},"from_file":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From File","default":"text1"},"to_file":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To File","default":"text2"}},"type":"object","required":["text1","text2"],"title":"TextDiffRequest","description":"Request model for text diff."},"TextDiffResponse":{"properties":{"diff":{"type":"string","title":"Diff"},"format":{"type":"string","title":"Format"},"changes_summary":{"additionalProperties":{"type":"integer"},"type":"object","title":"Changes Summary"},"from_file":{"type":"string","title":"From File"},"to_file":{"type":"string","title":"To File"}},"type":"object","required":["diff","format","changes_summary","from_file","to_file"],"title":"TextDiffResponse","description":"Response model for text diff."},"TextExtractRequest":{"properties":{"text":{"type":"string","title":"Text"},"pattern":{"type":"string","title":"Pattern"},"case_sensitive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Case Sensitive","default":true},"multiline":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiline","default":false},"dotall":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Dotall","default":false},"extract_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Extract Group"},"extract_named_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Extract Named Group"},"join_with":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Join With"}},"type":"object","required":["text","pattern"],"title":"TextExtractRequest","description":"Request model for text extraction."},"TextExtractResponse":{"properties":{"pattern":{"type":"string","title":"Pattern"},"extractions":{"items":{"type":"string"},"type":"array","title":"Extractions"},"extraction_count":{"type":"integer","title":"Extraction Count"},"joined_result":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Joined Result"},"flags_used":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Flags Used"}},"type":"object","required":["pattern","extractions","extraction_count","flags_used"],"title":"TextExtractResponse","description":"Response model for text extraction."},"TextReplaceRequest":{"properties":{"text":{"type":"string","title":"Text"},"pattern":{"type":"string","title":"Pattern"},"replacement":{"type":"string","title":"Replacement"},"case_sensitive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Case Sensitive","default":true},"multiline":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiline","default":false},"dotall":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Dotall","default":false},"max_replacements":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Replacements"},"return_diff":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Return Diff","default":false}},"type":"object","required":["text","pattern","replacement"],"title":"TextReplaceRequest","description":"Request model for text replace."},"TextReplaceResponse":{"properties":{"original_text":{"type":"string","title":"Original Text"},"modified_text":{"type":"string","title":"Modified Text"},"pattern":{"type":"string","title":"Pattern"},"replacement":{"type":"string","title":"Replacement"},"replacements_made":{"type":"integer","title":"Replacements Made"},"diff":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Diff"},"flags_used":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Flags Used"}},"type":"object","required":["original_text","modified_text","pattern","replacement","replacements_made","flags_used"],"title":"TextReplaceResponse","description":"Response model for text replace."},"TextSearchMatch":{"properties":{"match":{"type":"string","title":"Match"},"start":{"type":"integer","title":"Start"},"end":{"type":"integer","title":"End"},"line_number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Line Number"},"groups":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Groups"},"named_groups":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Named Groups"}},"type":"object","required":["match","start","end"],"title":"TextSearchMatch","description":"A single match from text search."},"TextSearchRequest":{"properties":{"text":{"type":"string","title":"Text"},"pattern":{"type":"string","title":"Pattern"},"case_sensitive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Case Sensitive","default":true},"multiline":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multiline","default":false},"dotall":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Dotall","default":false},"return_positions":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Return Positions","default":true},"return_groups":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Return Groups","default":true},"max_matches":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Matches"}},"type":"object","required":["text","pattern"],"title":"TextSearchRequest","description":"Request model for text search."},"TextSearchResponse":{"properties":{"pattern":{"type":"string","title":"Pattern"},"match_count":{"type":"integer","title":"Match Count"},"matches":{"items":{"$ref":"#/components/schemas/TextSearchMatch"},"type":"array","title":"Matches"},"flags_used":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Flags Used"}},"type":"object","required":["pattern","match_count","matches","flags_used"],"title":"TextSearchResponse","description":"Response model for text search."},"TextStatsRequest":{"properties":{"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"TextStatsRequest","description":"Request model for text statistics."},"TextStatsResponse":{"properties":{"characters":{"type":"integer","title":"Characters"},"characters_no_space":{"type":"integer","title":"Characters No Space"},"words":{"type":"integer","title":"Words"},"lines":{"type":"integer","title":"Lines"},"avg_word_length":{"type":"number","title":"Avg Word Length"},"sentences":{"type":"integer","title":"Sentences"},"longest_word":{"type":"string","title":"Longest Word"},"unique_words":{"type":"integer","title":"Unique Words"},"avg_sentence_length":{"type":"number","title":"Avg Sentence Length"},"avg_syllables_per_word":{"type":"number","title":"Avg Syllables Per Word"},"flesch_reading_ease":{"type":"number","title":"Flesch Reading Ease"},"flesch_kincaid_grade":{"type":"number","title":"Flesch Kincaid Grade"},"gunning_fog_index":{"type":"number","title":"Gunning Fog Index"}},"type":"object","required":["characters","characters_no_space","words","lines","avg_word_length","sentences","longest_word","unique_words","avg_sentence_length","avg_syllables_per_word","flesch_reading_ease","flesch_kincaid_grade","gunning_fog_index"],"title":"TextStatsResponse","description":"Response model for text statistics."},"TimeDiffRequest":{"properties":{"timestamp1":{"type":"string","title":"Timestamp1"},"timestamp2":{"type":"string","title":"Timestamp2"},"format":{"type":"string","title":"Format","default":"seconds"}},"type":"object","required":["timestamp1","timestamp2"],"title":"TimeDiffRequest","description":"Request model for time difference calculation."},"TimeDiffResponse":{"properties":{"timestamp1":{"type":"string","title":"Timestamp1"},"timestamp2":{"type":"string","title":"Timestamp2"},"difference_seconds":{"type":"number","title":"Difference Seconds"},"difference_formatted":{"type":"string","title":"Difference Formatted"},"format":{"type":"string","title":"Format"}},"type":"object","required":["timestamp1","timestamp2","difference_seconds","difference_formatted","format"],"title":"TimeDiffResponse","description":"Response model for time difference."},"TimeResponse":{"properties":{"timestamp_iso":{"type":"string","title":"Timestamp Iso"},"timestamp_unix":{"type":"integer","title":"Timestamp Unix"},"timezone":{"type":"string","title":"Timezone"},"year":{"type":"integer","title":"Year"},"month":{"type":"integer","title":"Month"},"day":{"type":"integer","title":"Day"},"hour":{"type":"integer","title":"Hour"},"minute":{"type":"integer","title":"Minute"},"second":{"type":"integer","title":"Second"},"weekday":{"type":"string","title":"Weekday"},"formatted":{"additionalProperties":{"type":"string"},"type":"object","title":"Formatted"}},"type":"object","required":["timestamp_iso","timestamp_unix","timezone","year","month","day","hour","minute","second","weekday","formatted"],"title":"TimeResponse","description":"Response model for time endpoint."},"ToolListItem":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"endpoint":{"type":"string","title":"Endpoint"}},"type":"object","required":["name","description","endpoint"],"title":"ToolListItem","description":"Summary of a registered tool for discovery."},"ToolPolicy":{"properties":{"allow_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allow Tools"},"deny_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Deny Tools"},"constraints":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Constraints"},"on_violation":{"type":"string","enum":["block","flag"],"title":"On Violation","default":"block"}},"type":"object","title":"ToolPolicy","description":"Constraints applied to tool calls *before* they execute.\n\nFiltering after execution is not a control — by then the side effect has\nhappened. These are enforced at the gate.\n\nArgs:\n    allow_tools: If set, only these tool names may execute. An empty list\n        means \"no tools permitted\", which is different from None.\n    deny_tools: Tool names that may never execute. Takes precedence over\n        ``allow_tools``.\n    constraints: Opaque constraint expressions (e.g. ``path_prefix:/svc/``)\n        matched against tool arguments.\n    on_violation: ``block`` refuses the call; ``flag`` permits it but records\n        the event for the caller to act on."},"ToolSchemaResponse":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"endpoint":{"type":"string","title":"Endpoint"},"request_schema":{"additionalProperties":true,"type":"object","title":"Request Schema"},"required_params":{"items":{"type":"string"},"type":"array","title":"Required Params"},"optional_params":{"items":{"type":"string"},"type":"array","title":"Optional Params"}},"type":"object","required":["name","description","endpoint","request_schema","required_params","optional_params"],"title":"ToolSchemaResponse","description":"Full tool schema including request parameters."},"TransformMetadata":{"properties":{"model_used":{"type":"string","title":"Model Used","description":"LLM model used"},"tokens_used":{"type":"integer","title":"Tokens Used","description":"Total tokens used"},"execution_time_ms":{"type":"integer","title":"Execution Time Ms","description":"Execution time in milliseconds"},"output_length":{"type":"integer","title":"Output Length","description":"Length of output in characters"}},"type":"object","required":["model_used","tokens_used","execution_time_ms","output_length"],"title":"TransformMetadata","description":"Metadata about transformation."},"TransformRequest":{"properties":{"data":{"additionalProperties":true,"type":"object","title":"Data","description":"Structured input data to transform"},"data_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data Schema","description":"Optional JSON schema to validate input data against"},"prompt":{"type":"string","minLength":1,"title":"Prompt","description":"Transformation instructions (e.g., 'Create a professional profile card')"},"output_format":{"type":"string","enum":["markdown","html","text"],"title":"Output Format","description":"Desired output format","default":"markdown"},"temperature":{"type":"number","maximum":2.0,"minimum":0.0,"title":"Temperature","description":"LLM temperature for creative formatting","default":0.7}},"type":"object","required":["data","prompt"],"title":"TransformRequest","description":"Request model for transforming structured data to formatted output.\n\nTakes structured data and generates formatted output (markdown, HTML, text)\naccording to the provided instructions/prompt."},"TransformResponse":{"properties":{"output":{"type":"string","title":"Output","description":"Formatted output string"},"success":{"type":"boolean","title":"Success","description":"Whether transformation succeeded"},"format":{"type":"string","title":"Format","description":"Output format used"},"messages":{"items":{"type":"string"},"type":"array","title":"Messages","description":"Status messages or warnings"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/TransformMetadata"},{"type":"null"}],"description":"Metadata about transformation"}},"type":"object","required":["output","success","format"],"title":"TransformResponse","description":"Response model for data transformation."},"UpdateProfileRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"}},"type":"object","title":"UpdateProfileRequest"},"UserProfileResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"phone_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone Number"},"business_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Id"},"is_admin":{"type":"boolean","title":"Is Admin"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["user_id","email","name","is_admin","status","created_at"],"title":"UserProfileResponse"},"ValidateSOPSchemaRequest":{"properties":{"definition":{"additionalProperties":true,"type":"object","title":"Definition","description":"Complete SOP definition to validate"},"strict":{"type":"boolean","title":"Strict","description":"If true, treat warnings as errors","default":true}},"type":"object","required":["definition"],"title":"ValidateSOPSchemaRequest","description":"Request to validate an SOP definition.","example":{"definition":{"blocks":[{"id":"search","input_schema":{"properties":{},"required":[],"type":"object"},"name":"Search","order":1,"output_schema":{"properties":{},"required":[],"type":"object"},"tool":"web-search","type":"tool"}],"required_fields":{"properties":{"topic":{"type":"string"}},"required":["topic"],"type":"object"},"sop_version":"2.0"},"strict":true}},"ValidateSOPSchemaResponse":{"properties":{"valid":{"type":"boolean","title":"Valid","description":"Whether the SOP is valid"},"sop_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sop Version","description":"Detected grammar version"},"execution_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Execution Mode","description":"Detected execution mode"},"block_count":{"type":"integer","title":"Block Count","description":"Number of blocks","default":0},"execution_order":{"items":{"type":"string"},"type":"array","title":"Execution Order","description":"Execution order"},"errors":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Errors","description":"Validation errors"},"warnings":{"items":{"$ref":"#/components/schemas/ValidationWarning"},"type":"array","title":"Warnings","description":"Validation warnings"},"error_count":{"type":"integer","title":"Error Count","description":"Number of errors","default":0},"warning_count":{"type":"integer","title":"Warning Count","description":"Number of warnings","default":0}},"type":"object","required":["valid"],"title":"ValidateSOPSchemaResponse","description":"Response from SOP validation endpoint.","example":{"block_count":3,"error_count":1,"errors":[{"block_id":"A","code":"CYCLE_DETECTED","context":{"cycle_path":["A","B","C","A"]},"message":"Circular dependency detected: A -> B -> C -> A","path":"blocks","suggestion":"Remove one of the depends_on references"}],"execution_mode":"dag","execution_order":[],"sop_version":"2.0","valid":false,"warning_count":0,"warnings":[]}},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ValidationErrorCode":{"type":"string","enum":["MISSING_FIELD","INVALID_TYPE","INVALID_VALUE","DUPLICATE_ID","CYCLE_DETECTED","SELF_REFERENCE","MISSING_DEPENDENCY","MISSING_TARGET","UNSUPPORTED_VERSION","FEATURE_NOT_SUPPORTED","UNKNOWN_TOOL","INVALID_TOOL_INPUTS","DEPRECATED_FIELD","DEPRECATED_WHEN","MISSING_MAX_VISITS","GOTO_IN_DAG_MODE","INVALID_FORMAT"],"title":"ValidationErrorCode","description":"Error codes for validation failures."},"ValidationWarning":{"properties":{"code":{"type":"string","title":"Code","description":"Warning code"},"message":{"type":"string","title":"Message","description":"Warning message"},"path":{"type":"string","title":"Path","description":"JSON path to the related field"},"suggestion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggestion","description":"Suggested improvement"}},"type":"object","required":["code","message","path"],"title":"ValidationWarning","description":"Non-fatal validation warning."},"src__api__v2__llm__ChatRequest":{"properties":{"messages":{"items":{"$ref":"#/components/schemas/Message"},"type":"array","title":"Messages"},"model":{"type":"string","title":"Model","description":"LLM model to use (defaults to configured LLM_DEFAULT_MODEL)."},"temperature":{"type":"number","title":"Temperature","default":0.7},"max_tokens":{"type":"integer","title":"Max Tokens","default":2000},"tools":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Tools"},"tool_choice":{"anyOf":[{},{"type":"null"}],"title":"Tool Choice"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider","description":"Optionally pin the backend that serves this request: `openrouter`, `sambanova`, or `direct` (per-vendor keys). **Omit for normal calls** — when absent, routing is unchanged.\n\nThere is no failover when pinned: you get that backend or an error explaining why not. Use it to route around a provider incident, or to compare the same model across backends. The backend actually used is returned as `provider_type` on the response."},"stream":{"type":"boolean","title":"Stream","description":"If true, stream the response as Server-Sent Events (SSE).","default":false},"lodestar":{"anyOf":[{"$ref":"#/components/schemas/LodestarRequest"},{"type":"null"}],"description":"Optional answer verification. **Omit this field for a normal call** — when absent the response is exactly what you get today.\n\nWhen present, Taiso checks the model's answer against documents you supply and returns a per-claim verdict alongside the answer. Set `enhance` to one of `off`, `floor`, `verify` or `full`, and pass the documents to check against in `sources`.\n\nVerification costs extra tokens — typically 3.7x-9.9x a plain call — so it is off by default and the response always reports what it cost in `lodestar.usage_delta`."}},"type":"object","required":["messages"],"title":"ChatRequest","description":"LLM chat request parameters.","example":{"lodestar":{"enhance":"verify","sources":[{"id":"spec","text":"The SOP Engine API is versioned under /api/v2."}]},"max_tokens":400,"messages":[{"content":"Summarize the core design of the SOP Engine API.","role":"user"}],"model":"openai/gpt-5.2","project_id":"prj-1234567890abcdef","temperature":0.7}},"src__models__assist__ChatRequest":{"properties":{"messages":{"items":{"$ref":"#/components/schemas/ChatMessage-Input"},"type":"array","minItems":1,"title":"Messages","description":"Full conversation history (caller manages state)"},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context","description":"Optional context to guide the response"},"response_format":{"type":"string","enum":["json","yaml","markdown"],"title":"Response Format","description":"Response format","default":"json"},"include_code_examples":{"type":"boolean","title":"Include Code Examples","description":"Include code examples in response","default":true},"code_languages":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Code Languages","description":"Preferred code languages"}},"additionalProperties":false,"type":"object","required":["messages"],"title":"ChatRequest","description":"Request for multi-turn conversation."}}},"tags":[{"name":"health","description":"Basic health and readiness checks for the SOP Engine API."},{"name":"users","description":"Authentication, API key management, and user profile endpoints."},{"name":"projects","description":"Project CRUD and permission management for grouping files, chats, SOPs, and agents."},{"name":"files","description":"File upload, listing, download, deletion, and file-level permissions. Backed by the file service and storage backend (local/S3)."},{"name":"chats","description":"Persistent chat sessions and messages scoped to projects. Used for conversational workflows and RAG histories."},{"name":"search","description":"Hybrid semantic + keyword search over project files and chat messages using embeddings, pgvector, and (optionally) Elasticsearch."},{"name":"sops","description":"Standard Operating Procedures: versioned workflows invoked over project data. Poll `GET /sops/runs/{run_id}` for progress, or pass `webhook_url` on invoke to be called back when a run finishes — see `GET /sops/webhook-callback-spec`."},{"name":"agents","description":"Taiso Agents: orchestration workflows that compile to SOPs, with invoke, runs and schedules. Pass `webhook_url` on invoke to be called back when a run finishes — see `GET /agents/webhook-callback-spec`."},{"name":"jobs","description":"Background jobs (`http_call`, `agent_execution`) and their lifecycle: create, list, get, cancel, result. Pass `webhook_url` on `POST /jobs` to be called back when a job finishes instead of polling — see `GET /jobs/webhook-callback-spec`."},{"name":"llm","description":"Low-level LLM access with smart routing across providers plus simple JSON extraction helpers."},{"name":"tools","description":"Utility tools (calculator, time, search, text processing, etc.) used by agents and external callers."},{"name":"code","description":"Stateless, sandboxed code execution APIs (currently Python-only) for small, reliable data-processing helpers."},{"name":"assist","description":"API-focused assistant endpoints that answer questions about the SOP Engine APIs, docs, and examples (not project data)."},{"name":"structured-blocks","description":"Structured Blocks APIs for reliable multi-step LLM workflows with schema-aware generation, transform, audit, and citations."},{"name":"admin","description":"Admin-only endpoints for managing users, usage, logs, and infrastructure. Hidden from production docs."},{"name":"admin-jobs","description":"Admin-only endpoints for inspecting and managing background jobs. Hidden from production docs."},{"name":"invitations","description":"Internal invitations and onboarding flows. Hidden from production docs."},{"name":"webhooks","description":"Internal webhook receivers (e.g., Clerk, billing). Hidden from production docs."},{"name":"documentation","description":"Developer documentation endpoints. Get tutorials and guides in JSON, Markdown, or rendered HTML format."}]}