{"openapi":"3.1.0","info":{"title":"ServiceGraph API","version":"0.1.0","description":"Search the ServiceGraph catalog of US professional-services firms. Anonymous /v1/explore for catalog discovery; authenticate with email + OTP for /v1/search and /v1/get/{id}."},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{}},"paths":{"/.well-known/oauth-authorization-server":{"get":{"description":"OAuth 2.0 Authorization Server Metadata (RFC 8414) — discovery endpoint for MCP clients.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/request-otp":{"post":{"description":"Request a one-time login code by email. Always 204 — never reveals whether the email exists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":320}}}}}},"security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/verify-otp":{"post":{"description":"Exchange a valid OTP for a long-lived bearer token. Token is shown once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","code"],"properties":{"email":{"type":"string","format":"email","maxLength":320},"code":{"type":"string","minLength":4,"maxLength":12},"name":{"type":"string","maxLength":100}}}}}},"security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/logout":{"post":{"description":"Revoke the calling bearer token.","responses":{"200":{"description":"Default Response"}}}},"/v1/auth/register":{"post":{"description":"OAuth 2.0 Dynamic Client Registration (RFC 7591). Public clients only — no client_secret is issued.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"client_name":{"type":"string","maxLength":200},"redirect_uris":{"type":"array","items":{"type":"string"},"maxItems":10},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"token_endpoint_auth_method":{"type":"string"}}}}}},"security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/authorize":{"get":{"description":"OAuth 2.1 authorization endpoint. Renders an HTML form that drives the existing email + OTP flow as a consent UI; on success issues a single-use authorization code bound to the client and PKCE challenge.","security":[],"responses":{"200":{"description":"Default Response"}}},"post":{"description":"Handles the email and code submission steps of the authorize page.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/token":{"post":{"description":"OAuth 2.1 token endpoint. Supports authorization_code and refresh_token grants.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/revoke":{"post":{"description":"OAuth 2.0 token revocation (RFC 7009). Accepts an access or refresh token.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/me/":{"get":{"description":"Identity, plan, and today's usage for the authed user.","responses":{"200":{"description":"Default Response"}}}},"/v1/me/tokens":{"get":{"description":"List the calling user's active tokens (token strings are never returned).","responses":{"200":{"description":"Default Response"}}}},"/v1/me/tokens/{id}":{"delete":{"description":"Revoke a token by id.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/explore":{"get":{"description":"Aggregate counts + sub-tag breakdowns over the catalog under a filter. Filter syntax: same as /v1/search — call /v1/tags to discover available fields, kinds, and values. When the filtered set has fewer than 20 firms, breakdowns are suppressed and count returns \"<20\" — broaden the query or sign up for /v1/search to drill deeper.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/check":{"get":{"description":"Validate a filter DSL string without executing it. Returns whether it parses and the canonical normalized form. No auth, IP-rate-limited. Useful for live syntax checking in agent UIs and dev tools.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/v1/search":{"get":{"description":"Filter the catalog using the filter DSL and return brief firm cards. Costs against monthly unique-firm-view quota — re-pages and re-queries that overlap with already-seen firms are free for the overlap. Briefs do not include apex/url/contact info; call /get/:id for those. Use GET /tags to discover field names, kinds, operators, and values.","responses":{"200":{"description":"Default Response"}}}},"/v1/get/{id}":{"get":{"description":"Full disclosure bundle for one firm by firm_id (12 hex chars, computed as sha256(apex.lower().rstrip(\".\")).hexdigest()[:12]). Costs against monthly /get quota; re-fetches in the same calendar month are free. 404s are not charged. Callers with their own apex list can compute the firm_id locally with the same algorithm and call /v1/get directly.","parameters":[{"schema":{"type":"string","minLength":12,"maxLength":12,"pattern":"^[0-9a-f]{12}$"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/stats":{"get":{"description":"Facet-distribution snapshot for the live catalog.","responses":{"200":{"description":"Default Response"}}}},"/v1/tags":{"get":{"description":"Self-describing field catalog for the filter DSL. Returns one entry per filterable field with its kind, allowed operators, description, and (optionally) the list of valid values. Query params: q (substring filter on name + description), include_values (1 to expand value lists, 0 to omit). Public, IP-rate-limited. Agents should call this once per session, then build /search and /explore filter strings.","security":[],"responses":{"200":{"description":"Default Response"}}}}},"servers":[{"url":"https://api.servicegraph.co"}],"security":[{"bearerAuth":[]}]}