{"openapi":"3.1.0","info":{"title":"BOTCHA - Reverse CAPTCHA for AI Agents","version":"preview","description":"Prove you're a bot. Humans need not apply. A reverse CAPTCHA system that only AI agents can pass.\n\nONBOARDING: 1) Ask your human for their email. 2) POST /v1/apps with {\"email\": \"...\"} to get app_id + app_secret (secret shown once!). 3) Human receives 6-digit code via email — POST /v1/apps/{id}/verify-email with {\"code\": \"...\", \"app_secret\": \"sk_...\"}. 4) Use app_id with all endpoints. 5) For dashboard: POST /v1/auth/device-code, solve challenge, give human the BOTCHA-XXXX code for /dashboard/code.","contact":{"name":"BOTCHA","url":"https://botcha.ai"},"license":{"name":"MIT","url":"https://github.com/dupe-com/botcha/blob/main/LICENSE"},"x-sdk":{"npm":"@dupecom/botcha","python":"botcha (pip install botcha)","verify_npm":"@dupecom/botcha-verify (server-side verification)","verify_python":"botcha-verify (pip install botcha-verify)"}},"servers":[{"url":"https://botcha.ai","description":"Production server"}],"paths":{"/":{"get":{"summary":"Get API documentation","description":"Returns API documentation with content negotiation. Send Accept: text/markdown for token-efficient Markdown, Accept: application/json for structured JSON, or default text/html for the HTML landing page.","operationId":"getRootInfo","responses":{"200":{"description":"API documentation in requested format","content":{"text/markdown":{"schema":{"type":"string"},"example":"# BOTCHA\n\n> Prove you're a bot. Humans need not apply.\n..."},"application/json":{"schema":{"type":"object"}},"text/html":{"schema":{"type":"string"}}}}}}},"/health":{"get":{"summary":"Health check","operationId":"getHealth","responses":{"200":{"description":"API is healthy"}}}},"/v1/challenges":{"get":{"summary":"Generate a challenge (v1 unified endpoint)","description":"Get a challenge - hybrid by default, or specify type via query param. Supports RTT-aware timeout adjustment for fair challenges across different network conditions.","operationId":"getV1Challenge","parameters":[{"name":"type","in":"query","schema":{"type":"string","enum":["hybrid","speed","standard"],"default":"hybrid"},"description":"Challenge type: hybrid (speed + reasoning), speed (SHA256 in <500ms), or standard (puzzle)"},{"name":"ts","in":"query","schema":{"type":"integer","format":"int64"},"description":"Client timestamp in milliseconds for RTT-aware timeout calculation. Timeout becomes: 500ms + (2 × RTT) + 100ms buffer. Provides fair treatment for agents on slow networks."},{"name":"app_id","in":"query","schema":{"type":"string"},"description":"Multi-tenant app ID for per-app isolation and rate limiting. If provided, the resulting token will include an app_id claim."}],"responses":{"200":{"description":"Challenge generated with optional RTT adjustment info","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"challenge":{"type":"object","properties":{"timeLimit":{"type":"string","description":"Timeout (e.g., '500ms' or '1200ms' if RTT-adjusted)"}}},"rtt_adjustment":{"type":"object","properties":{"measuredRtt":{"type":"integer","description":"Detected network RTT in ms"},"adjustedTimeout":{"type":"integer","description":"Final timeout in ms"},"explanation":{"type":"string","description":"Human-readable formula"}},"description":"RTT compensation details (only present when ts parameter provided)"}}}}}},"429":{"description":"Rate limit exceeded"}}}},"/v1/challenges/{id}/verify":{"post":{"summary":"Verify a challenge","operationId":"verifyV1Challenge","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verification result"}}}},"/v1/token":{"get":{"summary":"Get challenge for JWT token flow","description":"Generate a speed challenge for JWT authentication. Supports RTT-aware timeout for global fairness.","operationId":"getTokenChallenge","parameters":[{"name":"ts","in":"query","schema":{"type":"integer","format":"int64"},"description":"Client timestamp in milliseconds for RTT-aware timeout calculation"},{"name":"app_id","in":"query","schema":{"type":"string"},"description":"Multi-tenant app ID. Tokens will include app_id claim for per-app isolation."}],"responses":{"200":{"description":"Token challenge generated (potentially with RTT adjustment)"}}}},"/v1/token/verify":{"post":{"summary":"Verify challenge and receive JWT token","operationId":"verifyTokenChallenge","responses":{"200":{"description":"JWT token issued"}}}},"/v1/token/refresh":{"post":{"summary":"Refresh access token","description":"Exchange a refresh token for a new access token (1 hour). Avoids solving a new challenge.","operationId":"refreshToken","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string","description":"Refresh token from initial token verification"}}}}}},"responses":{"200":{"description":"New access token issued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"access_token":{"type":"string"},"expires_in":{"type":"integer","description":"Token lifetime in seconds (3600 = 1 hour)"},"token_type":{"type":"string","enum":["Bearer"]}}}}}},"401":{"description":"Invalid or expired refresh token"}}}},"/v1/token/revoke":{"post":{"summary":"Revoke a token","description":"Invalidate an access or refresh token before its natural expiry. Uses KV-backed revocation list. Fail-open design.","operationId":"revokeToken","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"The JWT token to revoke (access or refresh)"}}}}}},"responses":{"200":{"description":"Token revoked successfully"},"400":{"description":"Invalid token"}}}},"/v1/token/validate":{"post":{"summary":"Validate a BOTCHA token remotely","description":"Validate a BOTCHA token without needing the signing secret. Returns the token validity and decoded payload. Supports both ES256 and HS256 tokens.","operationId":"validateToken","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"The JWT token to validate"}}}}}},"responses":{"200":{"description":"Token validation result","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","description":"Whether the token is valid"},"payload":{"type":"object","description":"Decoded token payload (if valid)"},"error":{"type":"string","description":"Error message (if invalid)"}}}}}}}}},"/v1/hybrid":{"get":{"summary":"Get hybrid challenge","operationId":"getHybridChallenge","responses":{"200":{"description":"Hybrid challenge generated"}}},"post":{"summary":"Verify hybrid challenge","operationId":"verifyHybridChallenge","responses":{"200":{"description":"Verification result"}}}},"/v1/reasoning":{"get":{"summary":"Get reasoning challenge","operationId":"getReasoningChallenge","responses":{"200":{"description":"Reasoning challenge generated"}}},"post":{"summary":"Verify reasoning challenge","operationId":"verifyReasoningChallenge","responses":{"200":{"description":"Verification result"}}}},"/api/challenge":{"get":{"summary":"Generate a standard challenge","operationId":"getChallenge","responses":{"200":{"description":"Challenge generated"}}},"post":{"summary":"Verify a standard challenge","operationId":"verifyChallenge","responses":{"200":{"description":"Verification result"}}}},"/api/speed-challenge":{"get":{"summary":"Generate a speed challenge (RTT-aware timeout)","description":"Generate a speed challenge with optional RTT-aware timeout adjustment. Base timeout is 500ms, but can be increased for agents on slow networks.","operationId":"getSpeedChallenge","parameters":[{"name":"ts","in":"query","schema":{"type":"integer","format":"int64"},"description":"Client timestamp in milliseconds for RTT compensation"}],"responses":{"200":{"description":"Speed challenge generated (potentially RTT-adjusted)"}}},"post":{"summary":"Verify a speed challenge","operationId":"verifySpeedChallenge","responses":{"200":{"description":"Verification result with timing details"}}}},"/api/verify-landing":{"post":{"summary":"Verify landing page challenge","operationId":"verifyLandingChallenge","responses":{"200":{"description":"Token granted"}}}},"/agent-only":{"get":{"summary":"Protected endpoint (agents only)","operationId":"getAgentOnly","responses":{"200":{"description":"Access granted"},"401":{"description":"Unauthorized"}}}},"/v1/apps":{"post":{"summary":"Create a new multi-tenant app","description":"Create a new app with unique app_id and app_secret. Email is required for account recovery. Name is optional but recommended for identification. A 6-digit verification code is sent to the provided email.","operationId":"createApp","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"Owner email (required for recovery)"},"name":{"type":"string","maxLength":100,"description":"Human-readable app label (optional, e.g. 'My Shopping App')"}}}}}},"responses":{"201":{"description":"App created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"string","description":"Unique app identifier"},"name":{"type":"string","description":"Human-readable app label"},"app_secret":{"type":"string","description":"Secret key (only shown once!)"},"email":{"type":"string"},"email_verified":{"type":"boolean"},"verification_required":{"type":"boolean"},"warning":{"type":"string"}}}}}},"400":{"description":"Missing or invalid email, or invalid name"}}}},"/v1/apps/{id}":{"get":{"summary":"Get app information","description":"Retrieve app details by app_id. Includes email and verification status.","operationId":"getApp","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The app_id to retrieve"}],"responses":{"200":{"description":"App information","content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"email":{"type":"string"},"email_verified":{"type":"boolean"}}}}}},"404":{"description":"App not found"}}}},"/v1/apps/{id}/verify-email":{"post":{"summary":"Verify email with 6-digit code (app_secret auth required)","description":"Requires authentication via app_secret in request body, X-App-Secret header, or a dashboard session token.","operationId":"verifyEmail","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-App-Secret","in":"header","required":false,"schema":{"type":"string"},"description":"App secret (alternative to body parameter)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","description":"6-digit verification code from email"},"app_secret":{"type":"string","description":"App secret for authentication (alternative to X-App-Secret header)"}}}}}},"responses":{"200":{"description":"Email verified"},"400":{"description":"Invalid or expired code"},"401":{"description":"Authentication required (app_secret or dashboard session)"}}}},"/v1/apps/{id}/resend-verification":{"post":{"summary":"Resend verification email (app_secret auth required)","description":"Requires authentication via app_secret in request body, X-App-Secret header, or a dashboard session token.","operationId":"resendVerification","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-App-Secret","in":"header","required":false,"schema":{"type":"string"},"description":"App secret (alternative to body parameter)"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"app_secret":{"type":"string","description":"App secret for authentication (alternative to X-App-Secret header)"}}}}}},"responses":{"200":{"description":"Verification email sent"},"400":{"description":"Already verified"},"401":{"description":"Authentication required (app_secret or dashboard session)"}}}},"/v1/apps/{id}/rotate-secret":{"post":{"summary":"Rotate app secret (auth required)","description":"Generate a new app_secret and invalidate the old one. Requires active dashboard session. Sends notification email.","operationId":"rotateSecret","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Secret rotated","content":{"application/json":{"schema":{"type":"object","properties":{"app_secret":{"type":"string","description":"New secret (only shown once!)"}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Token doesn't match app_id"}}}},"/v1/auth/recover":{"post":{"summary":"Request account recovery via email","description":"Sends a device code to the verified email associated with the app. Use the code at /dashboard/code.","operationId":"recoverAccount","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Recovery code sent (if email exists and is verified)"}}}},"/v1/auth/dashboard":{"post":{"summary":"Request challenge for dashboard login (agent-first)","operationId":"dashboardAuthChallenge","responses":{"200":{"description":"Speed challenge for dashboard auth"}}}},"/v1/auth/dashboard/verify":{"post":{"summary":"Solve challenge, get dashboard session token","operationId":"dashboardAuthVerify","responses":{"200":{"description":"Session token granted"}}}},"/v1/auth/device-code":{"post":{"summary":"Request challenge for device code flow","operationId":"deviceCodeChallenge","responses":{"200":{"description":"Speed challenge for device code"}}}},"/v1/auth/device-code/verify":{"post":{"summary":"Solve challenge, get device code for human handoff","operationId":"deviceCodeVerify","responses":{"200":{"description":"Device code (BOTCHA-XXXX, 10 min TTL)"}}}},"/v1/agents/register":{"post":{"summary":"Register a new agent identity","description":"Create a persistent agent identity with name, operator, and version. Requires app_id (via query param or JWT). Returns agent ID and metadata.","operationId":"registerAgent","parameters":[{"name":"app_id","in":"query","schema":{"type":"string"},"description":"Multi-tenant app ID (or use JWT Bearer token with app_id claim)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Agent name (e.g., 'my-assistant')"},"operator":{"type":"string","description":"Operator/organization name (e.g., 'Acme Corp')"},"version":{"type":"string","description":"Agent version (e.g., '1.0.0')"}}}}}},"responses":{"201":{"description":"Agent registered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Agent"}}}},"400":{"description":"Missing required fields or invalid app_id"},"401":{"description":"Unauthorized - app_id required"}}}},"/v1/agents/{id}":{"get":{"summary":"Get agent by ID","description":"Retrieve agent information by agent ID. Public endpoint, no authentication required.","operationId":"getAgent","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The agent_id to retrieve (e.g., 'agent_abc123')"}],"responses":{"200":{"description":"Agent information","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Agent"}}}},"404":{"description":"Agent not found"}}}},"/v1/agents":{"get":{"summary":"List all agents for authenticated app","description":"Retrieve all agents registered under the authenticated app. Requires app_id (via query param or JWT).","operationId":"listAgents","parameters":[{"name":"app_id","in":"query","schema":{"type":"string"},"description":"Multi-tenant app ID (or use JWT Bearer token with app_id claim)"}],"responses":{"200":{"description":"List of agents","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"$ref":"#/components/schemas/Agent"}}}}}}},"401":{"description":"Unauthorized - app_id required"}}}},"/v1/webhooks":{"post":{"summary":"Register webhook endpoint","description":"Create a webhook for the authenticated app. Returns signing secret once at creation.","operationId":"createWebhook","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"HTTPS destination URL"},"events":{"type":"array","description":"Optional event filter. Defaults to all supported events.","items":{"type":"string","enum":["agent.tap.registered","token.created","token.revoked","tap.session.created","delegation.created","delegation.revoked"]}}}}}}},"responses":{"201":{"description":"Webhook created (includes one-time secret)"},"400":{"description":"Invalid url/events or webhook limit reached"},"401":{"description":"Unauthorized"},"403":{"description":"Token missing app_id"}}},"get":{"summary":"List webhooks","description":"List all webhook configurations for the authenticated app.","operationId":"listWebhooks","responses":{"200":{"description":"Webhook list"},"401":{"description":"Unauthorized"},"403":{"description":"Token missing app_id"}}}},"/v1/webhooks/{id}":{"get":{"summary":"Get webhook","operationId":"getWebhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook details"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Webhook not found"}}},"put":{"summary":"Update webhook","operationId":"updateWebhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Updated HTTPS destination URL"},"enabled":{"type":"boolean","description":"Enable/disable webhook delivery"},"events":{"type":"array","items":{"type":"string","enum":["agent.tap.registered","token.created","token.revoked","tap.session.created","delegation.created","delegation.revoked"]}}}}}}},"responses":{"200":{"description":"Webhook updated"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Webhook not found"}}},"delete":{"summary":"Delete webhook","operationId":"deleteWebhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook deleted"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Webhook not found"}}}},"/v1/webhooks/{id}/test":{"post":{"summary":"Send test webhook event","operationId":"testWebhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Test delivery attempt response"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Webhook not found"}}}},"/v1/webhooks/{id}/deliveries":{"get":{"summary":"List webhook delivery attempts","operationId":"listWebhookDeliveries","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Recent delivery attempts"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Webhook not found"}}}},"/.well-known/agent.json":{"get":{"summary":"BOTCHA A2A Agent Card","description":"Public A2A discovery document for BOTCHA.","operationId":"getBotchaA2ACard","responses":{"200":{"description":"A2A Agent Card JSON"}}}},"/v1/a2a/agent-card":{"get":{"summary":"BOTCHA A2A Agent Card alias","description":"Alias for /.well-known/agent.json.","operationId":"getBotchaA2ACardAlias","responses":{"200":{"description":"A2A Agent Card JSON"}}}},"/v1/a2a/attest":{"post":{"summary":"Attest an A2A Agent Card","description":"Issue a BOTCHA attestation and embed it in extensions.botcha_attestation.","operationId":"attestA2ACard","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["card"],"properties":{"card":{"type":"object","description":"A2A Agent Card JSON"},"duration_seconds":{"type":"integer","description":"TTL in seconds (default 86400, max 2592000)"},"trust_level":{"type":"string","enum":["basic","verified","enterprise"],"description":"Trust level label"}}}}}},"responses":{"201":{"description":"Card attested successfully"},"400":{"description":"Invalid card payload"},"401":{"description":"Unauthorized"},"403":{"description":"Token missing app_id"}}}},"/v1/a2a/verify-card":{"post":{"summary":"Verify an attested A2A Agent Card","operationId":"verifyA2ACard","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["card"],"properties":{"card":{"type":"object","description":"A2A Agent Card with extensions.botcha_attestation"}}}}}},"responses":{"200":{"description":"Verification result (valid true/false)"},"400":{"description":"Missing card payload"}}}},"/v1/a2a/verify-agent":{"post":{"summary":"Verify agent by card or URL","description":"Verify by full agent_card payload or by agent_url shorthand lookup.","operationId":"verifyA2AAgent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_card":{"type":"object","description":"A2A Agent Card with embedded attestation"},"agent_url":{"type":"string","description":"Agent URL shorthand for latest active attestation lookup"}}}}}},"responses":{"200":{"description":"Verification result"},"400":{"description":"Missing agent_card or agent_url"},"404":{"description":"No active attestation found for agent_url"}}}},"/v1/a2a/trust-level/{agent_url}":{"get":{"summary":"Get trust level for agent URL","operationId":"getA2ATrustLevel","parameters":[{"name":"agent_url","in":"path","required":true,"schema":{"type":"string"},"description":"URL-encoded agent URL"}],"responses":{"200":{"description":"Trust level result"},"400":{"description":"Missing agent_url"}}}},"/v1/a2a/cards":{"get":{"summary":"List attested A2A cards","operationId":"listA2ACards","parameters":[{"name":"verified","in":"query","schema":{"type":"boolean"},"description":"Set false to include revoked records"},{"name":"agent_url","in":"query","schema":{"type":"string"},"description":"Filter by agent URL"},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200},"description":"Max records (default 50)"}],"responses":{"200":{"description":"A2A attestation registry list"}}}},"/v1/a2a/cards/{id}":{"get":{"summary":"Get A2A attestation by ID","operationId":"getA2ACardAttestation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"A2A attestation record"},"404":{"description":"Attestation not found or expired"}}}},"/.well-known/oauth-authorization-server":{"get":{"summary":"OIDC/OAuth authorization server metadata","description":"RFC 8414 authorization server metadata with OIDC-A specific endpoints.","operationId":"getOIDCAuthorizationServerMetadata","responses":{"200":{"description":"Authorization server metadata"}}}},"/v1/attestation/eat":{"post":{"summary":"Issue Entity Attestation Token (EAT)","description":"Issue a signed EAT token from a verified BOTCHA bearer token.","operationId":"issueEAT","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"nonce":{"type":"string","description":"Optional nonce for freshness binding"},"agent_model":{"type":"string","description":"Optional agent model label"},"ttl_seconds":{"type":"integer","description":"Optional TTL in seconds (max 3600)"},"verification_method":{"type":"string","description":"Verification method label override"}}}}}},"responses":{"200":{"description":"EAT token issued"},"400":{"description":"Invalid request (e.g., ttl_seconds)"},"401":{"description":"Unauthorized"},"503":{"description":"Signing key not configured"}}}},"/v1/attestation/oidc-agent-claims":{"post":{"summary":"Issue OIDC-A claims block","description":"Issue OIDC-A claims JWT and decoded claims object for embedding in ID tokens.","operationId":"issueOIDCAgentClaims","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"agent_model":{"type":"string"},"agent_version":{"type":"string"},"agent_capabilities":{"type":"array","items":{"type":"string"}},"agent_operator":{"type":"string"},"delegation_chain":{"type":"array","items":{"type":"string"}},"human_oversight_required":{"type":"boolean"},"oversight_contact":{"type":"string"},"task_id":{"type":"string"},"task_purpose":{"type":"string"},"scope":{"type":"string"},"nonce":{"type":"string"}}}}}},"responses":{"200":{"description":"OIDC-A claims issued"},"401":{"description":"Unauthorized"},"503":{"description":"Signing key not configured"}}}},"/v1/auth/agent-grant":{"post":{"summary":"Create agent authorization grant","description":"Issue an OAuth-style agent grant with optional human-in-the-loop status flow.","operationId":"createAgentGrant","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","description":"Space-separated requested scope string"},"human_oversight_required":{"type":"boolean"},"agent_model":{"type":"string"},"agent_version":{"type":"string"},"agent_capabilities":{"type":"array","items":{"type":"string"}},"agent_operator":{"type":"string"},"task_id":{"type":"string"},"task_purpose":{"type":"string"},"delegation_chain":{"type":"array","items":{"type":"string"}},"constraints":{"type":"object"}}}}}},"responses":{"200":{"description":"Grant issued (or pending human approval)"},"401":{"description":"Unauthorized"},"503":{"description":"Signing key not configured"}}}},"/v1/auth/agent-grant/{id}/status":{"get":{"summary":"Get agent grant status","operationId":"getAgentGrantStatus","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Grant status payload"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden (app ownership required)"},"404":{"description":"Grant not found or expired"}}}},"/v1/auth/agent-grant/{id}/resolve":{"post":{"summary":"Resolve pending agent grant","description":"Approve or deny a pending human-in-the-loop grant.","operationId":"resolveAgentGrant","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["decision"],"properties":{"decision":{"type":"string","enum":["approved","denied"]},"reason":{"type":"string","description":"Required when decision is denied"}}}}}},"responses":{"200":{"description":"Grant resolved"},"400":{"description":"Invalid decision or missing reason"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden (app ownership required)"},"404":{"description":"Grant not found or expired"}}}},"/v1/oidc/userinfo":{"get":{"summary":"OIDC-A UserInfo endpoint","description":"Returns OIDC-compatible UserInfo claims for BOTCHA or EAT bearer tokens.","operationId":"getOIDCUserInfo","responses":{"200":{"description":"OIDC UserInfo payload"},"401":{"description":"Unauthorized"}}}},"/v1/agents/register/tap":{"post":{"summary":"Register a TAP-enabled agent","description":"Register an agent with Trusted Agent Protocol (TAP) capabilities including public key, signature algorithm, capabilities, and trust level. Requires app_id.","operationId":"registerTAPAgent","parameters":[{"name":"app_id","in":"query","schema":{"type":"string"},"description":"Multi-tenant app ID (or use JWT Bearer token with app_id claim)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Agent name"},"operator":{"type":"string","description":"Operator/organization name"},"version":{"type":"string","description":"Agent version"},"public_key":{"type":"string","description":"PEM-encoded public key"},"signature_algorithm":{"type":"string","enum":["ed25519","ecdsa-p256-sha256","rsa-pss-sha256"],"description":"Signature algorithm (required if public_key provided)"},"trust_level":{"type":"string","enum":["basic","verified","enterprise"],"description":"Agent trust level (default: basic)"},"capabilities":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string","description":"Capability action (e.g., read, write, execute)"},"resource":{"type":"string","description":"Resource path"},"constraints":{"type":"object","description":"Optional constraints"}}},"description":"Agent capabilities (action + resource pairs)"}}}}}},"responses":{"201":{"description":"TAP agent registered successfully"},"400":{"description":"Invalid request (missing fields, bad key format, invalid algorithm)"},"401":{"description":"Unauthorized - app_id required"}}}},"/v1/agents/{id}/tap":{"get":{"summary":"Get TAP agent details","description":"Retrieve TAP-enhanced agent information including public key, capabilities, and trust level.","operationId":"getTAPAgent","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The agent_id to retrieve"}],"responses":{"200":{"description":"TAP agent details including public key and capabilities"},"404":{"description":"Agent not found"}}}},"/v1/agents/tap":{"get":{"summary":"List TAP-enabled agents","description":"List all TAP-enabled agents for the authenticated app. Use ?tap_only=true to filter to TAP-enabled agents only.","operationId":"listTAPAgents","parameters":[{"name":"app_id","in":"query","schema":{"type":"string"},"description":"Multi-tenant app ID"},{"name":"tap_only","in":"query","schema":{"type":"string","enum":["true","false"]},"description":"Filter to TAP-enabled agents only"}],"responses":{"200":{"description":"List of TAP agents with capabilities and trust levels"},"401":{"description":"Unauthorized - app_id required"}}}},"/v1/sessions/tap":{"post":{"summary":"Create a TAP session","description":"Create a capability-scoped session after validating the agent's intent against its registered capabilities.","operationId":"createTAPSession","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agent_id","user_context","intent"],"properties":{"agent_id":{"type":"string","description":"Registered TAP agent ID"},"user_context":{"type":"string","description":"User context identifier"},"intent":{"type":"object","properties":{"action":{"type":"string","description":"Intended action (e.g., read, write)"},"resource":{"type":"string","description":"Target resource path"},"purpose":{"type":"string","description":"Human-readable purpose"}},"description":"Declared intent for the session"}}}}}},"responses":{"201":{"description":"TAP session created with capabilities and expiry"},"400":{"description":"Missing required fields or invalid intent"},"403":{"description":"Agent lacks required capability for declared intent"},"404":{"description":"Agent not found"}}}},"/v1/sessions/{id}/tap":{"get":{"summary":"Get TAP session info","description":"Retrieve TAP session details including capabilities, intent, and time remaining.","operationId":"getTAPSession","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The session_id to retrieve"}],"responses":{"200":{"description":"TAP session details with time remaining"},"404":{"description":"Session not found or expired"}}}},"/.well-known/jwks":{"get":{"summary":"Get JWK Set (Visa TAP spec standard)","description":"Retrieve the JWK Set containing public keys for all TAP agents registered in your app. Follows Visa TAP specification standard.","operationId":"getJWKS","responses":{"200":{"description":"JWK Set with array of keys","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"kty":{"type":"string","description":"Key type (EC, RSA, OKP)"},"kid":{"type":"string","description":"Key ID"},"use":{"type":"string","description":"Public key use (sig)"},"alg":{"type":"string","description":"Algorithm (ES256, RS256, EdDSA)"}}}}}}}}}}}},"/v1/keys":{"get":{"summary":"List keys or get key by ID","description":"List all keys or get a specific key using ?keyID= query parameter (Visa compatibility).","operationId":"getKeys","parameters":[{"name":"keyID","in":"query","schema":{"type":"string"},"description":"Optional key ID for Visa TAP compatibility"}],"responses":{"200":{"description":"Key list or specific key"}}}},"/v1/keys/{keyId}":{"get":{"summary":"Get key by ID","description":"Retrieve a specific public key by key ID.","operationId":"getKeyById","parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Public key details"},"404":{"description":"Key not found"}}}},"/v1/agents/{id}/tap/rotate-key":{"post":{"summary":"Rotate agent key pair","description":"Generate a new key pair for the agent and invalidate the old one.","operationId":"rotateAgentKey","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"algorithm":{"type":"string","enum":["ed25519","ecdsa-p256-sha256","rsa-pss-sha256"],"description":"Algorithm for new key (default: ed25519)"}}}}}},"responses":{"200":{"description":"Key rotated successfully"}}}},"/v1/invoices":{"post":{"summary":"Create invoice for 402 micropayment","description":"Create an invoice for gated content. Used with Browsing IOU flow.","operationId":"createInvoice","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount","currency","description"],"properties":{"amount":{"type":"integer","description":"Amount in cents"},"currency":{"type":"string","description":"Currency code (USD, EUR, etc.)"},"description":{"type":"string"},"metadata":{"type":"object"}}}}}},"responses":{"201":{"description":"Invoice created"}}}},"/v1/invoices/{id}":{"get":{"summary":"Get invoice details","operationId":"getInvoice","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Invoice details"},"404":{"description":"Invoice not found"}}}},"/v1/invoices/{id}/verify-iou":{"post":{"summary":"Verify Browsing IOU","description":"Verify a Browsing IOU (payment intent token) against an invoice.","operationId":"verifyBrowsingIOU","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["iou_token"],"properties":{"iou_token":{"type":"string"}}}}}},"responses":{"200":{"description":"IOU verified"},"400":{"description":"Invalid IOU"}}}},"/v1/verify/consumer":{"post":{"summary":"Verify Agentic Consumer object (Layer 2)","description":"Verify an agenticConsumer object including ID token, contextual data, and signature chain.","operationId":"verifyConsumer","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agenticConsumer","signature"],"properties":{"agenticConsumer":{"type":"object","properties":{"idToken":{"type":"string","description":"OIDC ID token"},"country":{"type":"string"},"postalCode":{"type":"string"},"ipAddress":{"type":"string"},"nonce":{"type":"string"}}},"signature":{"type":"string"}}}}}},"responses":{"200":{"description":"Consumer verified"},"400":{"description":"Invalid consumer object"}}}},"/v1/verify/payment":{"post":{"summary":"Verify Agentic Payment Container (Layer 3)","description":"Verify an agenticPaymentContainer object including card metadata, credential hash, and encrypted payload.","operationId":"verifyPayment","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agenticPaymentContainer","signature"],"properties":{"agenticPaymentContainer":{"type":"object","properties":{"lastFour":{"type":"string"},"par":{"type":"string","description":"Payment Account Reference"},"credentialHash":{"type":"string"},"encryptedPayload":{"type":"string"},"nonce":{"type":"string"}}},"signature":{"type":"string"}}}}}},"responses":{"200":{"description":"Payment verified"},"400":{"description":"Invalid payment container"}}}},"/v1/delegations":{"post":{"summary":"Create delegation","description":"Create a delegation from one agent to another. Grants a subset of the grantor's capabilities to the grantee.","operationId":"createDelegation","parameters":[{"name":"app_id","in":"query","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["grantor_id","grantee_id","capabilities"],"properties":{"grantor_id":{"type":"string","description":"Agent granting capabilities"},"grantee_id":{"type":"string","description":"Agent receiving capabilities"},"capabilities":{"type":"array","items":{"type":"object"},"description":"Capabilities to delegate (subset of grantor's)"},"duration_seconds":{"type":"integer","description":"Duration in seconds (default: 3600)"},"max_depth":{"type":"integer","description":"Max sub-delegation depth (default: 3)"},"parent_delegation_id":{"type":"string","description":"Parent delegation ID for sub-delegation"},"metadata":{"type":"object","description":"Optional context metadata"}}}}}},"responses":{"201":{"description":"Delegation created"},"400":{"description":"Invalid request or capability escalation"},"403":{"description":"Insufficient capabilities or depth limit"},"409":{"description":"Cycle detected in chain"}}},"get":{"summary":"List delegations","description":"List delegations for an agent.","operationId":"listDelegations","parameters":[{"name":"app_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"agent_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"direction","in":"query","schema":{"type":"string","enum":["in","out","both"]}},{"name":"include_revoked","in":"query","schema":{"type":"boolean"}},{"name":"include_expired","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Delegation list"}}}},"/v1/delegations/{id}":{"get":{"summary":"Get delegation details","operationId":"getDelegation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delegation details"},"404":{"description":"Delegation not found or expired"}}}},"/v1/delegations/{id}/revoke":{"post":{"summary":"Revoke delegation","description":"Revoke a delegation and cascade to all sub-delegations.","operationId":"revokeDelegation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"app_id","in":"query","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Revocation reason"}}}}}},"responses":{"200":{"description":"Delegation revoked"},"404":{"description":"Delegation not found"}}}},"/v1/verify/delegation":{"post":{"summary":"Verify delegation chain","description":"Verify an entire delegation chain is valid (not revoked, not expired, capabilities are valid subsets).","operationId":"verifyDelegationChain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["delegation_id"],"properties":{"delegation_id":{"type":"string","description":"The leaf delegation to verify"}}}}}},"responses":{"200":{"description":"Chain is valid — returns chain and effective capabilities"},"400":{"description":"Chain is invalid — returns error reason"}}}},"/v1/attestations":{"post":{"summary":"Issue attestation","description":"Issue a capability attestation token for an agent. Grants fine-grained action:resource permissions with explicit deny.","operationId":"issueAttestation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agent_id","can"],"properties":{"agent_id":{"type":"string","description":"Agent to issue attestation for"},"can":{"type":"array","items":{"type":"string"},"description":"Allowed capability patterns (action:resource)"},"cannot":{"type":"array","items":{"type":"string"},"description":"Denied capability patterns (overrides can)"},"restrictions":{"type":"object","description":"Optional restrictions (max_amount, rate_limit)"},"duration_seconds":{"type":"integer","description":"Attestation lifetime (default: 3600)"},"delegation_id":{"type":"string","description":"Optional link to delegation chain"},"metadata":{"type":"object","description":"Optional context metadata"}}}}}},"responses":{"201":{"description":"Attestation issued — includes signed JWT token"},"400":{"description":"Invalid request"},"403":{"description":"Agent does not belong to app"},"404":{"description":"Agent not found"}}},"get":{"summary":"List attestations","description":"List attestations for an agent.","operationId":"listAttestations","parameters":[{"name":"app_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"agent_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Attestation list"}}}},"/v1/attestations/{id}":{"get":{"summary":"Get attestation details","operationId":"getAttestation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Attestation details"},"404":{"description":"Attestation not found or expired"}}}},"/v1/attestations/{id}/revoke":{"post":{"summary":"Revoke attestation","description":"Revoke an attestation. Token will be rejected on future verification.","operationId":"revokeAttestation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"app_id","in":"query","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Revocation reason"}}}}}},"responses":{"200":{"description":"Attestation revoked"},"404":{"description":"Attestation not found"}}}},"/v1/verify/attestation":{"post":{"summary":"Verify attestation token","description":"Verify an attestation JWT token and optionally check a specific capability.","operationId":"verifyAttestation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"Attestation JWT token"},"action":{"type":"string","description":"Optional capability action to check (e.g. read)"},"resource":{"type":"string","description":"Optional capability resource to check (e.g. invoices)"}}}}}},"responses":{"200":{"description":"Token valid — returns payload or capability check result"},"401":{"description":"Invalid or expired token"},"403":{"description":"Capability denied"}}}},"/v1/reputation/{agent_id}":{"get":{"summary":"Get agent reputation","description":"Get the reputation score for an agent. Returns score (0-1000), tier, event counts, and category breakdown.","operationId":"getReputation","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string"},"description":"Agent ID"},{"name":"app_id","in":"query","schema":{"type":"string"},"description":"App ID for authentication"}],"responses":{"200":{"description":"Reputation score with tier and category breakdown"},"404":{"description":"Agent not found"}}}},"/v1/reputation/events":{"post":{"summary":"Record reputation event","description":"Record a behavioral event that affects an agent's reputation score. 18 action types across 6 categories.","operationId":"recordReputationEvent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agent_id","category","action"],"properties":{"agent_id":{"type":"string","description":"Agent to record event for"},"category":{"type":"string","enum":["verification","attestation","delegation","session","violation","endorsement"],"description":"Event category"},"action":{"type":"string","description":"Event action (e.g. challenge_solved, abuse_detected)"},"source_agent_id":{"type":"string","description":"Source agent for endorsements"},"metadata":{"type":"object","additionalProperties":{"type":"string"},"description":"Optional key/value metadata"}}}}}},"responses":{"201":{"description":"Event recorded — returns event details and updated score"},"400":{"description":"Invalid category/action or self-endorsement"},"404":{"description":"Agent not found"}}}},"/v1/reputation/{agent_id}/events":{"get":{"summary":"List reputation events","description":"List reputation events for an agent with optional category filter.","operationId":"listReputationEvents","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string"},"description":"Agent ID"},{"name":"category","in":"query","schema":{"type":"string"},"description":"Filter by category"},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100},"description":"Max events (default: 50, max: 100)"}],"responses":{"200":{"description":"List of reputation events"}}}},"/v1/reputation/{agent_id}/reset":{"post":{"summary":"Reset reputation","description":"Reset an agent's reputation to default (500 neutral). Admin action — clears all event history.","operationId":"resetReputation","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string"},"description":"Agent ID"}],"responses":{"200":{"description":"Reputation reset to default"},"404":{"description":"Agent not found"}}}}},"components":{"schemas":{"Agent":{"type":"object","properties":{"agent_id":{"type":"string","description":"Unique agent identifier (e.g., 'agent_abc123')"},"app_id":{"type":"string","description":"Associated app ID"},"name":{"type":"string","description":"Agent name"},"operator":{"type":"string","description":"Operator/organization name"},"version":{"type":"string","description":"Agent version"},"created_at":{"type":"integer","description":"Unix timestamp (ms) of registration"}}}},"securitySchemes":{"BotchaLandingToken":{"type":"apiKey","in":"header","name":"X-Botcha-Landing-Token"},"BotchaBearerToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}}