{"openapi":"3.1.0","info":{"title":"SalesHog API","version":"1.0.0","summary":"MCP server and OAuth 2.1 API for SalesHog.","description":"SalesHog is an AI-powered platform that monitors public conversations in real time to surface buying signals and brand mentions. The AI drafts personalized outreach for you to review, edit, and approve before anything is sent.\n\nSalesHog exposes a Model Context Protocol (MCP) server at /mcp for AI agents, protected by OAuth 2.1 with dynamic client registration. All errors are structured JSON with machine-readable codes and resolution hints. Rate-limited endpoints return RateLimit-* headers and Retry-After on HTTP 429.","contact":{"name":"SalesHog Support","email":"support@saleshog.co","url":"https://saleshog.co/contact"},"license":{"name":"Proprietary","identifier":"LicenseRef-Proprietary"}},"servers":[{"url":"https://saleshog.co","description":"Production"}],"tags":[{"name":"MCP","description":"Model Context Protocol server for AI agents (JSON-RPC 2.0 over HTTP)."},{"name":"OAuth","description":"OAuth 2.1 authorization server endpoints."},{"name":"Discovery","description":"Machine-readable metadata documents."},{"name":"Account","description":"Session-authenticated account endpoints."}],"components":{"securitySchemes":{"mcpOAuth":{"type":"oauth2","description":"OAuth 2.1 authorization code flow with PKCE (S256). Request the \"mcp\" scope.","flows":{"authorizationCode":{"authorizationUrl":"https://saleshog.co/oauth/authorize","tokenUrl":"https://saleshog.co/oauth/token","refreshUrl":"https://saleshog.co/oauth/token","scopes":{"mcp":"Access the SalesHog MCP tools (signals, mentions, outreach drafts) for one business workspace."}}}},"mcpBearer":{"type":"http","scheme":"bearer","description":"MCP access token obtained via the OAuth 2.1 authorization code flow (valid 1 hour)."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"__Secure-better-auth.session_token","description":"Browser session cookie created by signing in at /sign-in. For interactive use only; agents should use OAuth instead."}},"schemas":{"ApiError":{"type":"object","description":"Structured error returned by all JSON APIs.","required":["error","code","message"],"properties":{"error":{"type":"string","description":"Short human-readable error message."},"code":{"type":"string","description":"Machine-readable error code.","enum":["bad_request","unauthorized","forbidden","not_found","method_not_allowed","conflict","rate_limited","validation_failed","server_error"]},"message":{"type":"string","description":"Same as error; present so clients can read either field."},"hint":{"type":"string","description":"Resolution hint describing how to fix the request."}}},"JsonRpcRequest":{"type":"object","required":["jsonrpc","id","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","number"],"description":"Request identifier echoed back in the response."},"method":{"type":"string","enum":["initialize","notifications/initialized","tools/list","tools/call"],"description":"JSON-RPC method."},"params":{"type":"object","properties":{"name":{"type":"string","description":"Tool name for tools/call, e.g. getSignals or draftOutreach."},"arguments":{"type":"object","additionalProperties":true,"description":"Tool arguments; see tools/list for each tool's inputSchema."}}}}},"JsonRpcResponse":{"type":"object","required":["jsonrpc","id"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","number"]},"result":{"type":"object","additionalProperties":true},"error":{"type":"object","properties":{"code":{"type":"integer","description":"JSON-RPC error code."},"message":{"type":"string"}}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"string"},"description":"Allowed requests per window, e.g. \"12\"."},"RateLimit-Remaining":{"schema":{"type":"string"},"description":"Requests remaining in the current window."},"RateLimit-Reset":{"schema":{"type":"string"},"description":"Seconds until the current window resets (delta-seconds)."},"Retry-After":{"schema":{"type":"string"},"description":"Seconds to wait before retrying (sent on HTTP 429)."},"X-RateLimit-Limit":{"schema":{"type":"string"},"description":"Legacy per-window limit (same value as RateLimit-Limit)."},"X-RateLimit-Remaining":{"schema":{"type":"string"},"description":"Legacy remaining count."},"X-RateLimit-Reset":{"schema":{"type":"string"},"description":"Legacy reset time as UNIX epoch seconds."}}},"paths":{"/mcp":{"post":{"operationId":"mcpJsonRpc","tags":["MCP"],"summary":"SalesHog MCP server (JSON-RPC 2.0)","description":"Model Context Protocol endpoint. Initialize first, then list/call tools such as getBusinessInfo, getSignals, getMentions, getMetrics, draftOutreach, showDraft, and sendOutreach. Outreach tools never send without explicit user approval recorded in the product UI. Requires a Bearer access token issued for the 'mcp' scope via OAuth 2.1 (see /docs). Send rate is limited to 12 sendOutreach calls per minute per token+IP.","security":[{"mcpBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC result (check body for JSON-RPC level errors).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"401":{"description":"Missing or invalid access token (JSON-RPC error -32000).","headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"Bearer challenge with resource_metadata URL."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"429":{"description":"Rate limited (sendOutreach). Honor Retry-After.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}}}},"options":{"operationId":"mcpCorsPreflight","tags":["MCP"],"summary":"CORS preflight for the MCP endpoint","description":"Returns CORS headers allowing POST with Content-Type and Authorization headers.","responses":{"204":{"description":"No content."}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"getOAuthAuthorizationServerMetadata","tags":["Discovery"],"summary":"OAuth 2.0 Authorization Server Metadata (RFC 8414)","description":"Discovery document listing authorization, token, registration, and revocation endpoints plus supported scopes.","security":[],"responses":{"200":{"description":"Authorization server metadata.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"issuer":"https://saleshog.co","authorization_endpoint":"https://saleshog.co/oauth/authorize","token_endpoint":"https://saleshog.co/oauth/token","registration_endpoint":"https://saleshog.co/oauth/register","revocation_endpoint":"https://saleshog.co/oauth/revoke","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none"],"scopes_supported":["mcp"],"authorization_response_iss_parameter_supported":true,"revocation_endpoint_auth_methods_supported":["none"]}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"getOAuthProtectedResourceMetadata","tags":["Discovery"],"summary":"OAuth 2.0 Protected Resource Metadata","description":"Identifies /mcp as an OAuth 2.0 protected resource and points to the authorization server.","security":[],"responses":{"200":{"description":"Protected resource metadata.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth/register":{"post":{"operationId":"registerOAuthClient","tags":["OAuth"],"summary":"Dynamic client registration (RFC 7591)","description":"Registers a public OAuth client. Only token_endpoint_auth_method=none is supported; redirect URIs must be HTTPS or loopback (127.0.0.1/localhost). Limited to 10 registrations per minute per IP.","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,"description":"HTTPS or loopback redirect URIs."},"client_name":{"type":"string"},"client_uri":{"type":"string","format":"uri"},"grant_types":{"type":"array","items":{"type":"string"},"default":["authorization_code"]},"response_types":{"type":"array","items":{"type":"string"},"default":["code"]},"token_endpoint_auth_method":{"type":"string","enum":["none"],"default":"none"},"scope":{"type":"string","description":"Requested scope; \"mcp\" is supported."}}}}}},"responses":{"201":{"description":"Registered client with client_id (and client_secret_issued_at metadata).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid client metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/oauth/token":{"post":{"operationId":"exchangeOAuthToken","tags":["OAuth"],"summary":"Token endpoint (authorization_code + refresh_token grants)","description":"Exchanges an authorization code (with PKCE verifier) for tokens, or refreshes an access token. Access tokens last 1 hour, refresh tokens 30 days. Limited to 30 requests per minute per IP.","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type","client_id"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string","description":"Authorization code (required for authorization_code grant)."},"code_verifier":{"type":"string","description":"PKCE S256 verifier (required for authorization_code grant)."},"redirect_uri":{"type":"string","format":"uri","description":"Must match the authorize request."},"client_id":{"type":"string"},"refresh_token":{"type":"string","description":"Required for refresh_token grant."},"resource":{"type":"string","format":"uri","description":"Optional resource indicator; must be the MCP resource URL."}}}}}},"responses":{"200":{"description":"Token response (access_token, refresh_token, expires_in).","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid grant/request (RFC 6749 error format).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}}}}}},"429":{"description":"Rate limited.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}}}},"/oauth/revoke":{"post":{"operationId":"revokeOAuthToken","tags":["OAuth"],"summary":"Token revocation (RFC 7009)","description":"Revokes a previously issued token. Limited to 30 requests per minute per IP.","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string"},"client_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Revoked (or already invalid)."},"429":{"description":"Rate limited.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}}}},"/api/usage":{"get":{"operationId":"getUsage","tags":["Account"],"summary":"Current plan and usage limits","description":"Returns subscription plan, usage limits, and remaining capacity for mentions, signals, AI credits, and businesses for the signed-in user. Requires a browser session cookie; agents should use the MCP getUsage tool instead.","security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Usage stats object.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Not authenticated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/subscription/status":{"get":{"operationId":"getSubscriptionStatus","tags":["Account"],"summary":"Subscription status","description":"Returns the current subscription state for the signed-in user. Requires a browser session cookie.","security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Subscription status object.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Not authenticated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}}}