{"openapi":"3.1.0","info":{"title":"CooperBuild public API","version":"2026-10-18.1","summary":"OAuth, MCP, health, spend, audit, and the unpublished-schema GraphQL endpoint.","description":"This document describes HTTP behavior observed on api.cooperbuild.ai and input schemas published by the CooperBuild MCP server.\n\nThe language-model guide is https://www.cooperbuild.ai/llms.txt. The API host does not publish that guide; requesting llms.txt there requires login.\n\nIt is not a complete catalog of MCP tools. Output schemas, per-call prices, an API free-tier quota, an API key, Retry-After, and the authenticated bodies of GET /api/agent/spend and GET /api/agent/audit are not published.\n\nGET /health is public and returns status, version 1.0.0, signup_url https://app.cooperbuild.ai/auth/signup, and agent_bootstrap (start https://api.cooperbuild.ai/api/agent/bootstrap, verify https://api.cooperbuild.ai/api/agent/bootstrap/verify, token https://api.cooperbuild.ai/oauth/token). The default rate limit is 120 requests per minute with a burst of 40. Responses include X-RateLimit-Limit: 160, X-RateLimit-Remaining, and X-RateLimit-Reset. Retry-After is not sent.\n\nWorkspace calls are MCP tools/call on POST /mcp (or POST /mcp/directory for the ChatGPT plugin), with a bearer token from the OAuth authorization-code + PKCE flow. There is no API key.\n\nAccess tokens last one hour and can refresh for up to 30 days, as documented on https://cooperbuild.ai/claude. A person creates an account at https://app.cooperbuild.ai/auth/signup and connects the agent in the browser at https://app.cooperbuild.ai/oauth/authorize. An agent with no person in the loop POSTs /oauth/register with Content-Type application/json, then POST /api/agent/bootstrap with Content-Type application/json and email, company_name, client_id, and a S256 code_challenge, reads the 8-digit code from that inbox, POSTs /api/agent/bootstrap/verify with Content-Type application/json for an authorization_code and redirect_uri, and redeems the code at POST /oauth/token with Content-Type application/x-www-form-urlencoded and grant_type authorization_code, code, client_id, code_verifier, and redirect_uri. redirect_uri must exactly match the redirect_uri returned by POST /api/agent/bootstrap/verify (e.g. https://localhost/callback). Inbox access is required. The 8-digit code expires in 15 minutes (900 seconds) by default. For agent bootstrap, POST /oauth/register with Content-Type application/json, redirect_uris, and token_endpoint_auth_method none. Recommend redirect_uris [\"https://localhost/callback\"]. A public client does not receive a client_secret. Use client_id plus PKCE. Omitting token_endpoint_auth_method registers client_secret_post and returns a client_secret, which is not the bootstrap client. The default scope is mcp:tools. mcp:tools alone is enough for MCP tools and satisfies agent HTTP read/write checks. scopes_supported also advertises mcp:read and mcp:write. Sending scope \"mcp:tools mcp:read mcp:write\" stores that trio on the client. Unauthenticated MCP calls return JSON-RPC -32000 with signup_url and authorize_url in error.data and on WWW-Authenticate. After MCP initialize, every subsequent MCP request must reuse the mcp-session-id (or Mcp-Session-Id) response header. A bare tools/list without that header returns 400.","contact":{"email":"hello@cooperbuild.ai","url":"https://cooperbuild.ai/developers"}},"servers":[{"url":"https://api.cooperbuild.ai","description":"Product API and MCP server"}],"tags":[{"name":"health","description":"Public health check. No auth."},{"name":"mcp","description":"Remote MCP. Tool arguments are in components.schemas."},{"name":"agent","description":"Bearer-protected agent routes. Success bodies are not published."},{"name":"oauth","description":"OAuth 2.0 authorization server, protected resource, and agent bootstrap."},{"name":"graphql","description":"Exists. Schema is not published."},{"name":"marketing","description":"Unauthenticated demo-lead intake."}],"paths":{"/health":{"get":{"tags":["health"],"operationId":"health","summary":"Public health check","description":"No auth. signup_url is the human browser signup and does not mint an API key. agent_bootstrap.start, agent_bootstrap.verify, and agent_bootstrap.token are the agent-only path. tools_after_auth is a short list, not the full MCP catalog. uptime changes per response.","security":[],"responses":{"200":{"description":"Observed document.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"example":{"status":"ok","version":"1.0.0","uptime":1353,"description":"Cooper is construction management software for builders. There is no API key. A person creates an account at signup_url, then an agent connects with OAuth. An agent with no person in the loop can POST agent_bootstrap.start, read the emailed code, and POST agent_bootstrap.verify for an authorization code to redeem at /oauth/token. After auth the agent can build estimates, collect bids, write proposals, and message the workspace chat agents.","signup_url":"https://app.cooperbuild.ai/auth/signup","agent_bootstrap":{"start":"https://api.cooperbuild.ai/api/agent/bootstrap","verify":"https://api.cooperbuild.ai/api/agent/bootstrap/verify","token":"https://api.cooperbuild.ai/oauth/token"},"tools_after_auth":["estimate_browse","estimate_manage","vq_browse","proposal_browse","search_projects","chat_agent_message"]}}}}}}},"/mcp":{"post":{"tags":["mcp"],"operationId":"mcpJsonRpc","summary":"MCP JSON-RPC","description":"Bearer token required. After MCP initialize, every subsequent MCP request must reuse the mcp-session-id (or Mcp-Session-Id) response header. A bare tools/list without that header returns 400. The header is issued by initialize and must be sent on tools/list, tools/call, and every later request. Unauthenticated calls return JSON-RPC -32000. error.data.signup_url and error.data.authorize_url include ?resource=https://api.cooperbuild.ai/mcp, and WWW-Authenticate repeats those URLs with resource set to that MCP URL. Responses also send the rate-limit headers. tools/call params.name is the tool. Verified tools for the construction OS surface are listed as sibling operations and share this path. A successful tools/call body was not recorded in this spec.","security":[{"mcpBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mcp-jsonrpc.input"}}}},"responses":{"200":{"description":"initialize returns Mcp-Session-Id (also accepted on later requests as mcp-session-id). A success body was not copied into this spec. Reuse that header on every later request.","headers":{"Mcp-Session-Id":{"$ref":"#/components/headers/Mcp-Session-Id"}}},"400":{"description":"A later MCP request, including tools/list, that has a bearer token but no mcp-session-id / Mcp-Session-Id header. initialize is the call that issues the header."},"401":{"description":"Observed without a token.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"WWW-Authenticate":{"description":"Observed challenge. signup_url and authorize_url preserve the MCP resource.","schema":{"type":"string"},"example":"Bearer realm=\"cooper\", error=\"invalid_token\", resource=\"https://api.cooperbuild.ai/mcp\", signup_url=\"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\", authorize_url=\"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\""}},"content":{"application/json":{"example":{"jsonrpc":"2.0","error":{"code":-32000,"message":"Valid authentication token required. Create a Cooper account (signup_url), then finish this OAuth attempt (authorize_url). Both URLs are in error.data.","data":{"error":"unauthorized","message":"Valid authentication token required. Create a Cooper account (signup_url), then finish this OAuth attempt (authorize_url). Both URLs are in error.data.","signup_url":"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp","authorize_url":"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp","www_authenticate":"Bearer realm=\"cooper\", error=\"invalid_token\", resource=\"https://api.cooperbuild.ai/mcp\", signup_url=\"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\", authorize_url=\"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\""}},"id":null}}}}}}},"/mcp/directory":{"post":{"tags":["mcp"],"operationId":"mcpDirectoryJsonRpc","summary":"ChatGPT directory MCP JSON-RPC","description":"Same protocol as /mcp. After MCP initialize, every subsequent MCP request must reuse the mcp-session-id (or Mcp-Session-Id) response header. A bare tools/list without that header returns 400. Protected resource metadata: GET /.well-known/oauth-protected-resource/mcp/directory. Unauthenticated calls use resource https://api.cooperbuild.ai/mcp/directory on signup_url and authorize_url. WWW-Authenticate carries resource_metadata for that protected-resource document rather than a resource parameter.","security":[{"mcpBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mcp-jsonrpc.input"}}}},"responses":{"200":{"description":"initialize returns Mcp-Session-Id. Reuse it as mcp-session-id or Mcp-Session-Id on every later request. A success body was not copied into this spec.","headers":{"Mcp-Session-Id":{"$ref":"#/components/headers/Mcp-Session-Id"}}},"400":{"description":"A later request, including tools/list, that has a bearer token but no mcp-session-id / Mcp-Session-Id header."},"401":{"description":"Observed without a token. A successful tools/call body was not recorded in this spec.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"WWW-Authenticate":{"description":"Observed challenge for the directory resource.","schema":{"type":"string"},"example":"Bearer realm=\"cooper\", error=\"invalid_token\", resource_metadata=\"https://api.cooperbuild.ai/.well-known/oauth-protected-resource/mcp/directory\", signup_url=\"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp%2Fdirectory\", authorize_url=\"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp%2Fdirectory\""}},"content":{"application/json":{"example":{"jsonrpc":"2.0","error":{"code":-32000,"message":"Valid authentication token required. Create a Cooper account (signup_url), then finish this OAuth attempt (authorize_url). Both URLs are in error.data.","data":{"error":"unauthorized","message":"Valid authentication token required. Create a Cooper account (signup_url), then finish this OAuth attempt (authorize_url). Both URLs are in error.data.","signup_url":"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp%2Fdirectory","authorize_url":"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp%2Fdirectory","www_authenticate":"Bearer realm=\"cooper\", error=\"invalid_token\", resource_metadata=\"https://api.cooperbuild.ai/.well-known/oauth-protected-resource/mcp/directory\", signup_url=\"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp%2Fdirectory\", authorize_url=\"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp%2Fdirectory\""}},"id":null}}}}}}},"/graphql":{"post":{"tags":["graphql"],"operationId":"graphql","summary":"GraphQL","description":"Unauthenticated POST returns HTTP 500 with errors[].code UNAUTHORIZED and the message \"You need to be logged in to continue.\" The schema, auth header, and operation list are not published. Do not infer queries from this file.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphql.input"}}}},"responses":{"500":{"description":"Observed for an unauthenticated request. The GraphQL error code is UNAUTHORIZED."}}}},"/api/agent/spend":{"get":{"tags":["agent"],"operationId":"agentSpend","summary":"Spend cap","description":"Bearer token required. GET without a token returns 401 with signup_url and authorize_url for resource https://api.cooperbuild.ai/mcp (the same challenge as POST /mcp, not a spend-specific resource). POST returns 404 and { isSuccess: false, message: \"Not found\" }. An authenticated success body was not recorded in this spec.","security":[{"mcpBearer":[]}],"responses":{"401":{"description":"Observed without a bearer token.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"WWW-Authenticate":{"schema":{"type":"string"},"example":"Bearer realm=\"cooper\", error=\"invalid_token\", resource=\"https://api.cooperbuild.ai/mcp\", signup_url=\"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\", authorize_url=\"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\""}},"content":{"application/json":{"example":{"error":"unauthorized","message":"Valid bearer token required. Create a Cooper account (signup_url), then connect this agent with OAuth (authorize_url).","signup_url":"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp","authorize_url":"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp"}}}}}}},"/api/agent/audit":{"get":{"tags":["agent"],"operationId":"agentAudit","summary":"Audit log","description":"Bearer token required. GET without a token returns 401 with signup_url and authorize_url for resource https://api.cooperbuild.ai/mcp (the same challenge as POST /mcp). POST returns 404 and { isSuccess: false, message: \"Not found\" }. An authenticated success body was not recorded in this spec.","security":[{"mcpBearer":[]}],"responses":{"401":{"description":"Observed without a bearer token.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"WWW-Authenticate":{"schema":{"type":"string"},"example":"Bearer realm=\"cooper\", error=\"invalid_token\", resource=\"https://api.cooperbuild.ai/mcp\", signup_url=\"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\", authorize_url=\"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp\""}},"content":{"application/json":{"example":{"error":"unauthorized","message":"Valid bearer token required. Create a Cooper account (signup_url), then connect this agent with OAuth (authorize_url).","signup_url":"https://app.cooperbuild.ai/auth/signup?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp","authorize_url":"https://app.cooperbuild.ai/oauth/authorize?resource=https%3A%2F%2Fapi.cooperbuild.ai%2Fmcp"}}}}}}},"/api/landing-leads":{"post":{"tags":["marketing"],"operationId":"createLandingLead","summary":"Submit a demo lead","description":"No authentication. POST {} returns 400 and { success: false, message: \"Name is required.\" }.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/landing-lead.input"}}}},"responses":{"400":{"description":"Observed for POST {}. The body is { success: false, message: \"Name is required.\" }. A success status was not recorded in this spec."}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":["oauth"],"operationId":"oauthProtectedResource","summary":"OAuth protected resource metadata for /mcp","security":[],"responses":{"200":{"description":"Observed document.","content":{"application/json":{"example":{"resource":"https://api.cooperbuild.ai/mcp","resource_name":"CooperBuild","authorization_servers":["https://api.cooperbuild.ai"],"scopes_supported":["mcp:tools","mcp:read","mcp:write"],"bearer_methods_supported":["header"],"resource_documentation":"https://modelcontextprotocol.io/docs"}}}}}}},"/.well-known/oauth-protected-resource/mcp/directory":{"get":{"tags":["oauth"],"operationId":"oauthProtectedResourceDirectory","summary":"OAuth protected resource metadata for /mcp/directory","security":[],"responses":{"200":{"description":"Observed document. It does not include resource_documentation.","content":{"application/json":{"example":{"resource":"https://api.cooperbuild.ai/mcp/directory","resource_name":"CooperBuild","authorization_servers":["https://api.cooperbuild.ai"],"scopes_supported":["mcp:tools","mcp:read","mcp:write"],"bearer_methods_supported":["header"]}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"tags":["oauth"],"operationId":"oauthAuthorizationServer","summary":"OAuth authorization server metadata","description":"This is the scopes endpoint referenced by the agent manifest.","security":[],"responses":{"200":{"description":"Observed document.","content":{"application/json":{"example":{"issuer":"https://api.cooperbuild.ai","authorization_endpoint":"https://app.cooperbuild.ai/oauth/authorize","token_endpoint":"https://api.cooperbuild.ai/oauth/token","registration_endpoint":"https://api.cooperbuild.ai/oauth/register","code_creation_endpoint":"https://api.cooperbuild.ai/oauth/create-code","token_endpoint_auth_methods_supported":["none"],"response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token"],"code_challenge_methods_supported":["S256"],"scopes_supported":["mcp:tools","mcp:read","mcp:write"],"signup_url":"https://app.cooperbuild.ai/auth/signup","agent_bootstrap_endpoint":"https://api.cooperbuild.ai/api/agent/bootstrap","agent_bootstrap_verify_endpoint":"https://api.cooperbuild.ai/api/agent/bootstrap/verify"}}}}}}},"/oauth/token":{"post":{"tags":["oauth"],"operationId":"oauthToken","summary":"Token endpoint","description":"GET returns 404. POST {} returns 400 unsupported_grant_type. Content-Type application/x-www-form-urlencoded. Supported grants: authorization_code, refresh_token. Token endpoint auth method advertised: none. The authorization_code grant requires grant_type, code, client_id, code_verifier, and redirect_uri. redirect_uri must exactly match the redirect_uri returned by POST /api/agent/bootstrap/verify (e.g. https://localhost/callback). A public client has no client_secret. The response is a bearer token. curl -X POST https://api.cooperbuild.ai/oauth/token \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d grant_type=authorization_code \\\n  -d code=AUTHORIZATION_CODE \\\n  -d client_id=CLIENT_ID \\\n  -d code_verifier=CODE_VERIFIER \\\n  -d redirect_uri=https://localhost/callback","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/oauth-token.input"}}}},"responses":{"400":{"description":"Observed for POST {}: unsupported_grant_type. A success status was not recorded in this spec."}}}},"/oauth/register":{"post":{"tags":["oauth"],"operationId":"oauthRegister","summary":"Dynamic client registration","description":"Content-Type application/json. GET returns 404. POST {} returns 400 invalid_client_metadata because redirect_uris is required. This is not an API-key signup. For agent bootstrap, POST /oauth/register with Content-Type application/json, redirect_uris, and token_endpoint_auth_method none. Recommend redirect_uris [\"https://localhost/callback\"]. A public client does not receive a client_secret. Use client_id plus PKCE. Omitting token_endpoint_auth_method registers client_secret_post and returns a client_secret, which is not the bootstrap client. The default scope is mcp:tools. mcp:tools alone is enough for MCP tools and satisfies agent HTTP read/write checks. scopes_supported also advertises mcp:read and mcp:write. Sending scope \"mcp:tools mcp:read mcp:write\" stores that trio on the client. Register example: {\n  \"redirect_uris\": [\"https://localhost/callback\"],\n  \"token_endpoint_auth_method\": \"none\"\n} Observed 201 on 2026-10-16 for token_endpoint_auth_method none includes client_id, token_endpoint_auth_method, and scope, and does not include client_secret.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/oauth-register.input"}}}},"responses":{"201":{"description":"Observed. token_endpoint_auth_method none does not include client_secret. Default scope when scope is omitted is mcp:tools. scope \"mcp:tools mcp:read mcp:write\" is stored and returned when that string is sent. Omitting token_endpoint_auth_method returns client_secret_post and a client_secret."},"400":{"description":"Observed for POST {}: invalid_client_metadata, redirect_uris is required."}}}},"/api/agent/bootstrap":{"post":{"tags":["oauth"],"operationId":"agentBootstrap","summary":"Start agent workspace signup","description":"No bearer token. Content-Type application/json. An agent with no person in the loop sends email, company_name, client_id from POST /oauth/register, code_challenge, and code_challenge_method S256. Cooper emails an 8-digit code. Inbox access is required. The 8-digit code expires in 15 minutes (900 seconds) by default. GET returns 404 and { isSuccess: false, message: \"Not found\" }. POST {} returns 400 invalid_request, email is required. Later missing fields are company_name, then client_id, then PKCE required with S256. An unknown client_id returns 400 invalid_client. A success body was not recorded in this spec. Human browser signup remains signup_url.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/agent-bootstrap.input"}}}},"responses":{"400":{"description":"Observed for POST {}: invalid_request, email is required. Responses include the rate-limit headers.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}}}}},"/api/agent/bootstrap/verify":{"post":{"tags":["oauth"],"operationId":"agentBootstrapVerify","summary":"Verify the emailed bootstrap code","description":"No bearer token. Content-Type application/json. JSON requires email, the 8-digit code, client_id, and code_verifier. Inbox access is required. The 8-digit code expires in 15 minutes (900 seconds) by default. The response includes an authorization_code and redirect_uri. Redeem the code at POST /oauth/token with Content-Type application/x-www-form-urlencoded and grant_type authorization_code, code, client_id, code_verifier, and that redirect_uri. redirect_uri must exactly match the redirect_uri returned by POST /api/agent/bootstrap/verify (e.g. https://localhost/callback). A public client has no client_secret. GET returns 404 and { isSuccess: false, message: \"Not found\" }. POST {} returns 400 invalid_request, email is required. Later missing fields are code, then client_id, then code_verifier. A success body was not recorded in this spec.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/agent-bootstrap-verify.input"}}}},"responses":{"400":{"description":"Observed for POST {}: invalid_request, email is required. Responses include the rate-limit headers.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}}}}},"/oauth/create-code":{"post":{"tags":["oauth"],"operationId":"oauthCreateCode","summary":"Create an authorization code","description":"Advertised as code_creation_endpoint. GET returns 404. POST {} returns 400 invalid_request, userToken is required. Remaining fields are not published.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/oauth-create-code.input"}}}},"responses":{"400":{"description":"invalid_request when userToken is missing."}}}}},"components":{"headers":{"X-RateLimit-Limit":{"description":"Observed on /health, /mcp, /mcp/directory, /api/agent/spend, /api/agent/audit, /api/agent/bootstrap, and /api/agent/bootstrap/verify. The value is 160, which is 120 requests per minute plus a burst of 40.","schema":{"type":"string","const":"160"}},"X-RateLimit-Remaining":{"description":"Requests remaining. The value changes per response.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds. Observed values were within a couple of seconds of the request time. Retry-After is not sent.","schema":{"type":"string"}},"Mcp-Session-Id":{"description":"Session id from MCP initialize. Send this value on every later request as Mcp-Session-Id or mcp-session-id. tools/list without it returns 400.","schema":{"type":"string"}}},"securitySchemes":{"mcpBearer":{"type":"http","scheme":"bearer","description":"OAuth 2.0 access token from /oauth/token. Send as Authorization: Bearer. No key prefix is published. Scopes advertised: mcp:tools, mcp:read, mcp:write. A registered client often receives only mcp:tools unless scope \"mcp:tools mcp:read mcp:write\" is sent to /oauth/register. mcp:tools alone is enough for MCP tools and satisfies agent HTTP read/write checks. After initialize, also send Mcp-Session-Id."}},"schemas":{"mcp-jsonrpc.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"MCP JSON-RPC request","type":"object","additionalProperties":true,"required":["jsonrpc","method"],"properties":{"jsonrpc":{"const":"2.0"},"id":{"description":"Request id. Error responses observed without a token use null."},"method":{"type":"string","description":"MCP method. initialize returns the mcp-session-id (or Mcp-Session-Id) response header. Every later method, including tools/list and tools/call, must send that header back. A bare tools/list without it returns 400. An unauthenticated POST returns JSON-RPC error -32000."},"params":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string","description":"Tool name when method is tools/call."},"arguments":{"type":"object","additionalProperties":true}}}}},"oauth-token.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"OAuth token request","type":"object","additionalProperties":true,"required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"],"description":"Send this grant as application/x-www-form-urlencoded. Observed: an empty body returns 400 unsupported_grant_type and names these two grants. The authorization server advertises token_endpoint_auth_methods_supported: none (public client, PKCE S256)."},"code":{"type":"string","description":"authorization_code from POST /api/agent/bootstrap/verify. Send this with client_id, code_verifier, and redirect_uri when grant_type is authorization_code. A public client has no client_secret."},"client_id":{"type":"string","description":"client_id from POST /oauth/register. The bootstrap token call uses this public client with PKCE."},"code_verifier":{"type":"string","description":"PKCE verifier for the S256 challenge sent to POST /api/agent/bootstrap."},"redirect_uri":{"type":"string","description":"Required when grant_type is authorization_code. Must exactly match the redirect_uri returned by POST /api/agent/bootstrap/verify (e.g. https://localhost/callback).","examples":["https://localhost/callback"]}},"allOf":[{"if":{"properties":{"grant_type":{"const":"authorization_code"}},"required":["grant_type"]},"then":{"required":["redirect_uri"]}}],"examples":[{"grant_type":"authorization_code","code":"AUTHORIZATION_CODE","client_id":"CLIENT_ID","code_verifier":"CODE_VERIFIER","redirect_uri":"https://localhost/callback"}]},"oauth-register.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"OAuth dynamic client registration","type":"object","additionalProperties":true,"required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string"},"description":"Required. For agent bootstrap, recommend [\"https://localhost/callback\"]. Observed: POST {} returns 400 invalid_client_metadata, redirect_uris is required.","examples":[["https://localhost/callback"]]},"token_endpoint_auth_method":{"type":"string","const":"none","description":"Send none for agent bootstrap. Observed 2026-10-16: a 201 with token_endpoint_auth_method none does not include client_secret. Use client_id plus PKCE. Omitting this field registers client_secret_post and returns a client_secret, which is not the bootstrap client."},"scope":{"type":"string","description":"Space-separated. Observed default when omitted is mcp:tools. That scope is enough for MCP tools and satisfies agent HTTP read/write checks. Sending \"mcp:tools mcp:read mcp:write\" stores mcp:tools, mcp:read, and mcp:write on the client."}},"examples":[{"redirect_uris":["https://localhost/callback"],"token_endpoint_auth_method":"none"}]},"oauth-create-code.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"OAuth code creation","type":"object","additionalProperties":true,"required":["userToken"],"properties":{"userToken":{"type":"string","description":"Observed: POST {} returns 400 invalid_request, userToken is required. The rest of this contract is not published. This is not an API-key signup."}}},"agent-bootstrap.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"Agent bootstrap start","type":"object","additionalProperties":true,"required":["email","company_name","client_id","code_challenge","code_challenge_method"],"properties":{"email":{"type":"string","description":"Inbox that receives the 8-digit code. The agent must be able to read that inbox. The code expires in 15 minutes (900 seconds) by default. Observed: POST {} returns 400 invalid_request, email is required."},"company_name":{"type":"string","description":"Observed: required after email. The response is company_name is required."},"client_id":{"type":"string","description":"Client from POST /oauth/register. Observed: required after company_name. An unknown id returns 400 invalid_client and says to register."},"code_challenge":{"type":"string","description":"PKCE S256 challenge for the verifier sent to verify and to POST /oauth/token. Observed: a missing or non-S256 challenge returns 400 PKCE required with S256."},"code_challenge_method":{"const":"S256","description":"Observed: required. A valid challenge without this field still returns PKCE required with S256."}}},"agent-bootstrap-verify.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"Agent bootstrap verify","type":"object","additionalProperties":true,"required":["email","code","client_id","code_verifier"],"properties":{"email":{"type":"string","description":"Same email sent to POST /api/agent/bootstrap. Observed: POST {} returns email is required."},"code":{"type":"string","description":"8-digit code from that email. Read it from the inbox before it expires (15 minutes / 900 seconds by default). Observed: required after email."},"client_id":{"type":"string","description":"Same client_id as start. Observed: required after code. An unknown id returns 400 invalid_client."},"code_verifier":{"type":"string","description":"PKCE verifier for the S256 challenge. Observed: required after client_id. POST /oauth/token redeems the returned authorization_code with this verifier, client_id, and redirect_uri. redirect_uri must exactly match the redirect_uri returned by this verify call (e.g. https://localhost/callback)."}}},"landing-lead.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"Public demo lead","type":"object","additionalProperties":true,"required":["name"],"properties":{"name":{"type":"string","description":"Observed: POST {} returns 400 and { success: false, message: \"Name is required.\" }."},"workEmail":{"type":"string","description":"Sent by the marketing form. Not confirmed as required by the API."},"company":{"type":"string"},"mobilePhone":{"type":"string"},"smsConsent":{"type":"boolean"},"demoInterest":{"type":"string","description":"Free text. The marketing site forwards the form field details under this name."}}},"graphql.input":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"GraphQL HTTP body","type":"object","additionalProperties":true,"properties":{"query":{"type":"string","description":"GraphQL document. The public schema is not published."},"variables":{"type":"object","additionalProperties":true}}},"estimate_browse.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"estimate_browse arguments","type":"object","required":["action","context"],"properties":{"action":{"type":"string","enum":["list_projects","detail_view","summary_view","search","get_templates","get_deliverable_classes","get_task_types"]},"estimateId":{"type":"string","description":"Required for detail_view, summary_view, and search."},"projectId":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string"}}}]},"appMode":{"type":"string","default":"both","description":"both | external | internal"},"query":{"type":"string"},"searchScope":{"type":"object","additionalProperties":false,"properties":{"isTask":{"type":"boolean","default":true},"isDeliverable":{"type":"boolean","default":true},"isDeliverableClass":{"type":"boolean","default":true}}},"deliverableClassId":{"type":"string"},"deliverableClassName":{"type":"string"},"templateSearch":{"type":"string"},"limit":{"type":"number","minimum":1,"maximum":100,"default":20},"skip":{"type":"number","minimum":0,"default":0},"pageNo":{"type":"number","minimum":1,"default":1},"context":{"type":"string","description":"Required on this tool. Short third-person reason for the call, for analytics. Must not include names, contact details, or other personal data."}}},"estimate_manage.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"estimate_manage arguments","type":"object","required":["action","context"],"properties":{"action":{"type":"string","enum":["create","update","duplicate","delete","import_templates"]},"projectId":{"description":"Required for create. ObjectId or { name }.","anyOf":[{"type":"string"},{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string"}}}]},"name":{"type":"string","description":"Required for create."},"description":{"type":"string"},"contact":{"type":"string","description":"Contact Human ObjectId."},"estimateStatus":{"type":"string","enum":["queued","in_progress","on_hold","ready_for_review","approved","archived"]},"estimateUnit":{"type":"string"},"validUntil":{"type":"string","description":"ISO date."},"additionalFees":{"type":"array","description":"Carried to the proposal. Does not change totalEstimate.","items":{"type":"object","additionalProperties":false,"required":["name","amount"],"properties":{"name":{"type":"string"},"amount":{"type":"number","description":"Dollars. Stored as cents."},"description":{"type":"string"}}}},"discounts":{"type":"array","description":"Carried to the proposal. Does not change totalEstimate.","items":{"type":"object","additionalProperties":false,"required":["name","amount"],"properties":{"name":{"type":"string"},"amount":{"type":"number","description":"Dollars. Stored as cents."},"description":{"type":"string"}}}},"notes":{"type":"string"},"estimateDeliverables":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["templateId","deliverableClassId"],"properties":{"templateId":{"type":"string"},"deliverableClassId":{"type":"string"},"estimateDeliverableName":{"type":"string"},"estimateDeliverableDescription":{"type":"string"},"estimateQty":{"type":"number"},"estimateUnit":{"type":"string"}}}},"estimateId":{"type":"string","description":"Required for update, duplicate, and single delete."},"estimateSize":{"type":"number"},"status":{"type":"string","enum":["Active","Archived"]},"targetProjectId":{"type":"string"},"estimateIds":{"type":"array","items":{"type":"string"},"description":"For delete."},"activityMessage":{"type":"string"},"context":{"type":"string","description":"Required on this tool. Short third-person reason for the call, for analytics. Must not include names, contact details, or other personal data."}}},"proposal_manage.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"proposal_manage arguments","type":"object","required":["action","context"],"properties":{"action":{"type":"string","enum":["create","list_templates"]},"estimateId":{"type":"string"},"estimateNumber":{"type":"string","description":"Alternative to estimateId, for example EST-0001."},"markupPercentage":{"type":"number","minimum":0,"maximum":100,"default":0},"proposalSettingsId":{"type":"string"},"proposalProfileKey":{"type":"string"},"activityMessage":{"type":"string"},"context":{"type":"string","description":"Required on this tool. Short third-person reason for the call, for analytics. Must not include names, contact details, or other personal data."}}},"proposal_approve.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"proposal_approve arguments","type":"object","required":["proposalId","confirmApproval","context"],"properties":{"proposalId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"},"confirmApproval":{"type":"boolean","description":"false returns a summary and does not approve. true clones estimate deliverables, tasks, and resources onto the project, marks the proposal approved, and creates the draft client invoice."},"quickEdits":{"type":"object","additionalProperties":false,"properties":{"attention":{"type":"string"},"validUntil":{"type":"string"},"description":{"type":"string"}}},"activityMessage":{"type":"string"},"context":{"type":"string","description":"Required on this tool. Short third-person reason for the call, for analytics. Must not include names, contact details, or other personal data."}}},"vq_browse.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"vq_browse arguments","type":"object","additionalProperties":false,"required":["action"],"properties":{"action":{"type":"string","enum":["start","search_project","get_project_by_id","search_estimate","get_estimate_by_id","list_scopes","get_quote"]},"context":{"type":"string","enum":["project","estimate"],"description":"Required for start and list_scopes."},"search":{"type":"string"},"projectId":{"type":"string"},"estimateId":{"type":"string"},"vendorQuoteId":{"type":"string","description":"Required for get_quote."},"limit":{"type":"integer","minimum":1,"maximum":50}}},"vq_quote.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"vq_quote arguments","type":"object","required":["tradeScopeId","externalOrgId","context"],"properties":{"confirm":{"type":"boolean","default":false,"description":"false previews and does not save. true creates the quote, or updates an existing shortlisted or quoted quote in place."},"tradeScopeId":{"type":"string"},"externalOrgId":{"type":"string"},"projectId":{"type":"string"},"estimateId":{"type":"string"},"isProject":{"type":"boolean"},"tasks":{"type":"array","default":[],"items":{"type":"object","additionalProperties":false,"required":["_id","price"],"properties":{"_id":{"type":"string"},"price":{"type":"number","minimum":0,"description":"Dollars. Stored as cents."},"description":{"type":"string"}}}},"deliverables":{"type":"array","default":[],"items":{"type":"object","additionalProperties":false,"required":["_id","price"],"properties":{"_id":{"type":"string"},"price":{"type":"number","minimum":0},"description":{"type":"string"}}}},"resources":{"type":"array","default":[],"items":{"type":"object","additionalProperties":false,"required":["_id","price"],"properties":{"_id":{"type":"string"},"price":{"type":"number","minimum":0},"quantity":{"type":"number"},"description":{"type":"string"}}}},"attachments":{"type":"array","items":{"type":"string"},"default":[]},"remarks":{"type":"string","default":""},"context":{"type":"string","description":"Required on this tool. Short third-person reason for the call, for analytics. Must not include names, contact details, or other personal data."}}},"talk_to_agent.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"talk_to_agent arguments","type":"object","required":["message","context"],"properties":{"message":{"type":"string","description":"The full user message. The tool routes /slash, @mention, and #role triggers to a Cooper agent."},"context":{"type":"string","description":"Required on this tool. Short third-person reason for the call, for analytics. Must not include names, contact details, or other personal data."}}},"chat_agent_message.input":{"$schema":"http://json-schema.org/draft-07/schema#","title":"chat_agent_message arguments","type":"object","additionalProperties":false,"required":["text"],"properties":{"userId":{"type":"string","description":"Tenant user ObjectId. Not a CRM contact."},"email":{"type":"string"},"text":{"type":"string","minLength":1,"maxLength":20000},"agentId":{"type":"string"},"reason":{"type":"string","maxLength":80},"context":{"type":"object","additionalProperties":true,"description":"Ledger-only context. Not part of the message, and not the analytics string used by other tools."},"clientMessageId":{"type":"string","maxLength":120,"description":"Idempotency key. A retry with the same key does not double-post."},"confirm":{"type":"boolean","description":"false (default) previews and sends nothing. true sends the message to the tenant user."}}},"estimate_browse.call":{"title":"tools/call estimate_browse","description":"POST /mcp with this body. arguments use estimate_browse.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"estimate_browse"},"arguments":{"$ref":"#/components/schemas/estimate_browse.input"}}}}},"estimate_manage.call":{"title":"tools/call estimate_manage","description":"POST /mcp with this body. arguments use estimate_manage.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"estimate_manage"},"arguments":{"$ref":"#/components/schemas/estimate_manage.input"}}}}},"vq_browse.call":{"title":"tools/call vq_browse","description":"POST /mcp with this body. arguments use vq_browse.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"vq_browse"},"arguments":{"$ref":"#/components/schemas/vq_browse.input"}}}}},"vq_quote.call":{"title":"tools/call vq_quote","description":"POST /mcp with this body. arguments use vq_quote.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"vq_quote"},"arguments":{"$ref":"#/components/schemas/vq_quote.input"}}}}},"proposal_manage.call":{"title":"tools/call proposal_manage","description":"POST /mcp with this body. arguments use proposal_manage.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"proposal_manage"},"arguments":{"$ref":"#/components/schemas/proposal_manage.input"}}}}},"proposal_approve.call":{"title":"tools/call proposal_approve","description":"POST /mcp with this body. arguments use proposal_approve.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"proposal_approve"},"arguments":{"$ref":"#/components/schemas/proposal_approve.input"}}}}},"talk_to_agent.call":{"title":"tools/call talk_to_agent","description":"POST /mcp with this body. arguments use talk_to_agent.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"talk_to_agent"},"arguments":{"$ref":"#/components/schemas/talk_to_agent.input"}}}}},"chat_agent_message.call":{"title":"tools/call chat_agent_message","description":"POST /mcp with this body. arguments use chat_agent_message.input.","type":"object","additionalProperties":true,"required":["jsonrpc","method","params"],"properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"const":"tools/call"},"params":{"type":"object","required":["name","arguments"],"properties":{"name":{"const":"chat_agent_message"},"arguments":{"$ref":"#/components/schemas/chat_agent_message.input"}}}}}}},"x-mcp-tools":[{"name":"estimate_browse","side_effects":"read","idempotent":true,"dry_run":false,"call":"#/components/schemas/estimate_browse.call"},{"name":"estimate_manage","side_effects":"write","idempotent":false,"dry_run":false,"call":"#/components/schemas/estimate_manage.call"},{"name":"vq_browse","side_effects":"read","idempotent":true,"dry_run":false,"call":"#/components/schemas/vq_browse.call"},{"name":"vq_quote","side_effects":"write","idempotent":false,"dry_run":true,"call":"#/components/schemas/vq_quote.call"},{"name":"proposal_manage","side_effects":"write","idempotent":false,"dry_run":false,"call":"#/components/schemas/proposal_manage.call"},{"name":"proposal_approve","side_effects":"irreversible","idempotent":false,"dry_run":true,"call":"#/components/schemas/proposal_approve.call"},{"name":"talk_to_agent","side_effects":"read","idempotent":true,"dry_run":false,"call":"#/components/schemas/talk_to_agent.call"},{"name":"chat_agent_message","side_effects":"external_message","idempotent":false,"dry_run":true,"call":"#/components/schemas/chat_agent_message.call"}],"x-not-published":["API key signup and key prefix","per-call price and free-tier quota","Retry-After","authenticated response bodies for GET /api/agent/spend and GET /api/agent/audit","GraphQL schema","MCP output schemas","timeout_ms"]}