{"openapi":"3.1.1","info":{"title":"NotFair API and MCP Reference","version":"1.0.0","description":"Public discovery and the existing NotFair Streamable HTTP MCP API for advertising, search, analytics, CRM, and WordPress integrations. Tool operations require a workspace OAuth bearer token. This is a JSON-RPC MCP API, not a REST facade for individual platform operations. Connect an MCP client for initialization, authentication, tool discovery and execution. Platform access depends on connected accounts, permissions and enabled integrations.","contact":{"name":"NotFair support","url":"https://notfair.co/contact","email":"tong@notfair.co"}},"servers":[{"url":"https://notfair.co","description":"Canonical NotFair origin"},{"url":"https://www.notfair.co","description":"Supported legacy OAuth/MCP origin; discover OAuth metadata on the same origin you connect to."}],"externalDocs":{"description":"NotFair API documentation and examples","url":"https://notfair.co/docs/reference/api"},"paths":{"/api/health":{"get":{"operationId":"getNotFairHealth","summary":"Check NotFair HTTP availability","description":"Returns an application heartbeat and current UTC timestamp. This is not a check of upstream platform credentials or service health.","security":[],"responses":{"200":{"description":"Application is responding.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"ts":{"type":"string","format":"date-time"}},"required":["ok","ts"]}}}}}}},"/.well-known/mcp/server-card.json":{"get":{"operationId":"getNotFairMcpServerCard","summary":"Discover public MCP tool schemas","description":"Returns the generated static metadata for NotFair's canonical compact tools, including input schemas, descriptions and annotations. No customer data is included. Runtime tools/list and tools/call remain authenticated.","security":[],"responses":{"200":{"description":"Static server metadata matching the compact MCP registrar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerCard"}}}}}}},"/.well-known/oauth-protected-resource/api/mcp/notfair":{"get":{"operationId":"getNotFairProtectedResource","summary":"Discover MCP authorization servers","description":"RFC 9728 metadata for the canonical NotFair MCP resource. Discover authorization server metadata on the returned origin. Preserve that origin through OAuth and validate callback issuer matching exactly.","security":[],"responses":{"200":{"description":"Protected resource metadata.","content":{"application/json":{"schema":{"type":"object","properties":{"resource":{"type":"string","format":"uri"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"resource_name":{"type":"string"},"resource_documentation":{"type":"string","format":"uri"},"resource_policy_uri":{"type":"string","format":"uri"},"resource_tos_uri":{"type":"string","format":"uri"},"logo_uri":{"type":"string","format":"uri"}},"required":["resource","authorization_servers","resource_name","resource_documentation","resource_policy_uri","resource_tos_uri","logo_uri"]}}}}}}},"/api/mcp/notfair":{"post":{"operationId":"sendNotFairMcpMessage","summary":"Send a NotFair MCP JSON-RPC message","description":"Send one MCP message per POST using Streamable HTTP. Both application/json and text/event-stream must be accepted. Every JSON-RPC method, including initialize, requires OAuth. Unauthenticated requests return HTTP 401 with a WWW-Authenticate Bearer challenge whose resource_metadata points at this resource's protected-resource document. After authorization, initialize first and send notifications/initialized before tool calls. Use search to discover capabilities, executeRead for returned read capabilities, execute for supported user-authorized writes, and submitToolFeedback to report a problem with a NotFair tool. Monthly operation quotas, workspace roles and platform permissions apply; tool failures may return HTTP 200 with isError=true. Never retry a write blindly. Use an MCP SDK to manage the protocol lifecycle and streaming responses.","security":[{"NotFairOAuth":[]}],"parameters":[{"name":"MCP-Protocol-Version","in":"header","required":false,"description":"The version negotiated during initialization; send on subsequent requests.","schema":{"type":"string"},"example":"2025-03-26"},{"name":"Mcp-Session-Id","in":"header","required":false,"description":"Send only if the server assigned a session ID during initialization.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"A single JSON-RPC request or initialization notification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpRequest"},"examples":{"initialize":{"value":{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"notfair-api-example","version":"1.0.0"}}}},"search":{"value":{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search","arguments":{"query":"list connected accounts"}}}}}}}},"responses":{"200":{"description":"A JSON-RPC response or SSE stream of JSON-RPC messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpResponse"}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events; each message event's data contains an MCP JSON-RPC message."}}}},"202":{"description":"Notification accepted; no response body."},"400":{"description":"Malformed JSON or invalid MCP request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcError"}}}},"401":{"description":"Missing, invalid, expired or incorrectly scoped workspace bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"headers":{"WWW-Authenticate":{"description":"Bearer challenge with the resource_metadata URL for this request origin.","schema":{"type":"string"}}}},"406":{"description":"Required response media types were not accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcError"}}}}}}}},"components":{"securitySchemes":{"NotFairOAuth":{"type":"oauth2","description":"Authorization code with PKCE (S256) and RFC 8707 resource=https://notfair.co/api/mcp/notfair. Discover authorization server metadata at /.well-known/oauth-authorization-server. For www clients, use that origin for discovery, issuer and resource throughout the flow.","flows":{"authorizationCode":{"authorizationUrl":"https://notfair.co/api/oauth/authorize","tokenUrl":"https://notfair.co/api/oauth/token","scopes":{}}}}},"schemas":{"McpRequest":{"oneOf":[{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"id":{"type":["string","integer"]},"method":{"const":"initialize","type":"string"},"params":{"type":"object","properties":{"protocolVersion":{"type":"string"},"capabilities":{"type":"object","additionalProperties":true},"clientInfo":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name","version"]}},"required":["protocolVersion","capabilities","clientInfo"]}},"required":["jsonrpc","id","method","params"]},{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"method":{"const":"notifications/initialized","type":"string"}},"required":["jsonrpc","method"]},{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"id":{"type":["string","integer"]},"method":{"const":"tools/list","type":"string"}},"required":["jsonrpc","id","method"]},{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"id":{"type":["string","integer"]},"method":{"const":"tools/call","type":"string"},"params":{"type":"object","properties":{"name":{"type":"string","const":"search","description":"Discover NotFair capabilities for Google Ads, Meta Ads, X Ads, LinkedIn Ads, Reddit Ads, TikTok Ads, Google Analytics, Google Search Console, GoHighLevel, and WordPress. Returns matching capability ids, exact input schemas, safety annotations, the corresponding executor, and the authenticated workspace's connected platforms with every connected account and property."},"arguments":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"query":{"type":"string","minLength":2,"description":"What you need to do, such as 'pause Meta campaign' or 'GA4 key event'."},"platform":{"type":"string","enum":["google_ads","meta_ads","x_ads","linkedin_ads","reddit_ads","tiktok_ads","search_console","google_analytics","gohighlevel","wordpress"]},"workflowId":{"description":"Optional workflow correlation UUID returned by search. Invalid values are safely replaced."}},"required":["query"]}},"required":["name","arguments"]}},"required":["jsonrpc","id","method","params"]},{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"id":{"type":["string","integer"]},"method":{"const":"tools/call","type":"string"},"params":{"type":"object","properties":{"name":{"type":"string","const":"executeRead","description":"Execute one NotFair read-only capability by its exact id and validated arguments without changing external state. Contract repairs return a stable error code, safe guidance, and the workflowId to reuse."},"arguments":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"capabilityId":{"type":"string","minLength":1,"description":"Exact read-only capability id returned by search."},"arguments":{"default":{},"description":"Arguments matching the schema returned by search.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"workflowId":{"description":"Optional workflow correlation UUID returned by search. Invalid values are safely replaced."}},"required":["capabilityId"]}},"required":["name","arguments"]}},"required":["jsonrpc","id","method","params"]},{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"id":{"type":["string","integer"]},"method":{"const":"tools/call","type":"string"},"params":{"type":"object","properties":{"name":{"type":"string","const":"execute","description":"Execute one NotFair write-capable capability by its exact id and validated arguments through the guarded platform handler. The operation may change external state. Contract repairs return a stable error code, safe guidance, and the workflowId to reuse."},"arguments":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"capabilityId":{"type":"string","minLength":1,"description":"Exact capability id returned by search."},"arguments":{"default":{},"description":"Arguments matching the schema returned by search.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"workflowId":{"description":"Optional workflow correlation UUID returned by search. Invalid values are safely replaced."},"reasoning":{"description":"Optional one-sentence reason for this change, as a string, such as \"Paused: $140 spent and 0 conversions in 30 days.\" Stored in the account's change history with the change. Longer text is cut at 1000 characters."}},"required":["capabilityId"]}},"required":["name","arguments"]}},"required":["jsonrpc","id","method","params"]},{"type":"object","properties":{"jsonrpc":{"const":"2.0","type":"string"},"id":{"type":["string","integer"]},"method":{"const":"tools/call","type":"string"},"params":{"type":"object","properties":{"name":{"type":"string","const":"submitToolFeedback","description":"Leave or file private, structured internal feedback about a bug, issue, or problem with a NotFair tool or capability encountered during a real task. Reports capture the affected capability, the expected and actual behavior, and supporting evidence for NotFair engineering. A report changes nothing in the user's connected accounts, but NotFair engineers read it along with the user and workspace that sent it: tell the user in one sentence what you will report and send it only if they agree. Summarize account details instead of pasting records, and leave out personal data."},"arguments":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"report_kind":{"type":"string","enum":["false_success","unrecoverable_failure","contract_mismatch","missing_workflow","operator_correction","capability_request"],"description":"Incident/capability class. false_success requires an authoritative readback; operator_correction requires an explicit owner correction."},"affected_tool":{"type":"string","minLength":1,"maxLength":120,"description":"NotFair's own tool surface only: one real tool name (e.g. 'pauseKeyword'). Use 'general' only when actual names multiple NotFair tools; never name an external connector or harness."},"problem_signature":{"type":"string","minLength":3,"maxLength":120,"pattern":"^[a-z0-9]+(?:[._-][a-z0-9]+){1,11}$","description":"Stable root-cause key reused for the same underlying problem, e.g. update-campaign-goals.custom-goal-name."},"expected":{"type":"string","minLength":10,"maxLength":1000,"description":"What the agent reasonably expected from the NotFair tool surface or documented workflow."},"actual":{"type":"string","minLength":10,"maxLength":1000,"description":"What actually happened. Include the exact result/error/readback fact that proves it."},"evidence_kind":{"type":"string","enum":["tool_result","authoritative_readback","documentation","workflow_trace","owner_correction"],"description":"How actual was established. false_success requires authoritative_readback."},"suggestion":{"description":"Optional implementation hypothesis. Omit if the evidence does not support a specific fix.","type":"string","minLength":10,"maxLength":1000},"user_goal":{"description":"What the user was trying to accomplish — gives the team the use case context. Avoid PII.","type":"string","maxLength":500},"related_request_id":{"description":"Optional request_id of the earlier NotFair call that caused friction, when the caller has it. This is not a conversation id.","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)$"},"source":{"default":"agent","description":"agent=live product signal; eval=controlled regression signal. Eval reports are excluded from customer-signal metrics.","type":"string","enum":["agent","eval"]},"idempotency_key":{"description":"Optional dedup key. If the same key is submitted by this owner, the original feedback_id is returned without creating a duplicate. Omit to deduplicate this owner's reports by source + platform + report_kind + affected_tool + problem_signature.","type":"string","minLength":1,"maxLength":128},"workflowId":{"description":"Optional workflow correlation UUID returned by search. Invalid values are safely replaced."}},"required":["report_kind","affected_tool","problem_signature","expected","actual","evidence_kind"]}},"required":["name","arguments"]}},"required":["jsonrpc","id","method","params"]}]},"McpResponse":{"oneOf":[{"type":"object","properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","integer"]},"result":{"oneOf":[{"$ref":"#/components/schemas/InitializeResult"},{"$ref":"#/components/schemas/ToolListResult"},{"$ref":"#/components/schemas/CallToolResult"}]}},"required":["jsonrpc","id","result"]},{"$ref":"#/components/schemas/RpcError"}]},"InitializeResult":{"type":"object","properties":{"protocolVersion":{"type":"string"},"capabilities":{"type":"object","additionalProperties":true},"serverInfo":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name","version"]},"instructions":{"type":"string"}},"required":["protocolVersion","capabilities","serverInfo"]},"ToolListResult":{"type":"object","properties":{"tools":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}},"nextCursor":{"type":"string"}},"required":["tools"]},"CallToolResult":{"type":"object","properties":{"content":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["text","image","audio","resource","resource_link"]},"text":{"type":"string"},"data":{"type":"string"},"mimeType":{"type":"string"},"resource":{"type":"object","additionalProperties":true}},"required":["type"]}},"structuredContent":{"type":"object","additionalProperties":true},"isError":{"type":"boolean"}},"required":["content"]},"RpcError":{"type":"object","properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","integer","null"]},"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"data":{}},"required":["code","message"]}},"required":["jsonrpc","id","error"]},"Unauthorized":{"type":"object","properties":{"error":{"type":"string"},"oauth":{"type":"object","properties":{"protocol":{"type":"string"},"preferred_flow":{"type":"string"},"authorization_code_pkce":{"type":"boolean"},"resource_metadata":{"type":"string","format":"uri"}},"required":["protocol","preferred_flow","authorization_code_pkce","resource_metadata"]},"fallback":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"label":{"type":"string"},"preferred":{"type":"boolean"}},"required":["url","label","preferred"]}},"required":["error","oauth","fallback"]},"Tool":{"type":"object","properties":{"name":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"inputSchema":{"type":"object","additionalProperties":true},"outputSchema":{"type":"object","additionalProperties":true},"annotations":{"type":"object","properties":{"title":{"type":"string"},"readOnlyHint":{"type":"boolean"},"destructiveHint":{"type":"boolean"},"idempotentHint":{"type":"boolean"},"openWorldHint":{"type":"boolean"}},"required":[]}},"required":["name","inputSchema"]},"ServerCard":{"type":"object","properties":{"serverInfo":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name","version"]},"authentication":{"type":"object","properties":{"required":{"type":"boolean","const":true},"schemes":{"type":"array","items":{"type":"string","const":"oauth2"}}},"required":["required","schemes"]},"tools":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}},"resources":{"type":"array","items":{"type":"object","properties":{"uri":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"mimeType":{"type":"string"}},"required":["uri","name"]}},"prompts":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"arguments":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"}},"required":["name"]}}},"required":["name"]}}},"required":["serverInfo","authentication","tools","resources","prompts"]}}}}