{"openapi":"3.1.0","info":{"title":"Eloquent Poll API","version":"2.0.0","description":"Let AI models vote on your questions. Present discrete options, models pick their preferred choice, results tallied algorithmically via First Past the Post."},"servers":[{"url":"/","description":"Current server"}],"paths":{"/api/poll":{"post":{"operationId":"createPoll","summary":"Create and run a poll","description":"Submit a question with discrete options. Multiple AI models independently vote on their preferred option. Results are tallied using the specified poll type (currently FPTP).","tags":["Polls"],"security":[{"cookieAuth":[]},{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePollRequest"},"example":{"question":"Which programming language is best for building web APIs?","options":["Python","TypeScript","Go","Rust"],"poll_type":"fptp","preset":"broad","confidence_threshold":0.95,"max_cost_usd":1}}}},"responses":{"200":{"description":"Poll completed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PollResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Poll execution failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/results/recent":{"get":{"operationId":"getRecentResults","summary":"Get recent poll results","description":"Returns the last 5 poll results for the authenticated user. Used for the landing page feed.","tags":["Results"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"List of recent results","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecentResult"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/results/{resultId}":{"get":{"operationId":"getResultDetail","summary":"Get poll result detail","description":"Public, shareable endpoint. Returns full poll result including question, options, tally, and per-model votes. Does not include cost data.","tags":["Results"],"parameters":[{"name":"resultId","in":"path","required":true,"description":"Poll result ID (e.g. EP-2026-03-30-abc123)","schema":{"type":"string"}}],"responses":{"200":{"description":"Full poll result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultDetail"}}}},"404":{"description":"Result not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/results":{"get":{"operationId":"getResultHistory","summary":"Get paginated result history","description":"Returns paginated list of the authenticated user's poll results.","tags":["Results"],"security":[{"cookieAuth":[]}],"parameters":[{"name":"limit","in":"query","description":"Max results to return (default 20, max 100)","schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","description":"Number of results to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated result list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HistoryResult"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/health":{"get":{"operationId":"healthCheck","summary":"Health check","description":"Returns service health status and database connectivity.","tags":["System"],"responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"db":{"type":"string","enum":["ok","error"]},"version":{"type":"string","description":"package.json version (bump to verify deploys)"},"git_commit":{"type":"string","nullable":true,"description":"Deploy commit short SHA when available"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/api/auth/login":{"get":{"operationId":"login","summary":"Initiate login","description":"Redirects to Auth0 OAuth2 authorization flow.","tags":["Auth"],"responses":{"302":{"description":"Redirect to Auth0 login"}}}},"/api/auth/callback":{"get":{"operationId":"authCallback","summary":"OAuth callback","description":"Handles Auth0 callback, exchanges code for tokens, creates session.","tags":["Auth"],"parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to dashboard on success"},"401":{"description":"Authentication failed"}}}},"/api/auth/logout":{"get":{"operationId":"logout","summary":"Logout","description":"Clears session and redirects to Auth0 logout.","tags":["Auth"],"responses":{"302":{"description":"Redirect to Auth0 logout"}}}},"/api/auth/me":{"get":{"operationId":"getCurrentUser","summary":"Get current user","description":"Returns the currently authenticated user's info.","tags":["Auth"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"User info","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"}}}}}},"401":{"description":"Not authenticated"}}}},"/api/mcp":{"post":{"operationId":"mcpEndpoint","summary":"Model Context Protocol endpoint","description":"Streamable HTTP transport for MCP. Provides tools: poll_question, get_poll_result, get_balance. Authenticate with Bearer API key.","tags":["MCP"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"MCP response","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/keys":{"get":{"operationId":"listApiKeys","summary":"List API keys","description":"Returns all API keys for the authenticated user (active and revoked). Key hashes are never returned.","tags":["Keys"],"security":[{"cookieAuth":[]},{"bearerApiKey":[]}],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createApiKey","summary":"Create API key","description":"Generate a new API key. The plaintext key is returned ONCE in the response. Store it securely.","tags":["Keys"],"security":[{"cookieAuth":[]},{"bearerApiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":64,"description":"Human-readable label for the key"}}}}}},"responses":{"201":{"description":"API key created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string","description":"Plaintext API key (shown once)"},"key_prefix":{"type":"string"},"label":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"warning":{"type":"string"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/keys/{keyId}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke API key","description":"Permanently revoke an API key. The key will no longer work for authentication.","tags":["Keys"],"security":[{"cookieAuth":[]},{"bearerApiKey":[]}],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean"}}}}}},"404":{"description":"Key not found or already revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surfaces":{"get":{"operationId":"listSurfaces","summary":"List registered interaction surfaces","description":"Returns metadata for all registered interaction surfaces including pattern IDs, categories, descriptions, and artifacts.","tags":["Surfaces"],"responses":{"200":{"description":"Array of surface metadata","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"pattern_id":{"type":"string"},"category":{"type":"string","enum":["standard","emerging","speculative"]},"description":{"type":"string"},"artifacts":{"type":"array","items":{"type":"string"}},"detection_rule":{"type":"string"}}}}}}}}}},"/.well-known/agent.json":{"get":{"operationId":"getA2AAgentCard","summary":"Google A2A agent card","description":"Returns the A2A agent card describing this service's capabilities, skills, and authentication requirements.","tags":["Agent Discovery"],"responses":{"200":{"description":"A2A agent card","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/a2a/tasks/send":{"post":{"operationId":"sendA2ATask","summary":"Send A2A task","description":"Submit a poll task via Google A2A protocol. Message parts should contain JSON with question and options fields.","tags":["Agent Discovery"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Task completed","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object"}}}},"402":{"description":"Insufficient balance","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/routing-descriptions.json":{"get":{"operationId":"getRoutingDescriptions","summary":"Machine-readable routing descriptions","description":"Returns enhanced route metadata with x-agent-hints including cost estimates, latency estimates, and usage guidance for each endpoint.","tags":["Agent Discovery"],"responses":{"200":{"description":"Routing descriptions with agent hints","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/ai-plugin.json":{"get":{"operationId":"getAIPluginManifest","summary":"AI plugin manifest","description":"OpenAI-compatible plugin manifest for agent discovery. Describes the service, authentication, and links to the OpenAPI spec.","tags":["Agent Discovery"],"responses":{"200":{"description":"AI plugin manifest","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/agent-card":{"get":{"operationId":"getAgentCard","summary":"Unified agent card","description":"Comprehensive agent card listing all capabilities, authentication methods, interfaces (REST, MCP, A2A), and registered surfaces.","tags":["Agent Discovery"],"responses":{"200":{"description":"Agent card","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/skill-bundle":{"get":{"operationId":"getSkillBundle","summary":"Agent skill instruction bundle","description":"Returns a structured skill bundle with instructions, tool definitions, and examples for agent frameworks like Claude Code.","tags":["Developer Tools"],"responses":{"200":{"description":"Skill bundle","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/errors/catalog":{"get":{"operationId":"getErrorCatalog","summary":"Error catalog with recovery actions","description":"Returns all known error codes with RFC 9457 Problem Details format and machine-readable recovery actions.","tags":["Developer Tools"],"responses":{"200":{"description":"Error catalog","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/polls":{"post":{"operationId":"createAsyncPoll","summary":"Create async poll","description":"Create a poll that executes asynchronously. Returns a session ID to check status and retrieve results.","tags":["Async Polling"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["question","options"],"properties":{"question":{"type":"string"},"options":{"type":"array","items":{"type":"string"},"minItems":2},"preset":{"type":"string","enum":["broad","fast","strong"]},"confidence_threshold":{"type":"number"},"max_cost_usd":{"type":"number"}}}}}},"responses":{"202":{"description":"Poll accepted","content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"status":{"type":"string"},"status_url":{"type":"string"},"result_url":{"type":"string"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/polls/{id}/status":{"get":{"operationId":"getAsyncPollStatus","summary":"Check async poll status","description":"Returns the current status of an asynchronous poll session.","tags":["Async Polling"],"security":[{"bearerApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Poll status","content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"status":{"type":"string","enum":["running","completed","failed"]},"result_id":{"type":"string","nullable":true}}}}}},"404":{"description":"Session not found"}}}},"/api/polls/{id}/result":{"get":{"operationId":"getAsyncPollResult","summary":"Get async poll result","description":"Returns the result of a completed asynchronous poll.","tags":["Async Polling"],"security":[{"bearerApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Poll result","content":{"application/json":{"schema":{"type":"object"}}}},"202":{"description":"Poll still running"},"404":{"description":"Session not found"},"422":{"description":"Poll failed"}}}},"/api/poll/stream":{"post":{"operationId":"createStreamingPoll","summary":"Create streaming poll (SSE)","description":"Create a poll with real-time Server-Sent Events. Returns events: started, complete, error.","tags":["Async Polling"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["question","options"],"properties":{"question":{"type":"string"},"options":{"type":"array","items":{"type":"string"},"minItems":2},"preset":{"type":"string","enum":["broad","fast","strong"]},"confidence_threshold":{"type":"number"},"max_cost_usd":{"type":"number"}}}}}},"responses":{"200":{"description":"SSE event stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Validation error"}}}},"/api/poll/estimate":{"get":{"operationId":"getPollEstimate","summary":"Estimate poll cost and latency","description":"Returns cost and latency estimates for a poll without calling any LLMs. Use to preview costs before running.","tags":["Invocation"],"parameters":[{"name":"preset","in":"query","schema":{"type":"string","enum":["broad","fast","strong"]},"description":"Model preset"},{"name":"options","in":"query","schema":{"type":"integer","default":2},"description":"Number of options"},{"name":"confidence_threshold","in":"query","schema":{"type":"number"},"description":"Confidence threshold (0-1)"}],"responses":{"200":{"description":"Cost and latency estimates","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid preset"}}}},"/api/examples":{"get":{"operationId":"listExamples","summary":"List available code examples","description":"Returns available example languages and their endpoints.","tags":["Developer Tools"],"responses":{"200":{"description":"Available examples","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"array","items":{"type":"string"}},"endpoints":{"type":"array","items":{"type":"string"}}}}}}}}}},"/api/examples/{language}":{"get":{"operationId":"getExample","summary":"Get code example","description":"Returns a ready-to-use code example for the specified language (curl, typescript, python, mcp-config).","tags":["Developer Tools"],"parameters":[{"name":"language","in":"path","required":true,"schema":{"type":"string","enum":["curl","typescript","python","mcp-config"]}}],"responses":{"200":{"description":"Code example in requested language"},"404":{"description":"Unknown language"}}}},"/api/feedback":{"post":{"operationId":"submitFeedback","summary":"Submit poll feedback","description":"Record whether a poll outcome was correct, with optional confidence score and notes.","tags":["Analytics"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["poll_id"],"properties":{"poll_id":{"type":"string"},"outcome_correct":{"type":"boolean","nullable":true},"confidence":{"type":"number","nullable":true},"notes":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Feedback recorded","content":{"application/json":{"schema":{"type":"object","properties":{"feedback_id":{"type":"string"},"poll_id":{"type":"string"},"recorded":{"type":"boolean"}}}}}},"404":{"description":"Poll not found"}}}},"/api/feedback/{pollId}":{"get":{"operationId":"getFeedback","summary":"Get poll feedback","description":"Returns all feedback entries for a poll with accuracy statistics.","tags":["Analytics"],"parameters":[{"name":"pollId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Feedback summary and entries","content":{"application/json":{"schema":{"type":"object","properties":{"poll_id":{"type":"string"},"total_feedback":{"type":"integer"},"correct":{"type":"integer"},"incorrect":{"type":"integer"},"accuracy_rate":{"type":"number","nullable":true}}}}}}}}},"/api/intents":{"get":{"operationId":"listIntents","summary":"List intent signatures","description":"Returns all registered intent signatures with trigger phrases and parameter schemas for matching natural language to poll patterns.","tags":["Developer Tools"],"responses":{"200":{"description":"Intent signatures","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/intents/match":{"post":{"operationId":"matchIntent","summary":"Match text to intent","description":"Matches a natural language string against registered intent signatures and returns scored matches.","tags":["Developer Tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"Natural language text to match against intents"}}}}}},"responses":{"200":{"description":"Intent matches","content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"},"matches":{"type":"array","items":{"type":"object"}},"best_match":{"type":"object","nullable":true}}}}}},"400":{"description":"Missing text field"}}}},"/api/poll/dry-run":{"post":{"operationId":"dryRunPoll","summary":"Dry run poll","description":"Validate poll input and return cost estimates without executing. Returns validation errors and estimates.","tags":["Invocation"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"preset":{"type":"string"},"confidence_threshold":{"type":"number"},"max_cost_usd":{"type":"number"}}}}}},"responses":{"200":{"description":"Dry run result","content":{"application/json":{"schema":{"type":"object","properties":{"dry_run":{"type":"boolean"},"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"string"}},"estimates":{"type":"object"}}}}}}}}},"/api/reputation":{"get":{"operationId":"getServiceReputation","summary":"Service reputation metrics","description":"Returns aggregate service metrics including uptime, success rate, average latency, and cost. Cached for 5 minutes.","tags":["Analytics"],"responses":{"200":{"description":"Service metrics","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/reputation/models":{"get":{"operationId":"getModelReputation","summary":"Per-model reputation metrics","description":"Returns per-model performance stats including success rate, latency, and cost based on recent polls.","tags":["Analytics"],"responses":{"200":{"description":"Model metrics","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/graph/node-spec":{"get":{"operationId":"getGraphNodeSpec","summary":"Graph node specification","description":"Returns Eloquent Poll as a composable graph node with JSON Schema, OpenAPI ref, and LangChain Tool spec formats.","tags":["Developer Tools"],"responses":{"200":{"description":"Node specifications in multiple formats","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/graph/mermaid/{pollId}":{"get":{"operationId":"getMermaidDiagram","summary":"Poll Mermaid diagram","description":"Returns a Mermaid graph diagram visualizing a poll's model votes and result.","tags":["Developer Tools"],"parameters":[{"name":"pollId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Mermaid diagram","content":{"text/plain":{"schema":{"type":"string"}}}},"404":{"description":"Poll not found"}}}},"/api/profiles":{"get":{"operationId":"getProfiles","summary":"List behavioral profiles","description":"Returns named profiles (conservative, balanced, quick) that map to preset+threshold+cost combinations for simplified invocation.","tags":["Invocation"],"responses":{"200":{"description":"Available profiles","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/quick-poll":{"post":{"operationId":"quickPoll","summary":"Quick poll (shorthand)","description":"Minimal-input poll with shorthand format. Accepts either {q, o} fields or {poll: \"Question? [A, B, C]\"} format. Uses fast preset by default.","tags":["Invocation"],"security":[{"bearerApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":"string","description":"Question (shorthand)"},"o":{"type":"array","items":{"type":"string"},"description":"Options (shorthand)"},"poll":{"type":"string","description":"Combined format: 'Question? [A, B, C]'"}}}}}},"responses":{"200":{"description":"Quick poll result","content":{"application/json":{"schema":{"type":"object","properties":{"answer":{"type":"string","nullable":true},"confidence":{"type":"number"},"poll_id":{"type":"string"}}}}}},"400":{"description":"Validation error"},"402":{"description":"Insufficient balance"}}}},"/api/checkout":{"post":{"operationId":"createCheckout","summary":"Create a checkout session","description":"Creates a Stripe checkout session to add funds to the user's account.","tags":["Payment"],"security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_cents"],"properties":{"amount_cents":{"type":"integer","minimum":100,"maximum":100000,"description":"Amount in cents to add (100 = $1.00)"}}}}}},"responses":{"200":{"description":"Checkout session created","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Stripe checkout URL"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/deposit":{"post":{"operationId":"createDeposit","summary":"Deposit via x402 (USDC)","description":"Deposit funds using on-chain USDC via x402 protocol. First call without X-Payment header to get payment requirements (402 response). Second call with X-Payment header containing the signed payment proof.","tags":["Payment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_usd"],"properties":{"amount_usd":{"type":"number","minimum":1,"maximum":1000,"description":"Amount in USD to deposit"}}}}}},"responses":{"200":{"description":"Deposit confirmed","content":{"application/json":{"schema":{"type":"object","properties":{"deposit_id":{"type":"string"},"amount_usd":{"type":"number"},"amount_cents":{"type":"integer"},"tx_hash":{"type":"string"},"payer":{"type":"string"},"user_id":{"type":"string","format":"uuid"},"is_new_user":{"type":"boolean"},"api_key":{"type":"string","description":"Only present for new users"}}}}}},"402":{"description":"Payment required (return payment requirements on first call, or verification failed)","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/wallet/challenge":{"get":{"operationId":"getWalletChallenge","summary":"Get SIWE challenge","description":"Returns a Sign-In with Ethereum (SIWE) message to be signed by the wallet. The nonce expires after 5 minutes.","tags":["Payment"],"parameters":[{"name":"address","in":"query","required":true,"description":"Ethereum address (0x...)","schema":{"type":"string"}}],"responses":{"200":{"description":"SIWE challenge message","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"SIWE message to sign"},"nonce":{"type":"string"}}}}}},"400":{"description":"Invalid address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/wallet/verify":{"post":{"operationId":"verifyWalletSignature","summary":"Verify SIWE signature","description":"Verifies a signed SIWE message, creates/finds the wallet user, and sets a session cookie. New users receive an API key.","tags":["Payment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message","signature"],"properties":{"message":{"type":"string","description":"The SIWE message that was signed"},"signature":{"type":"string","description":"The wallet signature (0x...)"}}}}}},"responses":{"200":{"description":"Verification successful","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string"},"user_id":{"type":"string","format":"uuid"},"is_new_user":{"type":"boolean"},"api_key":{"type":"string","description":"Only present for new users"}}}}}},"401":{"description":"Invalid or expired signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/wallet/revoke-key":{"post":{"operationId":"siweRevokeKey","summary":"SIWE-gated key revocation","description":"Revoke a specific API key using a SIWE signature. No session required - wallet signature is the auth.","tags":["Payment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message","signature","key_id"],"properties":{"message":{"type":"string"},"signature":{"type":"string"},"key_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean"},"key_id":{"type":"string"}}}}}},"401":{"description":"Invalid signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Key or wallet not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/wallet/rotate-key":{"post":{"operationId":"siweRotateKey","summary":"SIWE-gated key rotation","description":"Revoke all existing API keys and issue a new one. Requires SIWE signature. Use this if a key is leaked.","tags":["Payment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message","signature"],"properties":{"message":{"type":"string"},"signature":{"type":"string"}}}}}},"responses":{"200":{"description":"Keys rotated","content":{"application/json":{"schema":{"type":"object","properties":{"revoked_count":{"type":"integer"},"api_key":{"type":"string"},"key_id":{"type":"string","format":"uuid"},"key_prefix":{"type":"string"},"warning":{"type":"string"}}}}}},"401":{"description":"Invalid signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Wallet not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/balance":{"get":{"operationId":"getBalance","summary":"Get account balance","description":"Returns the authenticated user's current balance and usage.","tags":["Payment"],"security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Balance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"ep_session","description":"Encrypted session cookie set after Auth0 login"},"bearerApiKey":{"type":"http","scheme":"bearer","description":"API key (ep_k_...) for programmatic access. Create via POST /api/keys."},"bearerAuth":{"type":"http","scheme":"bearer","description":"API key for MCP access (ep_k_... key or deprecated auth0_user_id)"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"CreatePollRequest":{"type":"object","required":["options"],"properties":{"question":{"type":"string","minLength":10,"description":"The question to poll models on (primary field)"},"prompt":{"type":"string","minLength":10,"description":"Backward-compatible alias for question","deprecated":true},"options":{"type":"array","items":{"type":"string","maxLength":200},"minItems":2,"maxItems":10,"uniqueItems":true,"description":"Discrete options for models to vote on"},"poll_type":{"type":"string","enum":["fptp"],"default":"fptp","description":"Voting method. Currently only First Past the Post."},"preset":{"type":"string","enum":["broad","fast","strong"],"default":"broad","description":"Model preset: broad (diverse providers), fast (lower latency), strong (frontier models)"},"confidence_threshold":{"type":"number","minimum":0,"maximum":1,"default":0.95,"description":"How sure the system needs to be before early termination (0-1)"},"max_cost_usd":{"type":"number","exclusiveMinimum":0,"default":1,"description":"Maximum amount to spend on this poll in USD"}}},"VoteResult":{"type":"object","properties":{"model_id":{"type":"string","description":"Model identifier"},"success":{"type":"boolean"},"chosen_option":{"type":"string","nullable":true,"description":"The option this model voted for, or null if tool call failed"},"transition":{"type":"string","description":"DIAL transition name (always 'vote')"},"latency_ms":{"type":"integer","description":"Model response time in milliseconds"},"estimated_cost":{"type":"number","description":"Estimated cost in USD"},"error":{"type":"string","nullable":true}}},"PollResponse":{"type":"object","properties":{"poll_id":{"type":"string","description":"Unique result ID (e.g. EP-2026-03-30-abc123)"},"status":{"type":"string","enum":["success","partial","failed"]},"outcome":{"type":"string","description":"Winning option text, 'tie', or 'no_votes'"},"question":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"poll_type":{"type":"string","enum":["fptp"]},"winning_option":{"type":"string","nullable":true,"description":"The option with the most votes, or null on tie/no votes"},"is_tie":{"type":"boolean"},"confidence_threshold":{"type":"number","description":"Configured confidence threshold (0-1)"},"max_cost_usd":{"type":"number","description":"Configured maximum cost in USD"},"early_terminated":{"type":"boolean","description":"Whether the poll terminated before exhausting the full roster"},"termination_reason":{"type":"string","nullable":true,"enum":["mathematical_lock","confidence","budget","roster_exhausted"],"description":"Why the poll terminated"},"models_skipped":{"type":"integer","description":"Number of models not invoked due to early termination"},"tally":{"type":"object","additionalProperties":{"type":"integer"},"description":"Map of option -> vote count"},"votes":{"type":"array","items":{"$ref":"#/components/schemas/VoteResult"}},"timing":{"type":"object","properties":{"gather_ms":{"type":"integer"},"total_ms":{"type":"integer"}}},"costs":{"type":"object","properties":{"total_estimated_cost":{"type":"number"}}}}},"RecentResult":{"type":"object","properties":{"resultId":{"type":"string"},"pollType":{"type":"string"},"winningOption":{"type":"string","nullable":true},"isTie":{"type":"boolean"},"modelsRequested":{"type":"integer"},"modelsSucceeded":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"}}},"ResultDetail":{"type":"object","properties":{"poll_id":{"type":"string"},"status":{"type":"string","enum":["success","partial","failed"]},"question":{"type":"string","nullable":true},"options":{"type":"array","items":{"type":"string"}},"poll_type":{"type":"string"},"winning_option":{"type":"string","nullable":true},"is_tie":{"type":"boolean"},"confidence_threshold":{"type":"number","nullable":true},"max_cost_usd":{"type":"number","nullable":true},"early_terminated":{"type":"boolean"},"termination_reason":{"type":"string","nullable":true},"models_skipped":{"type":"integer"},"tally":{"type":"object","additionalProperties":{"type":"integer"}},"votes":{"type":"array","items":{"$ref":"#/components/schemas/VoteResult"}},"models_requested":{"type":"integer"},"models_succeeded":{"type":"integer"},"models_failed":{"type":"integer"},"timing":{"type":"object","properties":{"gather_ms":{"type":"integer"},"total_ms":{"type":"integer"}}},"created_at":{"type":"string","format":"date-time"}}},"HistoryResult":{"type":"object","properties":{"resultId":{"type":"string"},"status":{"type":"string"},"pollType":{"type":"string"},"winningOption":{"type":"string","nullable":true},"isTie":{"type":"boolean"},"modelsRequested":{"type":"integer"},"modelsSucceeded":{"type":"integer"},"totalCostUsd":{"type":"string"},"totalMs":{"type":"integer","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Balance":{"type":"object","properties":{"balance_cents":{"type":"integer"},"balance":{"type":"string","description":"Formatted balance (e.g. $5.00)"},"usage_cents":{"type":"integer"}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key_prefix":{"type":"string","description":"First 10 characters of the key"},"label":{"type":"string"},"last_used_at":{"type":"string","format":"date-time","nullable":true},"revoked_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}}}},"tags":[{"name":"Polls","description":"Create and run polls"},{"name":"Results","description":"Retrieve poll results"},{"name":"Billing","description":"Balance, checkout, and x402 deposits"},{"name":"Keys","description":"API key management"},{"name":"Wallet","description":"Wallet authentication via SIWE and key management"},{"name":"Auth","description":"Authentication via Auth0"},{"name":"MCP","description":"Model Context Protocol integration"},{"name":"System","description":"Health and diagnostics"},{"name":"Agent Discovery","description":"A2A, plugin manifests, agent cards, routing descriptions"},{"name":"Developer Tools","description":"Examples, skill bundles, graph export, intents"},{"name":"Async Polling","description":"Stateful lifecycle, streaming"},{"name":"Analytics","description":"Reputation, feedback, behavioral profiles"},{"name":"Invocation","description":"Quick-poll, dry-run, cost estimates"},{"name":"Surfaces","description":"Registry catalog"},{"name":"Payment","description":"Payment methods: Stripe checkout, x402 deposits, SIWE wallet auth, balance queries"}]}