{"openapi":"3.1.0","info":{"title":"SEOABLE API","version":"0.1.0","description":"Public API and MCP server for Seoable. See the spec at docs/api-mcp-spec.md in the seoable monorepo."},"servers":[{"url":"https://api.seoable.dev","description":"Production"},{"url":"http://localhost:4000","description":"Local dev"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"sk_live_... or Clerk OAuth token","description":"Either a Seoable API key (sk_live_... from /dashboard/developers) OR a Clerk-issued OAuth access token from the MCP sign-in flow."}},"schemas":{"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"service":{"type":"string","enum":["seoable-api"]},"version":{"type":"string"},"uptime_seconds":{"type":"number"},"started_at":{"type":"string"}},"required":["status","service","version","uptime_seconds","started_at"]},"MeResponse":{"type":"object","properties":{"user_id":{"type":"string"},"api_key_id":{"type":["string","null"]},"auth_source":{"type":"string","enum":["api_key","clerk_oauth"]},"balance_credits":{"type":"integer"},"balance_dollars":{"type":"number"}},"required":["user_id","api_key_id","auth_source","balance_credits","balance_dollars"]},"Project":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"},"display_name":{"type":["string","null"]},"created_at":{"type":"number"}},"required":["id","domain","display_name","created_at"]},"ProjectListResponse":{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}},"required":["projects"]},"BrandIdeasQuota":{"type":"object","properties":{"used":{"type":"integer","minimum":0},"max":{"type":"integer","minimum":0},"remaining":{"type":"integer","minimum":0}},"required":["used","max","remaining"]},"ProjectDetailResponse":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"brand_ideas_quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["project","brand_ideas_quota"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]},"ReportSummary":{"type":"object","properties":{"id":{"type":"string"},"project_id":{"type":["string","null"]},"domain":{"type":"string"},"type":{"type":"string"},"source_report_id":{"type":["string","null"]},"overall_score":{"type":"integer"},"seo_score":{"type":"integer"},"aeo_score":{"type":"integer"},"score_label":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"number"},"updated_at":{"type":"number"}},"required":["id","project_id","domain","type","source_report_id","overall_score","seo_score","aeo_score","score_label","status","created_at","updated_at"]},"ReportListResponse":{"type":"object","properties":{"reports":{"type":"array","items":{"$ref":"#/components/schemas/ReportSummary"}}},"required":["reports"]},"ReportDetail":{"allOf":[{"$ref":"#/components/schemas/ReportSummary"},{"type":"object","properties":{"data":{"description":"Full report payload: scores, quick wins, keywords, competitors, AI citation tests, strategy, and the blog-post outlines. Schema in packages/db/src/reports-types.ts."}}}]},"ReportDetailResponse":{"type":"object","properties":{"report":{"$ref":"#/components/schemas/ReportDetail"}},"required":["report"]},"CreateReportResponse":{"type":"object","properties":{"report_id":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"domain":{"type":"string"},"cost_credits":{"type":"integer"},"estimated_completion_seconds":{"type":"integer"}},"required":["report_id","type","status","domain","cost_credits","estimated_completion_seconds"]},"CreateReportBody":{"type":"object","properties":{"project_id":{"type":"string","minLength":1},"type":{"type":"string","enum":["full","audit_only"],"default":"full"},"blog_post_count":{"type":"integer","minimum":0,"maximum":10000,"default":100}},"required":["project_id"]},"CreditPackCheckoutResponse":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"session_id":{"type":"string"},"pack":{"type":"string"},"credits":{"type":"number"},"amount_cents":{"type":"number"}},"required":["url","session_id","pack","credits","amount_cents"]},"CreditPackCheckoutBody":{"type":"object","properties":{"pack":{"type":"string","enum":["p5","p10","p20","p50","p100","p500","p1000"]},"custom_dollars":{"type":"integer","minimum":5,"maximum":5000}}},"BrandIdea":{"type":"object","properties":{"id":{"type":"string"},"project_id":{"type":"string"},"title":{"type":"string"},"brief":{"type":["string","null"]},"status":{"type":"string"},"source":{"type":"string"},"used_in_report_id":{"type":["string","null"]},"created_at":{"type":"number"},"updated_at":{"type":"number"}},"required":["id","project_id","title","brief","status","source","used_in_report_id","created_at","updated_at"]},"BrandIdeasListResponse":{"type":"object","properties":{"ideas":{"type":"array","items":{"$ref":"#/components/schemas/BrandIdea"}},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["ideas","quota"]},"BrandIdeasQuotaResponse":{"type":"object","properties":{"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["quota"]},"CreateBrandIdeaResponse":{"type":"object","properties":{"idea":{"$ref":"#/components/schemas/BrandIdea"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["idea","quota"]},"CreateBrandIdeaBody":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"brief":{"type":"string","maxLength":800}},"required":["title"]},"BulkCreateBrandIdeasResponse":{"type":"object","properties":{"accepted":{"type":"integer"},"rejected":{"type":"integer"},"reason":{"type":"string","enum":["ok","inventory_full","per_call_cap"]},"ideas":{"type":"array","items":{"$ref":"#/components/schemas/BrandIdea"}},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"},"message":{"type":"string"}},"required":["accepted","rejected","reason","ideas","quota"]},"BulkCreateBrandIdeasBody":{"type":"object","properties":{"ideas":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"brief":{"type":"string","maxLength":800}},"required":["title"]},"minItems":1,"maxItems":200}},"required":["ideas"]},"CreatePostsResponse":{"type":"object","properties":{"report_id":{"type":"string"},"type":{"type":"string","enum":["posts_only"]},"status":{"type":"string"},"domain":{"type":"string"},"source_report_id":{"type":"string"},"cost_credits":{"type":"integer"},"estimated_completion_seconds":{"type":"integer"}},"required":["report_id","type","status","domain","source_report_id","cost_credits","estimated_completion_seconds"]},"PostsErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"pending_available":{"type":"integer"},"requested":{"type":"integer"}},"required":["code","message"]}},"required":["error"]},"CreatePostsBody":{"type":"object","properties":{"project_id":{"type":"string","minLength":1},"count":{"type":"integer","minimum":1,"maximum":10000},"source_report_id":{"type":"string"}},"required":["project_id","count"]},"GoogleConnection":{"type":"object","properties":{"id":{"type":"string"},"google_email":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"created_at":{"type":"number"}},"required":["id","google_email","scopes","created_at"]},"GoogleConnectionsListResponse":{"type":"object","properties":{"connections":{"type":"array","items":{"$ref":"#/components/schemas/GoogleConnection"}}},"required":["connections"]},"GoogleSearchConsoleSite":{"type":"object","properties":{"site_url":{"type":"string"},"permission_level":{"type":"string"}},"required":["site_url","permission_level"]},"GoogleSearchConsoleSitesResponse":{"type":"object","properties":{"sites":{"type":"array","items":{"$ref":"#/components/schemas/GoogleSearchConsoleSite"}}},"required":["sites"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["meta"],"summary":"Liveness check","description":"Returns 200 with basic process info when the service is alive. No auth required.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/me":{"get":{"tags":["account"],"summary":"Who am I","description":"Returns the authenticated user id (from the API key), the calling key id, and the current credit balance.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}}}}},"/v1/projects":{"get":{"tags":["projects"],"summary":"List projects","description":"All projects owned by the calling user.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectListResponse"}}}}}}},"/v1/projects/{id}":{"get":{"tags":["projects"],"summary":"Get a project","description":"Returns one project plus its brand-ideas quota block. The quota block is a sprint-7 stub right now (max 1000, used 0).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"prj_abc123"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDetailResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/reports":{"get":{"tags":["reports"],"summary":"List reports","description":"Reports owned by the calling user. Filterable by project + type.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Limit to one project.","example":"prj_abc123"},"required":false,"name":"project_id","in":"query"},{"schema":{"type":"string","enum":["full","audit_only","posts_only"],"description":"Filter by SKU."},"required":false,"name":"type","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListResponse"}}}}}},"post":{"tags":["reports"],"summary":"Order a new report (full or audit-only)","description":"Charges credits up front, queues the audit + (optional) blog generation trigger, returns the new report id immediately. Status starts at 'pending' and flips to 'complete' when the trigger finishes (typically 5 to 15 minutes).","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReportBody"}}}},"responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReportResponse"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/reports/{id}":{"get":{"tags":["reports"],"summary":"Get a report","description":"One report including the full data payload.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"rep_abc123"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDetailResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/me/billing/checkout":{"post":{"tags":["billing"],"summary":"Buy a credit pack","description":"Returns a Stripe Checkout URL for a credit pack. The user pays in Stripe; the webhook back in apps/web inserts the credit_purchases row and the balance updates within seconds. Three fixed packs: starter ($20 / 2,000 credits), pro ($50 / 5,000), scale ($200 / 20,000). Flat per-credit pricing.","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditPackCheckoutBody"}}}},"responses":{"200":{"description":"Stripe Checkout URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditPackCheckoutResponse"}}}},"500":{"description":"Stripe misconfigured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/brand-ideas":{"get":{"tags":["brand-ideas"],"summary":"List brand ideas","description":"Pending ideas for a project (default), with quota.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"projectId","in":"path"},{"schema":{"type":"string","enum":["pending","used","archived"],"default":"pending"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandIdeasListResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}}}},"post":{"tags":["brand-ideas"],"summary":"Add one brand idea","description":"Returns 409 inventory_full when the project has hit the cap (default 1,000 pending). Archive used ideas or contact support to lift it.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"projectId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrandIdeaBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrandIdeaResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}},"409":{"description":"Inventory full","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}}}}},"/v1/projects/{projectId}/brand-ideas/quota":{"get":{"tags":["brand-ideas"],"summary":"Get the brand-ideas quota for a project","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"projectId","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandIdeasQuotaResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}}}}},"/v1/projects/{projectId}/brand-ideas/bulk":{"post":{"tags":["brand-ideas"],"summary":"Add many brand ideas in one call","description":"Up to 100 ideas per call. Partial-fills when near the cap; check accepted/rejected counts in the response.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"projectId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateBrandIdeasBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateBrandIdeasResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}}}}},"/v1/brand-ideas/{id}/archive":{"patch":{"tags":["brand-ideas"],"summary":"Archive an idea (frees a quota slot)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandIdeasQuotaResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}}}}},"/v1/brand-ideas/{id}":{"delete":{"tags":["brand-ideas"],"summary":"Delete an idea (frees a quota slot)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandIdeasQuotaResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"cost":{"type":"number"},"balance":{"type":"number"},"shortfall":{"type":"number"},"topup_url":{"type":"string"},"quota":{"$ref":"#/components/schemas/BrandIdeasQuota"}},"required":["code","message"]}}}]}}}}}}},"/v1/posts":{"post":{"tags":["reports"],"summary":"Order more posts against an existing audit","description":"Writes N blog posts using the brand context from your most recent audit (or the source_report_id you pass). Topic source order: your pending brand_ideas first (oldest first), then AI-invented topics for any remainder.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostsBody"}}}},"responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostsResponse"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostsErrorResponse"}}}},"404":{"description":"Project or source report not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostsErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostsErrorResponse"}}}}}}},"/v1/google/connections":{"get":{"tags":["google"],"summary":"List connected Google accounts","description":"Returns the Google accounts the calling user has linked to Seoable. Connect a new one via the dashboard at /dashboard/settings/integrations.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoogleConnectionsListResponse"}}}}}}},"/v1/google/connections/{id}/sites":{"get":{"tags":["google"],"summary":"List Search Console sites","description":"Returns the verified Search Console properties on the given Google connection.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"gc_abc123"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoogleSearchConsoleSitesResponse"}}}},"401":{"description":"Google connection expired (refresh token revoked or rotated). Reconnect at /dashboard/integrations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Google error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"webhooks":{}}