{"openapi":"3.1.0","info":{"title":"Fanflet Agent API","summary":"MCP HTTP transport and OAuth 2.1 discovery for Fanflet.","description":"Fanflet's public agent surface is the Model Context Protocol server at /api/mcp, authenticated with OAuth 2.1 + PKCE. Version 1 is current. Unversioned /api/mcp is v1; /api/v1/mcp is an alias. Responses send X-API-Version: 1. Deprecated routes will send Deprecation and Sunset (RFC 8594) at least 90 days before removal. There is no public REST of speaker-generated content and no API-key product. Speakers need Pro or Studio. See /developers, /llms.txt, and /docs/mcp-for-agents.","version":"1.0.0","contact":{"name":"Fanflet","url":"https://fanflet.com/contact","email":"support@fanflet.com"}},"servers":[{"url":"https://fanflet.com","description":"Fanflet API v1 (current). /api/mcp is v1."}],"tags":[{"name":"MCP","description":"Streamable HTTP MCP transport"},{"name":"OAuth","description":"OAuth 2.1 with PKCE and RFC 9728 metadata"},{"name":"Discovery","description":"Unauthenticated machine-readable files"}],"paths":{"/api":{"get":{"operationId":"getApiCatalog","tags":["Discovery"],"summary":"Agent API catalog","description":"Unauthenticated JSON index of Fanflet developer URLs, versioning, and rate-limit conventions.","security":[],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"]},"description":"Major API version. Current value is 1. Unversioned /api/mcp is v1."}],"responses":{"200":{"description":"Catalog of agent endpoints.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiCatalog"}}}}}}},"/api/mcp":{"get":{"operationId":"mcpTransportGet","tags":["MCP"],"summary":"MCP Streamable HTTP (GET)","description":"Unauthenticated GET returns 401 JSON plus WWW-Authenticate pointing at protected-resource metadata. Authenticated GET is the MCP Streamable HTTP transport.","security":[{"oauth2":["admin","speaker","sponsor","audience"]},{"bearerAuth":[]}],"responses":{"200":{"description":"MCP session or JSON-RPC payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"Missing or invalid bearer token. Start OAuth.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}},"403":{"description":"Speaker is on Free, or role is not allowed on this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}},"500":{"description":"Transport failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}}}},"post":{"operationId":"mcpTransportPost","tags":["MCP"],"summary":"MCP Streamable HTTP (POST)","description":"JSON-RPC MCP messages. Requires a bearer token issued by /api/mcp/token. Tools are role-scoped (speaker, sponsor, audience, admin).","security":[{"oauth2":["admin","speaker","sponsor","audience"]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}},"403":{"description":"Plan or role does not allow this MCP endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}},"500":{"description":"Transport failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}}}},"delete":{"operationId":"mcpTransportDelete","tags":["MCP"],"summary":"End an MCP HTTP session","description":"Closes a Streamable HTTP session for the authenticated client.","security":[{"oauth2":["admin","speaker","sponsor","audience"]},{"bearerAuth":[]}],"responses":{"200":{"description":"Session closed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}},"403":{"description":"Plan or role does not allow this MCP endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}}}}},"/api/mcp/authorize":{"get":{"operationId":"oauthAuthorize","tags":["OAuth"],"summary":"OAuth 2.1 authorization endpoint","description":"Starts the authorization-code + PKCE flow. Redirects the user to log in, then back to an allowlisted redirect_uri with `code`.","security":[],"parameters":[{"name":"client_id","in":"query","required":true,"schema":{"type":"string"},"description":"Client id from /api/mcp/register."},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri"},"description":"Must be Claude, ChatGPT, or http://localhost."},{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"code_challenge","in":"query","required":true,"schema":{"type":"string"},"description":"PKCE S256 challenge."},{"name":"code_challenge_method","in":"query","required":false,"schema":{"type":"string","enum":["S256"]}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"scope","in":"query","required":false,"schema":{"type":"string"},"description":"One of admin, speaker, sponsor, audience."}],"responses":{"302":{"description":"Redirect to login or to redirect_uri with code."},"400":{"description":"Missing or invalid OAuth parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}}}}},"/api/mcp/token":{"post":{"operationId":"oauthToken","tags":["OAuth"],"summary":"OAuth token endpoint","description":"Exchange an authorization code (or refresh token) for a bearer access token.","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string"},"client_id":{"type":"string"},"code_verifier":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"refresh_token":{"type":"string"}}}},"application/json":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string"},"client_id":{"type":"string"},"code_verifier":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"refresh_token":{"type":"string"}}}}}},"responses":{"200":{"description":"Access token payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenResponse"}}}},"400":{"description":"Token exchange failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}}}}},"/api/mcp/register":{"post":{"operationId":"oauthRegister","tags":["OAuth"],"summary":"OAuth dynamic client registration","description":"RFC 7591-style registration. Every redirect_uri must pass the allowlist (Claude, ChatGPT, localhost).","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1},"client_name":{"type":"string"},"client_uri":{"type":"string","format":"uri"},"logo_uri":{"type":"string","format":"uri"},"scope":{"type":"string"},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"}}}}}},"responses":{"201":{"description":"Registered client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClient"}}}},"400":{"description":"Invalid client metadata or redirect_uri.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}},"429":{"description":"Registration rate limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentError"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"oauthAuthorizationServerMetadata","tags":["Discovery"],"summary":"RFC 8414 authorization server metadata","description":"Unauthenticated. Includes scopes_supported: admin, speaker, sponsor, audience.","security":[],"responses":{"200":{"description":"Authorization server metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationServerMetadata"},"example":{"issuer":"https://fanflet.com","authorization_endpoint":"https://fanflet.com/api/mcp/authorize","token_endpoint":"https://fanflet.com/api/mcp/token","registration_endpoint":"https://fanflet.com/api/mcp/register","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token"],"token_endpoint_auth_methods_supported":["none"],"code_challenge_methods_supported":["S256"],"scopes_supported":["admin","speaker","sponsor","audience"],"service_documentation":"https://fanflet.com/docs/mcp-for-agents"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"oauthProtectedResourceMetadata","tags":["Discovery"],"summary":"RFC 9728 protected resource metadata (origin)","description":"Unauthenticated discovery of the Fanflet MCP resource and its scopes.","security":[],"responses":{"200":{"description":"Protected resource metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProtectedResourceMetadata"},"example":{"resource":"https://fanflet.com/api/mcp","authorization_servers":["https://fanflet.com"],"scopes_supported":["admin","speaker","sponsor","audience"],"bearer_methods_supported":["header"],"resource_name":"Fanflet MCP Server","resource_documentation":"https://fanflet.com/docs/mcp-for-agents"}}}}}}},"/.well-known/oauth-protected-resource/api/mcp":{"get":{"operationId":"oauthProtectedResourceMetadataMcp","tags":["Discovery"],"summary":"RFC 9728 protected resource metadata (MCP path)","description":"Path-specific RFC 9728 document. MCP clients should fetch this URL from WWW-Authenticate on 401.","security":[],"responses":{"200":{"description":"Protected resource metadata for /api/mcp.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProtectedResourceMetadata"},"example":{"resource":"https://fanflet.com/api/mcp","authorization_servers":["https://fanflet.com"],"scopes_supported":["admin","speaker","sponsor","audience"],"bearer_methods_supported":["header"],"resource_name":"Fanflet MCP Server","resource_documentation":"https://fanflet.com/docs/mcp-for-agents"}}}}}}},"/.well-known/mcp.json":{"get":{"operationId":"getMcpManifest","tags":["MCP"],"summary":"MCP discovery manifest","description":"Unauthenticated MCP server manifest (SEP-1960 style). Streamable HTTP at /api/mcp.","security":[],"responses":{"200":{"description":"MCP manifest.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpManifest"}}}}}}},"/.well-known/mcp/server-card.json":{"get":{"operationId":"getMcpServerCard","tags":["MCP"],"summary":"MCP server card","description":"Unauthenticated MCP server card (SEP-1649 style).","security":[],"responses":{"200":{"description":"MCP server card.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpManifest"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","tags":["Discovery"],"summary":"This OpenAPI document","description":"Unauthenticated OpenAPI 3.1 description of the agent-reachable surface.","security":[],"responses":{"200":{"description":"OpenAPI 3.1 JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}}}}},"/llms.txt":{"get":{"operationId":"getLlmsTxt","tags":["Discovery"],"summary":"Agent instruction file","description":"Markdown index: when to use Fanflet, MCP/OAuth how-to, developer URLs.","security":[],"responses":{"200":{"description":"text/markdown body.","content":{"text/markdown":{"schema":{"type":"string"}}}}}}}},"components":{"schemas":{"AgentError":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","description":"Machine-readable error code (OAuth or Fanflet).","examples":["unauthorized","invalid_request","subscription_required"]},"message":{"type":"string","description":"Human-readable explanation."},"error_description":{"type":"string","description":"OAuth 2.1 alias of message, for clients that only read this field."},"resolution":{"type":"string","description":"What the agent should do next."},"plan_required":{"type":"string"},"upgrade_url":{"type":"string","format":"uri"}}},"JsonRpcRequest":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"method":{"type":"string"},"params":{"type":"object","additionalProperties":true},"id":{"description":"JSON-RPC id","oneOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]}}},"JsonRpcResponse":{"type":"object","required":["jsonrpc"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"result":{"type":"object","additionalProperties":true},"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"}}},"id":{"oneOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]}}},"OAuthTokenResponse":{"type":"object","required":["access_token","token_type"],"properties":{"access_token":{"type":"string"},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer"},"refresh_token":{"type":"string"},"scope":{"type":"string"}}},"OAuthClient":{"type":"object","required":["client_id"],"properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"}},"token_endpoint_auth_method":{"type":"string"}}},"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"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"}}}},"ProtectedResourceMetadata":{"type":"object","required":["resource","authorization_servers","scopes_supported"],"properties":{"resource":{"type":"string","format":"uri"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"bearer_methods_supported":{"type":"array","items":{"type":"string"}},"resource_name":{"type":"string"},"resource_documentation":{"type":"string","format":"uri"}}},"ApiCatalog":{"type":"object","required":["name","version","links"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"description":{"type":"string"},"links":{"type":"object","additionalProperties":{"type":"string"}},"versioning":{"type":"object","additionalProperties":{"type":"string"}},"rate_limit":{"type":"object","additionalProperties":true}}},"McpManifest":{"type":"object","required":["mcp_version","endpoints"],"properties":{"mcp_version":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"endpoints":{"type":"array","items":{"type":"object","required":["url","transport"],"properties":{"url":{"type":"string","format":"uri"},"transport":{"type":"string","enum":["streamable-http"]}}}}}},"OpenApiDocument":{"type":"object","required":["openapi","info","paths"],"properties":{"openapi":{"type":"string"},"info":{"type":"object","additionalProperties":true},"paths":{"type":"object","additionalProperties":true}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"OAuth2 access token","description":"Access token from POST /api/mcp/token."},"oauth2":{"type":"oauth2","description":"OAuth 2.1 authorization code + PKCE. Named scopes match RFC 9728 scopes_supported.","flows":{"authorizationCode":{"authorizationUrl":"https://fanflet.com/api/mcp/authorize","tokenUrl":"https://fanflet.com/api/mcp/token","refreshUrl":"https://fanflet.com/api/mcp/token","scopes":{"admin":"Platform administrator tools across tenants. Restricted to platform_admin identities.","speaker":"Speaker workspace tools (fanflets, resources, subscribers, analytics). Requires a Pro or Studio plan.","sponsor":"Sponsor workspace tools (connections, leads, campaigns, analytics).","audience":"Audience tools (saved fanflets, following, subscriber identity)."}}}}}},"externalDocs":{"description":"Fanflet MCP and OAuth (human + agent)","url":"https://fanflet.com/docs/mcp-for-agents"},"x-mcp-endpoint":"https://fanflet.com/api/mcp"}