{"openapi":"3.1.0","info":{"title":"Orkestrate Gateway API","version":"0.3.0","description":"Orkestrate is the call layer for AI agents at orkestrate.space — one MCP endpoint for every product agent. This spec describes the Gateway HTTP API and MCP tools for callers and publishers. All endpoints return structured JSON errors with code, message and hint. Publisher endpoints require DNS TXT verification at _orkestrate.<domain>. Free tier with self-serve API keys and sandbox at https://orkestrate.space/chat.","contact":{"name":"Orkestrate Support","url":"https://orkestrate.space/contact","email":"support@orkestrate.space"},"license":{"name":"MIT","url":"https://github.com/system1970/orkestrate/blob/main/LICENSE"},"termsOfService":"https://orkestrate.space/legal/terms"},"servers":[{"url":"https://orkestrate.space","description":"Production"},{"url":"http://localhost:3000","description":"Local dev"}],"tags":[{"name":"MCP","description":"Model Context Protocol — Streamable HTTP at /mcp and /api/mcp"},{"name":"Caller","description":"Consumer — discover and talk to product agents"},{"name":"Publisher","description":"Publisher — claim domains, register agents, go live"},{"name":"Orgs","description":"Organizations, teams and audit"},{"name":"Billing","description":"Subscriptions and invoices"},{"name":"Health","description":"Health and metadata"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"WorkOS MCP OAuth access token. Obtain via OAuth 2.1 PKCE flow at /.well-known/oauth-authorization-server. Scopes: mcp:read, mcp:write, publisher:manage, caller:chat, offline_access. Token verified against JWKS on every request. For server-to-server, use Authorization: Bearer <token>."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Orkestrate-Api-Key","description":"Alternative API key for scoped publisher access. Roles: publisher:read, publisher:write, caller:read. Request only the scopes you need."},"oauth2":{"type":"oauth2","description":"OAuth 2.1 with PKCE for MCP clients. Scopes: openid, email, profile, offline_access, mcp:read, mcp:write, publisher:manage, caller:chat","flows":{"authorizationCode":{"authorizationUrl":"https://orkestrate.space/.well-known/oauth-authorization-server","tokenUrl":"https://api.workos.com/oauth/token","scopes":{"openid":"OpenID","email":"Email","profile":"Profile","offline_access":"Refresh token","mcp:read":"Read MCP tools and agents","mcp:write":"Invoke MCP tools and sessions","publisher:manage":"Claim domains and manage agents","caller:chat":"Start sessions and send messages"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code","enum":["AgentNotFound","SessionNotFound","SessionExpired","SessionStale","SessionInFlight","AuthFailed","RateLimited","PublisherUnreachable","MessageTooLarge","ModelError","MalformedRequest","DomainNotVerified","DomainClaimConflict","AgentNameConflict","OrgRequired","AccessDenied","NotFound","GatewayInternal","UNAUTHORIZED","BAD_REQUEST","LIMIT_EXCEEDED","SESSION_EXPIRED","UPSTREAM_ERROR","GATEWAY_ERROR"]},"message":{"type":"string","description":"Human-readable message"},"hint":{"type":"string","description":"Resolution hint for agents and humans"},"details":{"type":"object","additionalProperties":true,"description":"Optional structured details"},"doc":{"type":"string","format":"uri","description":"Link to docs for this error"}}}},"example":{"error":{"code":"AgentNotFound","message":"No live agent for domain example.com","hint":"Call find_agent with domain or search, then retry start_session","doc":"https://orkestrate.space/docs/reference#error-codes"}}},"Agent":{"type":"object","required":["agentName","domain","status"],"properties":{"agentName":{"type":"string","example":"linear"},"domain":{"type":"string","format":"hostname","example":"linear.app"},"status":{"type":"string","enum":["pending","live","suspended","removed"],"example":"live"},"verificationStatus":{"type":"string","example":"verified"},"description":{"type":"string","nullable":true},"publisherUrl":{"type":"string","format":"uri","example":"https://linear.app/api/orkestrate"}}},"Session":{"type":"object","required":["sessionId","agentName","status"],"properties":{"sessionId":{"type":"string","pattern":"^ses_","example":"ses_01jk47m8q9"},"agentName":{"type":"string"},"status":{"type":"string","enum":["active","closed","expired"]},"turnCount":{"type":"integer","minimum":0,"maximum":40},"createdAt":{"type":"string","format":"date-time"},"lastActivityAt":{"type":"string","format":"date-time"}}},"McpToolInput":{"type":"object","description":"Input schema for MCP tools — see each tool's description"}},"responses":{"Unauthorized":{"description":"Authentication required — use OAuth flow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"AuthFailed","message":"Authentication required. Use the OAuth flow to obtain an access token.","hint":"Sign in via WorkOS OAuth at https://orkestrate.space/sign-in, then retry with Authorization: Bearer <token>"}}}},"headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"Bearer error with resource_metadata"}}},"BadRequest":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"Retry-After":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"externalDocs":{"description":"Orkestrate docs — caller, publisher, wire protocol","url":"https://orkestrate.space/docs"},"paths":{"/mcp":{"get":{"operationId":"mcpGet","summary":"MCP Streamable HTTP — GET (SSE)","description":"Open an SSE stream for MCP. Requires Bearer OAuth token. Returns session events. Use POST for tool calls. Transport: Streamable HTTP. See https://orkestrate.space/docs/reference","tags":["MCP"],"security":[{"bearerAuth":["mcp:read"]}],"responses":{"200":{"description":"SSE stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"mcpPost","summary":"MCP Streamable HTTP — POST tool calls","description":"Invoke MCP tools via Streamable HTTP. Tools: find_agent, start_session, send_message, end_session, claim_domain, verify_domain, list_my_domains, register_agent, remove_agent, list_my_agents, go_live. Requires Bearer OAuth. Content-Type: application/json, Accept: application/json, text/event-stream","tags":["MCP"],"security":[{"bearerAuth":["mcp:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"],"example":"2.0"},"id":{"type":["string","number"]},"method":{"type":"string","enum":["tools/call","tools/list","initialize","ping"],"description":"MCP method"},"params":{"type":"object","properties":{"name":{"type":"string","description":"Tool name for tools/call"},"arguments":{"type":"object","description":"Tool arguments","additionalProperties":true}}}}},"examples":{"find_agent":{"summary":"find_agent by domain","value":{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"find_agent","arguments":{"domain":"linear.app"}}}},"start_session":{"summary":"start_session","value":{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"start_session","arguments":{"domain":"linear.app","message":"How do I create an issue?"}}}}}}}},"responses":{"200":{"description":"MCP response","content":{"application/json":{"schema":{"type":"object"},"example":{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"sessionId\":\"ses_123\",\"reply\":\"...\"}"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"delete":{"operationId":"mcpDelete","summary":"MCP Streamable HTTP — DELETE session","description":"Terminate MCP session. Requires mcp-session-id header.","tags":["MCP"],"security":[{"bearerAuth":["mcp:write"]}],"parameters":[{"name":"mcp-session-id","in":"header","required":false,"schema":{"type":"string"},"description":"MCP session id"}],"responses":{"200":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/mcp":{"get":{"operationId":"apiMcpGet","summary":"Alias for /mcp GET","tags":["MCP"],"security":[{"bearerAuth":["mcp:read"]}],"responses":{"200":{"description":"SSE"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"apiMcpPost","summary":"Alias for /mcp POST","tags":["MCP"],"security":[{"bearerAuth":["mcp:write"]}],"responses":{"200":{"description":"MCP response"}}}},"/api/health":{"get":{"operationId":"getHealth","summary":"Health check","description":"Returns db connectivity and latency. No auth required. Useful for uptime and sandbox checks.","tags":["Health"],"security":[],"responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"type":"object","required":["ok","db","ms","ts"],"properties":{"ok":{"type":"boolean"},"db":{"type":"string","enum":["connected","disconnected"]},"ms":{"type":"number"},"ts":{"type":"string","format":"date-time"}}}}}},"503":{"description":"Unhealthy","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/caller/chat":{"post":{"operationId":"callerChat","summary":"Caller chat — start or continue a session via REST","description":"REST alternative to MCP for browser callers. Requires WorkOS session cookie. Provide agent or domain plus message, or sessionId to continue. Returns reply and sessionId. Scoped to caller:chat.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"agent":{"type":"string","description":"Agent name","example":"linear"},"domain":{"type":"string","description":"Verified domain","example":"linear.app"},"message":{"type":"string","description":"User message","maxLength":102400},"sessionId":{"type":"string","description":"Existing session to continue","pattern":"^ses_"}}}}}},"responses":{"200":{"description":"Chat reply","content":{"application/json":{"schema":{"type":"object","required":["ok","sessionId","reply"],"properties":{"ok":{"type":"boolean"},"sessionId":{"type":"string"},"reply":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"description":"Model not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/caller/sessions":{"get":{"operationId":"listCallerSessions","summary":"List caller sessions","description":"List sessions for the authenticated caller. Paginated, includes turn counts.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Sessions","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/Session"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createCallerSession","summary":"Create caller session (REST)","description":"Create a session via REST. See /api/caller/chat for chat variant.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"responses":{"200":{"description":"Created"}}}},"/api/caller/sessions/{id}/messages":{"get":{"operationId":"getSessionMessages","summary":"Get session messages","description":"Retrieve messages for a session owned by caller. Ephemeral — deleted on close.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Session id"}],"responses":{"200":{"description":"Messages","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/caller/settings":{"get":{"operationId":"getCallerSettings","summary":"Get caller settings","description":"Returns tier and showAllAgents preference. Used for Discover filtering.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"responses":{"200":{"description":"Settings"}}},"put":{"operationId":"updateCallerSettings","summary":"Update caller settings","description":"Update showAllAgents or tier preference.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"showAllAgents":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated"}}}},"/api/caller/connections":{"get":{"operationId":"listCallerConnections","summary":"List caller product connections","description":"Lists product connections for the caller, with status pending/approved/revoked.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"responses":{"200":{"description":"Connections"}}},"post":{"operationId":"requestCallerConnection","summary":"Request connection to a product","description":"Request access to a product that requires approval.","tags":["Caller"],"security":[{"bearerAuth":["caller:chat"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string"}}}}}},"responses":{"200":{"description":"Requested"}}}},"/api/publisher/agents":{"get":{"operationId":"listPublisherAgents","summary":"List publisher agents","description":"List agents for the authenticated publisher org. Requires publisher:manage scope.","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"Agents","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"$ref":"#/components/schemas/Agent"}}}}}}}}},"post":{"operationId":"registerPublisherAgent","summary":"Register a new agent","description":"Register agent on a verified domain. Requires publisher:manage. Body: agentName, domain, secret (optional, generated if omitted), description, goLive boolean, productId. Returns secret once, connectivity and live status. Agent name must match domain SLD.","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]},{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agentName","domain"],"properties":{"agentName":{"type":"string","pattern":"^[a-z0-9-]{3,32}$","description":"Must match domain SLD","example":"linear"},"domain":{"type":"string","format":"hostname","example":"linear.app"},"secret":{"type":"string","description":"Publisher secret; generated if omitted"},"description":{"type":"string","maxLength":500},"goLive":{"type":"boolean","default":false},"productId":{"type":"string"},"organizationId":{"type":"string"}}}}}},"responses":{"200":{"description":"Registered","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"409":{"description":"Name conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/publisher/agents/{name}":{"get":{"operationId":"getPublisherAgent","summary":"Get publisher agent by name","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agent"},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"removePublisherAgent","summary":"Remove publisher agent","description":"Soft-delete; name reserved 7 days.","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Removed"}}}},"/api/publisher/agents/{name}/check":{"get":{"operationId":"checkPublisherAgent","summary":"Check publisher agent connectivity","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Connectivity"}}}},"/api/publisher/agents/{name}/live":{"post":{"operationId":"goLivePublisherAgent","summary":"Promote agent to live","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Live status"}}}},"/api/publisher/domains":{"get":{"operationId":"listPublisherDomains","summary":"List domain claims","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"responses":{"200":{"description":"Domains"}}},"post":{"operationId":"claimPublisherDomain","summary":"Claim a domain","description":"Claim domain for DNS verification. Returns TXT token. Pending 7 days.","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string","example":"example.com"}}}}}},"responses":{"200":{"description":"Claimed"}}}},"/api/publisher/domains/{domain}/verify":{"post":{"operationId":"verifyPublisherDomain","summary":"Verify domain DNS TXT","description":"Check _orkestrate.<domain> TXT matches token. Uses Google DNS.","tags":["Publisher"],"security":[{"bearerAuth":["publisher:manage"]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verified"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/api/agents/directory":{"get":{"operationId":"listAgentDirectory","summary":"List live agent directory","description":"Public directory of live agents. Auth optional; shows more when authenticated.","tags":["Caller"],"security":[],"responses":{"200":{"description":"Directory","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"$ref":"#/components/schemas/Agent"}}}}}}}}}},"/api/orkestrate":{"get":{"operationId":"orkestratePing","summary":"Gateway ping for publisher health","description":"GET returns { ok: true, protocol: 1 } when handler is live. Used for connectivity checks.","tags":["Publisher"],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"protocol":{"type":"integer"}}}}}}}},"post":{"operationId":"orkestrateTurn","summary":"Gateway turn for publisher","description":"POST from gateway to publisher. Headers: Authorization Bearer <secret>, X-Orkestrate-Action, X-Orkestrate-Session-Id, X-Orkestrate-Model (BYOM token). Body: message, messages. Returns { reply: string }.","tags":["Publisher"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"messages":{"type":"array","items":{"type":"object"}}}}}}},"responses":{"200":{"description":"Reply"}}}},"/api/proxy/llm":{"post":{"operationId":"proxyLlm","summary":"Proxy LLM call with BYOM token","description":"Publisher calls this to execute LLM with caller BYOM token. Headers: x-orkestrate-proxy-token, x-orkestrate-original-url. Token pinned to session, turn, provider domain, 10m TTL. Rate limited per turn.","tags":["Publisher"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Upstream proxied"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"getOAuthAuthorizationServer","summary":"OAuth authorization server metadata (RFC 8414)","tags":["Health"],"security":[],"responses":{"200":{"description":"Metadata"}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"operationId":"getOAuthProtectedResourceMcp","summary":"MCP protected resource metadata (RFC 9728)","tags":["Health"],"security":[],"responses":{"200":{"description":"Metadata"}}}},"/.well-known/mcp":{"get":{"operationId":"getMcpWellKnown","summary":"MCP server manifest for discovery","description":"Returns MCP server manifest with name, version, endpoint, tools, transport. For Claude, ChatGPT, etc.","tags":["MCP"],"security":[],"responses":{"200":{"description":"Manifest"}}}},"/openapi.json":{"get":{"operationId":"getOpenApiJson","summary":"OpenAPI spec JSON","tags":["Health"],"security":[],"responses":{"200":{"description":"OpenAPI JSON"}}}},"/sitemap.xml":{"get":{"operationId":"getSitemap","summary":"Sitemap XML","tags":["Health"],"security":[],"responses":{"200":{"description":"XML"}}}},"/llms.txt":{"get":{"operationId":"getLlmsTxt","summary":"LLMs.txt for agent discovery","tags":["Health"],"security":[],"responses":{"200":{"description":"Markdown"}}}}}}