{"openapi":"3.1.0","info":{"title":"Cactus API","version":"1.0.0","description":"Every address https://heycactus.ai answers for a program: its sign-in, the team's A2A address, the team's MCP address on https://mcp.heycactus.ai, and the files that describe Cactus to an agent. A product's public help has its own description on its help site; Cactus's own is https://cactus.heycactus.ai/openapi.json.","contact":{"name":"Cactus","email":"support@heycactus.ai","url":"https://heycactus.ai"}},"externalDocs":{"description":"How an agent signs in here","url":"https://heycactus.ai/auth.md"},"servers":[{"url":"https://heycactus.ai"}],"paths":{"/a2a":{"servers":[{"url":"https://heycactus.ai"},{"url":"https://mcp.heycactus.ai"}],"post":{"operationId":"askTeamA2a","summary":"Ask about your team's product over A2A","description":"The team's A2A address, here and on the MCP host: send a question about one of the team's products as text, and the reply is one answer drawn from everything the team can see, with the pages it cites. A2A 1.0 over JSON-RPC: send `A2A-Version: 1.0` and `SendMessage`. Nothing streams and no task is kept. An answer that takes longer than a few minutes comes back as a link to the conversation, where it is written when it is done.","security":[{"cactus":["cactus:ask"]},{"cactusMcp":["cactus:ask"]}],"parameters":[{"name":"A2A-Version","in":"header","schema":{"const":"1.0"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/A2aRequest"}}}},"responses":{"200":{"description":"The reply, or a JSON-RPC error for a request this agent does not run or cannot read.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/A2aReply"},{"$ref":"#/components/schemas/JsonRpcError"}]}}}},"401":{"description":"Signing in is needed, or the bearer could not be read.","headers":{"WWW-Authenticate":{"description":"`Bearer` with `resource_metadata`, the address of this resource's protected-resource metadata, and the scope it takes.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcError"},{"$ref":"#/components/schemas/Error"}]},"example":{"error":"invalid_token"}}}},"403":{"description":"The token cannot ask: it needs cactus:ask.","headers":{"WWW-Authenticate":{"description":"`Bearer` with `resource_metadata`, the address of this resource's protected-resource metadata, and the scope it takes.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcError"}}}},"429":{"description":"Too many questions. `Retry-After` says when to ask again.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcError"}}}}}}},"/mcp":{"servers":[{"url":"https://mcp.heycactus.ai"}],"post":{"operationId":"callTeamMcp","summary":"Cactus for your team over MCP","description":"The team's MCP address: every product on the team, read and asked by a member signed in. MCP 2025-06-18 over streamable HTTP: POST JSON-RPC with `Accept: application/json, text/event-stream`. Its tools: `list_products`, `search_help`, `read_page`, `list_topics`, `read_timeline`, `search_conversations`, `read_conversation`, `search`, `fetch`, `ask`, `start_investigation`, `get_investigation`.","security":[{"cactusMcp":["cactus:read"]},{"cactusMcp":["cactus:ask"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpRequest"}}}},"responses":{"200":{"description":"The JSON-RPC answer, as JSON or as a stream of server-sent events.","content":{"application/json":{"schema":{"type":"object"}},"text/event-stream":{"schema":{"type":"string"}}}},"202":{"description":"A notification, taken."},"401":{"description":"The bearer could not be read. A request with no bearer gets the same status and challenge, with no body.","headers":{"WWW-Authenticate":{"description":"`Bearer` with `resource_metadata`, the address of this resource's protected-resource metadata, and the scope it takes.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_token"}}}},"403":{"description":"Sent with a bearer from a browser page whose origin may not use it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"forbidden_origin"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"getAuthorizationServerMetadata","summary":"The sign-in's metadata","description":"RFC 8414 metadata for this host as an issuer, with auth.md's `agent_auth` block.","security":[],"responses":{"200":{"description":"The sign-in's metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationServerMetadata"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"getProtectedResourceMetadata","summary":"What a token for this host is","description":"RFC 9728 metadata: https://heycactus.ai as a protected resource, its issuer and its scopes.","security":[],"responses":{"200":{"description":"What a token for this host is","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProtectedResourceMetadata"}}}}}}},"/oauth/authorize":{"get":{"operationId":"authorize","summary":"Start a browser sign-in","description":"The authorization code flow's first step, in a person's browser. It sends them to the consent page on app.heycactus.ai, and from there back to the client's redirect URI with a code.","security":[],"parameters":[{"name":"response_type","in":"query","required":true,"schema":{"const":"code"}},{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri"}},{"name":"code_challenge","in":"query","required":true,"schema":{"type":"string"}},{"name":"code_challenge_method","in":"query","required":true,"schema":{"const":"S256"}},{"name":"scope","in":"query","schema":{"type":"string","description":"Space-separated. Both scopes when left out."}},{"name":"state","in":"query","schema":{"type":"string"}},{"name":"resource","in":"query","schema":{"type":"string","format":"uri"}}],"responses":{"302":{"description":"To the consent page.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"400":{"description":"The request, its client or its redirect URI did not check out, so nothing is sent to that address. A browser gets a page that says so; any other caller gets JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","error_description":"The authorization request was not valid."}},"text/html":{"schema":{"type":"string"}}}}}}},"/oauth/token":{"post":{"operationId":"token","summary":"Exchange a grant for a token, or revoke one","description":"RFC 6749's token endpoint, with auth.md's two grants: `urn:ietf:params:oauth:grant-type:jwt-bearer` exchanges an agent's identity assertion, and `urn:workos:agent-auth:grant-type:claim` polls for the token a claimed agent gets. A POST with only `token=<access_token>` revokes an agent's own token (RFC 7009) and answers 200 with no body.","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"A token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"400":{"description":"The grant was refused: `invalid_request`, `invalid_grant`, `authorization_pending` while the person has not typed the code, `expired_token` once it has run out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"authorization_pending","error_description":"The person has not typed the code yet."}}}},"401":{"description":"The client could not be identified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_client","error_description":"Client ID is required"}}}},"403":{"description":"Sent from a browser page whose origin may not use this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"forbidden_origin"}}}}}}},"/oauth/register":{"post":{"operationId":"registerClient","summary":"Register an OAuth client","description":"RFC 7591 dynamic client registration. A registration lasts thirty days.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientMetadata"}}}},"responses":{"201":{"description":"The client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientInformation"}}}},"400":{"description":"The metadata was not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_client_metadata","error_description":"Invalid client metadata"}}}},"429":{"description":"Too many registrations from this address this hour. `Retry-After` says when to try again.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"temporarily_unavailable","error_description":"Too many registrations from this address."}}}}}}},"/oauth/agent/identity":{"post":{"operationId":"registerAgent","summary":"Register an agent with no browser","description":"auth.md's registration. `anonymous` answers an identity assertion at once; `service_auth` names the person up front and answers the claim code with it. Ten registrations an hour from one address.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRegistrationRequest"}}}},"responses":{"200":{"description":"The registration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRegistration"}}}},"400":{"description":"The body was refused: `invalid_request`, `invalid_login_hint`, or `issuer_not_enabled` for `identity_assertion`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","error_description":"`type` must be anonymous or service_auth."}}}},"403":{"description":"Sent from a browser page whose origin may not use this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"forbidden_origin"}}}},"429":{"description":"Too many registrations from this address this hour. `Retry-After` says when to try again.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited","error_description":"Too many registrations from this address."}}}}}}},"/oauth/agent/identity/claim":{"post":{"operationId":"claimAgent","summary":"Ask for a code the agent's person types to claim it","description":"The agent names its person's email and gets a six-digit code and the page to type it on. A code lasts ten minutes and takes five wrong tries.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimRequest"}}}},"responses":{"200":{"description":"The claim has started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimStarted"}}}},"400":{"description":"The body was refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","error_description":"The body must carry `claim_token` and the `email` of the person the agent acts for."}}}},"401":{"description":"The claim token is not known.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_claim_token","error_description":"This claim token is not known."}}}},"403":{"description":"Sent from a browser page whose origin may not use this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"forbidden_origin"}}}},"409":{"description":"A person has already claimed this agent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"claimed_or_in_flight"}}}},"410":{"description":"The registration has expired. Register again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"claim_expired"}}}},"429":{"description":"Too many codes for this agent. Register again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This description","description":"Every address this host answers for a program, in OpenAPI 3.1.","security":[],"responses":{"200":{"description":"This description","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/llms.txt":{"get":{"operationId":"getLlmsTxt","summary":"What is here, for a language model","description":"When to read this site, and every page with a line on what it answers.","security":[],"responses":{"200":{"description":"What is here, for a language model","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/llms-full.txt":{"get":{"operationId":"getLlmsFullTxt","summary":"Every page in one file","description":"The whole site as markdown, for a model that wants all of it at once.","security":[],"responses":{"200":{"description":"Every page in one file","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/auth.md":{"get":{"operationId":"getAuthMd","summary":"How an agent signs in here","description":"What needs a sign-in and what does not, and every step of signing in with or without a browser.","security":[],"responses":{"200":{"description":"How an agent signs in here","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/.well-known/api-catalog":{"get":{"operationId":"getApiCatalog","summary":"The API catalog","description":"RFC 9727: each address here and the document that describes it.","security":[],"responses":{"200":{"description":"The API catalog","content":{"application/linkset+json":{"schema":{"type":"object","required":["linkset"],"properties":{"linkset":{"type":"array","items":{"type":"object"}}}}}}}}}},"/.well-known/ai-catalog.json":{"get":{"operationId":"getAiCatalog","summary":"The AI catalog","description":"What an agent can reach here and ask, with example questions.","security":[],"responses":{"200":{"description":"The AI catalog","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/mcp/server-card.json":{"get":{"operationId":"getMcpServerCard","summary":"The MCP server card","description":"The MCP address, how it is signed in to, and its tools with their input schemas.","security":[],"responses":{"200":{"description":"The MCP server card","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/agent-card.json":{"get":{"operationId":"getAgentCard","summary":"The A2A agent card","description":"The A2A address, how it is signed in to, and its skill.","security":[],"responses":{"200":{"description":"The A2A agent card","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/poppy.json":{"get":{"operationId":"getPoppyDiscovery","summary":"The Personal Agent Protocol discovery file","description":"The product and the domain it answers on, as a website.","security":[],"responses":{"200":{"description":"The Personal Agent Protocol discovery file","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/agent-skills/index.json":{"get":{"operationId":"getAgentSkills","summary":"The agent skills index","description":"Each skill this host offers, where it is and the digest of its body.","security":[],"responses":{"200":{"description":"The agent skills index","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/agent-skills/install-cactus-messenger/SKILL.md":{"get":{"operationId":"getSkill_install_cactus_messenger","summary":"The install-cactus-messenger skill","description":"The skill's SKILL.md.","security":[],"responses":{"200":{"description":"The install-cactus-messenger skill","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/.well-known/agent-skills/connect-cactus-mcp/SKILL.md":{"get":{"operationId":"getSkill_connect_cactus_mcp","summary":"The connect-cactus-mcp skill","description":"The skill's SKILL.md.","security":[],"responses":{"200":{"description":"The connect-cactus-mcp skill","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/md":{"get":{"operationId":"getHomeMarkdown","summary":"The home page as markdown","description":"What Cactus is, as the home page says it.","security":[],"responses":{"200":{"description":"The page.","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/{page}/md":{"get":{"operationId":"getPageMarkdown","summary":"One page as markdown","description":"A page's markdown twin: the page's own address with /md on the end.","security":[],"parameters":[{"name":"page","in":"path","required":true,"schema":{"type":"string","enum":["pricing","privacy","security","mcp","about","contact","terms","acceptable-use","dpa","cookies"]}}],"responses":{"200":{"description":"The page.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"There's no page at this address on heycactus.ai.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no page at this address on heycactus.ai."}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"What went wrong, as a code a program can branch on or, on the ask, a short sentence."},"error_description":{"type":"string","description":"The same in a sentence a person can read, when there is more to say."}}},"JsonRpcError":{"type":"object","required":["jsonrpc","id","error"],"properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","integer","null"]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"JSON-RPC's code, or A2A's from section 5.4 of the A2A specification."},"message":{"type":"string"},"data":{"type":"array","description":"A `google.rpc.ErrorInfo` naming the reason, such as `TASK_NOT_FOUND` or `RATE_LIMITED`.","items":{"type":"object"}}}}}},"A2aRequest":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","integer","null"]},"method":{"type":"string","description":"`SendMessage`. `ListTasks` answers an empty list: no task is ever kept.","examples":["SendMessage"]},"params":{"type":"object","required":["message"],"properties":{"message":{"type":"object","required":["messageId","role","parts"],"properties":{"messageId":{"type":"string"},"role":{"const":"ROLE_USER"},"contextId":{"type":"string","description":"The conversation this message continues, as an earlier reply named it."},"parts":{"type":"array","minItems":1,"description":"Text parts only, up to 4000 characters in all. A file or data part is refused with -32005.","items":{"type":"object","required":["text"],"properties":{"text":{"type":"string"}}}}}},"configuration":{"type":"object","properties":{"acceptedOutputModes":{"type":"array","items":{"type":"string"},"description":"With `application/json` named, or nothing named, the reply carries the answer as data beside its markdown."}}},"metadata":{"type":"object","properties":{"product":{"type":"integer","minimum":1,"description":"On the team address, the ref of the team's product to ask about when it has more than one."}}}}}}},"A2aReply":{"type":"object","required":["jsonrpc","id","result"],"properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","integer","null"]},"result":{"type":"object","required":["message"],"properties":{"message":{"type":"object","required":["messageId","contextId","role","parts"],"properties":{"messageId":{"type":"string"},"contextId":{"type":"string"},"role":{"const":"ROLE_AGENT"},"parts":{"type":"array","description":"The answer in markdown with the pages it cites, then, when JSON is accepted, the same answer as data.","items":{"type":"object","properties":{"text":{"type":"string"},"data":{"type":"object"},"mediaType":{"type":"string"}}}}}}}}}},"McpRequest":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","integer","null"]},"method":{"type":"string","examples":["initialize","tools/list","tools/call"]},"params":{"type":"object"}}},"TokenRequest":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:jwt-bearer","urn:workos:agent-auth:grant-type:claim"]},"code":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string"},"code_verifier":{"type":"string"},"refresh_token":{"type":"string"},"assertion":{"type":"string","description":"The agent's `identity_assertion`, with the jwt-bearer grant."},"claim_token":{"type":"string","description":"The agent's `claim_token`, with the claim grant."},"resource":{"type":"string","format":"uri","description":"The address the token is for. This site itself when left out."}}},"TokenResponse":{"type":"object","required":["access_token","token_type"],"properties":{"access_token":{"type":"string"},"token_type":{"const":"bearer"},"expires_in":{"type":"integer","description":"Seconds. A token lasts an hour."},"scope":{"type":"string","description":"Space-separated, from cactus:read and cactus:ask. Empty for an agent no person has claimed yet."},"refresh_token":{"type":"string"},"resource":{"type":"string"},"identity_assertion":{"type":"string","description":"On the claim grant: the assertion that mints the claimed agent's next token."},"assertion_expires":{"type":"string","format":"date-time"}}},"AuthorizationServerMetadata":{"type":"object","required":["issuer","authorization_endpoint","token_endpoint","scopes_supported"],"properties":{"issuer":{"type":"string","format":"uri"},"authorization_endpoint":{"type":"string","format":"uri"},"token_endpoint":{"type":"string","format":"uri"},"registration_endpoint":{"type":"string","format":"uri"},"scopes_supported":{"type":"array","items":{"type":"string","enum":["cactus:read","cactus:ask"]}},"agent_auth":{"type":"object","description":"auth.md's block: where an agent with no browser registers and is claimed.","properties":{"skill":{"type":"string","format":"uri"},"identity_endpoint":{"type":"string","format":"uri"},"claim_endpoint":{"type":"string","format":"uri"},"identity_types_supported":{"type":"array","items":{"type":"string"}}}}}},"ProtectedResourceMetadata":{"type":"object","required":["resource","authorization_servers"],"properties":{"resource":{"type":"string","format":"uri"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"scopes_supported":{"type":"array","items":{"type":"string","enum":["cactus:read","cactus:ask"]}},"bearer_methods_supported":{"type":"array","items":{"type":"string"}},"resource_name":{"type":"string"}}},"ClientMetadata":{"type":"object","required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"}},"client_name":{"type":"string"},"token_endpoint_auth_method":{"type":"string"},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}}}},"ClientInformation":{"type":"object","required":["client_id","redirect_uris"],"properties":{"client_id":{"type":"string"},"client_secret":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"}},"client_name":{"type":"string"},"token_endpoint_auth_method":{"type":"string"},"client_id_issued_at":{"type":"integer"}}},"AgentRegistrationRequest":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["anonymous","service_auth"],"description":"`identity_assertion` is refused with `issuer_not_enabled`."},"name":{"type":"string","maxLength":80,"description":"What the agent's person sees it called on the claim page."},"login_hint":{"type":"string","format":"email","description":"With `service_auth`: the email of the person the agent acts for."}}},"AgentRegistration":{"type":"object","required":["registration_id","registration_type","claim_url","claim_token","claim_token_expires","post_claim_scopes"],"properties":{"registration_id":{"type":"string"},"registration_type":{"type":"string","enum":["anonymous","service_auth"]},"claim_url":{"type":"string","format":"uri"},"claim_token":{"type":"string"},"claim_token_expires":{"type":"string","format":"date-time"},"post_claim_scopes":{"type":"array","items":{"type":"string","enum":["cactus:read","cactus:ask"]}},"pre_claim_scopes":{"type":"array","items":{"type":"string"}},"identity_assertion":{"type":"string","description":"With `anonymous`: exchanged at the token endpoint for a token for public help."},"assertion_expires":{"type":"string","format":"date-time"},"claim":{"type":"object","required":["user_code","verification_uri","expires_in","interval"],"properties":{"user_code":{"type":"string","description":"Six digits."},"verification_uri":{"type":"string","format":"uri"},"expires_in":{"type":"integer"},"interval":{"type":"integer","description":"Seconds between polls of the token endpoint."}}}}},"ClaimRequest":{"type":"object","required":["claim_token","email"],"properties":{"claim_token":{"type":"string"},"email":{"type":"string","format":"email"}}},"ClaimStarted":{"type":"object","required":["registration_id","status","claim_attempt"],"properties":{"registration_id":{"type":"string"},"claim_attempt_id":{"type":"string"},"status":{"const":"initiated"},"expires_at":{"type":"string","format":"date-time"},"claim_attempt":{"type":"object","required":["user_code","verification_uri","expires_in","interval"],"properties":{"user_code":{"type":"string","description":"Six digits."},"verification_uri":{"type":"string","format":"uri"},"expires_in":{"type":"integer"},"interval":{"type":"integer","description":"Seconds between polls of the token endpoint."}}}}}},"securitySchemes":{"cactus":{"type":"oauth2","description":"Sign in to Cactus through https://heycactus.ai, an authorization server over the one Cactus sign-in. Its metadata is at https://heycactus.ai/.well-known/oauth-authorization-server. A token from here is for https://heycactus.ai, and asks at https://heycactus.ai/a2a.\n\nA client with a browser registers at https://heycactus.ai/oauth/register (RFC 7591) or names itself with a client ID metadata document, then uses the authorization code flow with PKCE (S256); a member of the team approves it on app.heycactus.ai.\n\nAn agent with no browser uses auth.md (https://github.com/workos/auth.md): it registers at https://heycactus.ai/oauth/agent/identity, exchanges its identity assertion at https://heycactus.ai/oauth/token for a token that reads public help, has its person claim it at https://heycactus.ai/oauth/agent/identity/claim, and polls https://heycactus.ai/oauth/token with the claim grant for a token with both scopes.","flows":{"authorizationCode":{"authorizationUrl":"https://heycactus.ai/oauth/authorize","tokenUrl":"https://heycactus.ai/oauth/token","refreshUrl":"https://heycactus.ai/oauth/token","scopes":{"cactus:read":"Read your team's pages, code map and past conversations","cactus:ask":"Ask Cactus questions and start investigations; each creates a conversation"}}}},"cactusMcp":{"type":"oauth2","description":"Sign in to Cactus through https://mcp.heycactus.ai, an authorization server over the one Cactus sign-in. Its metadata is at https://mcp.heycactus.ai/.well-known/oauth-authorization-server. A token from here is for https://mcp.heycactus.ai: it works on https://mcp.heycactus.ai/mcp, https://mcp.heycactus.ai/a2a and every product's address there.\n\nA client with a browser registers at https://mcp.heycactus.ai/register (RFC 7591) or names itself with a client ID metadata document, then uses the authorization code flow with PKCE (S256); a member of the team approves it on app.heycactus.ai.\n\nAn agent with no browser uses auth.md (https://github.com/workos/auth.md): it registers at https://mcp.heycactus.ai/agent/identity, exchanges its identity assertion at https://mcp.heycactus.ai/token for a token that reads public help, has its person claim it at https://mcp.heycactus.ai/agent/identity/claim, and polls https://mcp.heycactus.ai/token with the claim grant for a token with both scopes.","flows":{"authorizationCode":{"authorizationUrl":"https://mcp.heycactus.ai/authorize","tokenUrl":"https://mcp.heycactus.ai/token","refreshUrl":"https://mcp.heycactus.ai/token","scopes":{"cactus:read":"Read your team's pages, code map and past conversations","cactus:ask":"Ask Cactus questions and start investigations; each creates a conversation"}}}}}}}