{"openapi":"3.1.0","info":{"title":"Cactus help API","version":"1.0.0","description":"Every address https://cactus.heycactus.ai answers for a program: the ask, each page as markdown, the files that describe the site to an agent, and its sign-in. Everything here reads without signing in. The product's public MCP and A2A addresses are on https://mcp.heycactus.ai."},"externalDocs":{"description":"How an agent signs in here","url":"https://cactus.heycactus.ai/auth.md"},"servers":[{"url":"https://cactus.heycactus.ai"}],"paths":{"/api/help/ask":{"post":{"operationId":"askHelp","summary":"Ask Cactus help a question","description":"Starts one answer, drawn from Cactus's published help, and answers 202 at once with the conversation it is written on. The answer is read at https://cactus.heycactus.ai/a/{conversationId} when it is done. For the answer in the response itself, use the A2A address.\n\n`ref` is this site's product, \"1\". `turnKey` is a new UUID per question, which makes a retry of the same question one question. A reader who is not on the team may ask 30 questions an hour.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"page":{"type":"string","minLength":1},"conversationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"turnKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["ref","message","turnKey"]},"example":{"ref":"1","message":"How do I get started with Cactus?","turnKey":"5f0c6d7e-2a1b-4c3d-8e9f-0a1b2c3d4e5f"}}}},"responses":{"202":{"description":"Taken. The answer is being written.","headers":{"Set-Cookie":{"description":"The visitor cookie that ties the reader's questions together.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"The budget a reader who is not on the team has: `\"hourly\";q=30;w=3600`, 30 questions in any hour.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it once the reader's hour has been read: `\"hourly\";r=<questions left>;t=<seconds until the oldest question this hour stops counting>`. Absent for a reader on the team, who has no budget.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"watchKey":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productRef":{"type":"string"}},"required":["conversationId","watchKey","productRef"],"additionalProperties":false}}}},"400":{"description":"The body is not JSON, or not the shape above.","headers":{"RateLimit-Policy":{"description":"The budget a reader who is not on the team has: `\"hourly\";q=30;w=3600`, 30 questions in any hour.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it once the reader's hour has been read: `\"hourly\";r=<questions left>;t=<seconds until the oldest question this hour stops counting>`. Absent for a reader on the team, who has no budget.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request"}}}},"404":{"description":"No product has that ref, or its help is not this reader's.","headers":{"RateLimit-Policy":{"description":"The budget a reader who is not on the team has: `\"hourly\";q=30;w=3600`, 30 questions in any hour.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it once the reader's hour has been read: `\"hourly\";r=<questions left>;t=<seconds until the oldest question this hour stops counting>`. Absent for a reader on the team, who has no budget.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"No such product"}}}},"429":{"description":"30 questions this hour already. `Retry-After` says when to ask again.","headers":{"RateLimit-Policy":{"description":"The budget a reader who is not on the team has: `\"hourly\";q=30;w=3600`, 30 questions in any hour.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it once the reader's hour has been read: `\"hourly\";r=<questions left>;t=<seconds until the oldest question this hour stops counting>`. Absent for a reader on the team, who has no budget.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds until the oldest question this hour stops counting.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"slow down"}}}},"502":{"description":"The answer could not be started.","headers":{"RateLimit-Policy":{"description":"The budget a reader who is not on the team has: `\"hourly\";q=30;w=3600`, 30 questions in any hour.","schema":{"type":"string"}},"RateLimit":{"description":"What is left of it once the reader's hour has been read: `\"hourly\";r=<questions left>;t=<seconds until the oldest question this hour stops counting>`. Absent for a reader on the team, who has no budget.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Cactus is unavailable"}}}}}}},"/{slug}/md":{"get":{"operationId":"getPageMarkdown","summary":"One page as markdown","description":"A published page's markdown twin: the page's own address with /md on the end. https://cactus.heycactus.ai/llms.txt lists every page's address.","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]+$"}}],"responses":{"200":{"description":"The page.","content":{"text/markdown":{"schema":{"type":"string"}}}},"301":{"description":"The page has moved to a new slug.","headers":{"Location":{"schema":{"type":"string"}}}},"404":{"description":"There's no published page at this address. Or: There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no published page at this address."}}}}}}},"/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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.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"}}}}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.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"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.well-known/agent-skills/ask-help/SKILL.md":{"get":{"operationId":"getSkill_ask_help","summary":"The ask-help skill","description":"The skill's SKILL.md.","security":[],"responses":{"200":{"description":"The ask-help skill","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"There's no help site at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found","error_description":"There's no help site at this address."}}}}}}},"/.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://cactus.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"}}}}}}},"/p/cactus/mcp":{"servers":[{"url":"https://mcp.heycactus.ai"}],"post":{"operationId":"callHelpMcp","summary":"Cactus help over MCP","description":"Cactus's public MCP address. It answers anyone from the published pages; a member of the team signed in reads the team's pages too. MCP 2025-06-18 over streamable HTTP: POST JSON-RPC with `Accept: application/json, text/event-stream`. Its tools: `search_help`, `read_page`, `list_topics`, `read_timeline`, `ask_help`, `contact_team`, `search`, `fetch`.","security":[{},{"cactus":["cactus:read"]}],"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.","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"}}}},"404":{"description":"No public help at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found"}}}}}}},"/p/cactus/a2a":{"servers":[{"url":"https://mcp.heycactus.ai"}],"post":{"operationId":"askHelpA2a","summary":"Ask Cactus help over A2A","description":"Cactus's public A2A address: send a question as text, and the reply is one answer from the published help 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"]}],"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"}}}},"404":{"description":"No public help at this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found"}}}},"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"}}}}}}}},"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://cactus.heycactus.ai, an authorization server over the one Cactus sign-in. Its metadata is at https://cactus.heycactus.ai/.well-known/oauth-authorization-server. A token from here is for https://cactus.heycactus.ai; add `resource=https://mcp.heycactus.ai/p/cactus/mcp` when minting it to use it on the MCP address instead.\n\nA client with a browser registers at https://cactus.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://cactus.heycactus.ai/oauth/agent/identity, exchanges its identity assertion at https://cactus.heycactus.ai/oauth/token for a token that reads public help, has its person claim it at https://cactus.heycactus.ai/oauth/agent/identity/claim, and polls https://cactus.heycactus.ai/oauth/token with the claim grant for a token with both scopes.","flows":{"authorizationCode":{"authorizationUrl":"https://cactus.heycactus.ai/oauth/authorize","tokenUrl":"https://cactus.heycactus.ai/oauth/token","refreshUrl":"https://cactus.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"}}}}}}}