{"openapi":"3.0.0","paths":{"/public/v1/sources":{"get":{"operationId":"PublicApiController_sources","summary":"The data sources this deployment exposes, and their provenance rules","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/ad-accounts":{"get":{"operationId":"PublicApiController_adAccounts","summary":"The ad accounts this workspace connected, with per-account sync state","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/metrics/summary":{"get":{"operationId":"PublicApiController_metricsSummary","summary":"Totals over a window. Counters are null when there is no reading, never 0.","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/metrics/daily":{"get":{"operationId":"PublicApiController_metricsDaily","summary":"Per-day totals over a window, newest first","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/crawl/sites":{"get":{"operationId":"PublicApiController_crawlSites","summary":"Sites overads crawls for this workspace, each with its own crawl state","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/crawl/pages":{"get":{"operationId":"PublicApiController_crawlPages","summary":"Tracked URLs and the outcome of the last visit to each, verbatim from the crawler","parameters":[{"name":"siteId","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/crawl/pages/{pageId}/timeline":{"get":{"operationId":"PublicApiController_crawlTimeline","summary":"Every visit to one page, newest first, INCLUDING the visits that returned nothing","parameters":[{"name":"pageId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/crawl/changes":{"get":{"operationId":"PublicApiController_crawlChanges","summary":"Detected page changes. An empty list is qualified: read provenance.emptyMeans before concluding that nothing changed.","parameters":[{"name":"siteId","required":true,"in":"query","schema":{"type":"string"}},{"name":"pageId","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/instagram/profiles":{"get":{"operationId":"PublicApiController_instagramProfiles","summary":"Tracked Instagram handles (GATED: returns 404 unless the platform-derived flag is on)","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/connections":{"get":{"operationId":"PublicPublishingController_connections","summary":"Connected social accounts with token health (valid, expiring_soon, expired, unknown), last error and last successful post","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/posts":{"post":{"operationId":"PublicPublishingController_createPost","summary":"Create a post (draft, or scheduled when scheduledAt is set). Lands as pending with an APPROVAL_REQUIRED warning when the workspace requires approval.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreatePostDto"}}}},"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"get":{"operationId":"PublicPublishingController_listPosts","summary":"List posts, newest first, filterable by status, platform and window","parameters":[{"name":"status","required":false,"in":"query","schema":{"enum":["draft","scheduled","publishing","published","partial_published","failed","cancelled"],"type":"string"}},{"name":"platform","required":false,"in":"query","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"format":"date-time","type":"string"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":200,"default":50,"type":"number"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/posts/{id}":{"get":{"operationId":"PublicPublishingController_getPost","summary":"One post with its per-account delivery log","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"patch":{"operationId":"PublicPublishingController_updatePost","summary":"Edit a draft or scheduled post. POST_NOT_EDITABLE once it has shipped.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicUpdatePostDto"}}}},"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"delete":{"operationId":"PublicPublishingController_cancelPost","summary":"Cancel a draft or scheduled post (the row is kept as cancelled)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/posts/{id}/publish":{"post":{"operationId":"PublicPublishingController_publishNow","summary":"Publish now and wait for the outcome. Refused with APPROVAL_REQUIRED while the post awaits approval.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/posts/{id}/targets":{"get":{"operationId":"PublicPublishingController_targets","summary":"The per-account delivery log for one post","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/media/upload-url":{"post":{"operationId":"PublicPublishingController_uploadUrl","summary":"A presigned PUT for a direct upload (15 MB images, 500 MB video). PUT the bytes, then complete it.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicUploadUrlDto"}}}},"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/media/{id}/complete":{"post":{"operationId":"PublicPublishingController_completeMedia","summary":"Confirm a presigned upload landed; the media becomes ready to attach","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/media/{id}":{"delete":{"operationId":"PublicPublishingController_deleteMedia","summary":"Delete a media asset and its object","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/best-times":{"get":{"operationId":"PublicPublishingController_bestTimes","summary":"Suggested upcoming times to schedule, from this workspace’s own publish history","parameters":[{"name":"platform","required":false,"in":"query","schema":{"enum":["instagram","facebook","linkedin","twitter"],"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/mcp":{"post":{"operationId":"McpController_post","summary":"MCP endpoint (Streamable HTTP). Tools read and write the same data as the REST surface.","parameters":[],"responses":{"201":{"description":""}},"tags":["public-api-mcp"],"security":[{"apiKey":[]}]},"get":{"operationId":"McpController_get","summary":"MCP standalone SSE stream for an open session","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-mcp"],"security":[{"apiKey":[]}]},"delete":{"operationId":"McpController_delete","summary":"End an MCP session","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-mcp"],"security":[{"apiKey":[]}]}},"/public/v1/mcp/setup":{"get":{"operationId":"McpSetupController_setup","summary":"MCP client setup snippets (Claude, Cursor, VS Code, Codex, n8n, ...)","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-mcp"]}},"/public/v1/workspaces":{"get":{"operationId":"PublicWorkspacesController_list","summary":"List the workspaces this key's workspace manages","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"post":{"operationId":"PublicWorkspacesController_create","summary":"Create a client workspace managed by this key's workspace. It appears in the app switcher and under Settings > Clients.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateManagedWorkspaceDto"}}}},"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/workspaces/{id}":{"get":{"operationId":"PublicWorkspacesController_get","summary":"One managed workspace","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"delete":{"operationId":"PublicWorkspacesController_detach","summary":"Detach a managed workspace. Nothing is deleted: the workspace, its posts and connections stay with their owner, unmanaged. Connect links are revoked.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/workspaces/{id}/api-keys":{"post":{"operationId":"PublicWorkspacesController_mintKey","summary":"Mint an API key that belongs to the managed workspace. The secret is returned once, here.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateManagedKeyDto"}}}},"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/workspaces/{id}/connect-url":{"post":{"operationId":"PublicWorkspacesController_connectUrl","summary":"A time-limited hosted page where the client connects channels without an overads login. The link is returned once.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConnectUrlDto"}}}},"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/webhooks":{"get":{"operationId":"PublicWebhooksController_list","summary":"List webhook endpoints (secrets never included)","parameters":[],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"post":{"operationId":"PublicWebhooksController_create","summary":"Register an endpoint. https only, public addresses only. The signing secret is returned once, here.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/webhooks/{id}":{"get":{"operationId":"PublicWebhooksController_get","summary":"One endpoint","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"patch":{"operationId":"PublicWebhooksController_update","summary":"Rename, re-point, re-subscribe, pause (active: false) or resume","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]},"delete":{"operationId":"PublicWebhooksController_remove","summary":"Delete an endpoint and its delivery log","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/webhooks/{id}/rotate-secret":{"post":{"operationId":"PublicWebhooksController_rotate","summary":"Mint a new signing secret; returned once, here","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/webhooks/{id}/deliveries":{"get":{"operationId":"PublicWebhooksController_deliveries","summary":"The last deliveries, newest first (limit up to 50)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":50,"default":50,"type":"number"}}],"responses":{"200":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/webhooks/{id}/deliveries/{deliveryId}/redeliver":{"post":{"operationId":"PublicWebhooksController_redeliver","summary":"Send a past delivery again as a new delivery","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"deliveryId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}},"/public/v1/webhooks/{id}/test":{"post":{"operationId":"PublicWebhooksController_test","summary":"Queue a signed `ping` delivery so you can verify the signature","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["public-api-v1"],"security":[{"apiKey":[]}]}}},"info":{"title":"overads public API","description":"The overads public REST API (v1). Authenticate with `Authorization: Bearer sk_live_...`. Every write accepts an `Idempotency-Key` header. Errors are `{ error: { code, message, field? } }`.","version":"0.1.0","contact":{}},"tags":[{"name":"public-api-v1","description":"Versioned public REST API (sk_live_* key). Your own connected ad accounts and their metrics, plus our own landing-page crawl. Every response carries a provenance block naming the source and when it was collected."},{"name":"public-api-mcp","description":"MCP server (JSON-RPC 2.0) over the same data, the same key and the same per-workspace limits"}],"servers":[{"url":"https://api.overads.io"}],"components":{"schemas":{"ConnectBrandDto":{"type":"object","properties":{"name":{"type":"string","maxLength":80,"description":"Defaults to the agency workspace name."},"logoUrl":{"type":"string","description":"https image URL shown above the channel tiles."}}},"CreateConnectUrlDto":{"type":"object","properties":{"expiresInDays":{"type":"number","minimum":1,"maximum":90,"default":7,"description":"How long the link stays valid."},"rotate":{"type":"boolean","default":false,"description":"Revoke every earlier link for this workspace before minting this one."},"brand":{"$ref":"#/components/schemas/ConnectBrandDto"}}},"CreateManagedKeyDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"example":"Acme integration"},"scopes":{"type":"array","description":"Scopes the key holds. Omitted means the same scopes as the calling key; a managed key can never hold workspaces:manage.","items":{"type":"string","enum":["posts:read","posts:write","connections:read","media:write","analytics:read","workflows:run","webhooks:manage","workspaces:manage"]}},"expiresAt":{"type":"string","format":"date-time","nullable":true}},"required":["name"]},"CreateManagedWorkspaceDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"example":"Acme Coffee"},"slug":{"type":"string","description":"URL-safe handle (lower-case letters, digits, hyphens). Derived from the name when omitted; must be unused.","example":"acme-coffee"}},"required":["name"]},"CreateWebhookDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"example":"Zapier catch hook"},"url":{"type":"string","example":"https://hooks.example.com/overads","description":"https only. Private, link-local and localhost addresses are refused."},"events":{"type":"array","example":["post.published","post.failed"],"items":{"type":"string","enum":["post.scheduled","post.published","post.failed","post.approval_required","post.approved","connection.error","workflow.run.completed","workflow.run.failed","proposal.staged","proposal.confirmed"]}}},"required":["name","url","events"]},"PublicCreatePostDto":{"type":"object","properties":{"content":{"type":"string","minLength":1,"description":"The shared copy. Per-platform overrides go in variants."},"connectionIds":{"minItems":1,"description":"Connection ids from GET /connections.","type":"array","items":{"type":"string"}},"scheduledAt":{"type":"string","format":"date-time","description":"When to publish (ISO 8601). Omit for a draft. Up to 5 minutes in the past is moved to now with a SCHEDULED_TIME_COERCED warning; earlier is SCHEDULED_TIME_IN_PAST."},"mediaUrls":{"maxItems":10,"description":"Public https image or video URLs. Downloaded server-side into overads storage. URLs already in overads storage are used as-is.","type":"array","items":{"type":"string"}},"mediaIds":{"maxItems":10,"description":"Ids of media completed through POST /media/:id/complete.","type":"array","items":{"type":"string"}},"platformSettings":{"type":"object","additionalProperties":{"type":"object"},"description":"Per-platform delivery settings: { instagram: { videoType, shareToFeed, coverUrl, firstComment }, facebook: { firstComment }, linkedin: { visibility }, twitter: { replyTo, quoteTweet } }. Unknown keys are refused with PLATFORM_SETTING_UNKNOWN."},"variants":{"type":"object","additionalProperties":{"type":"object"},"description":"Per-platform copy overrides: { [platform]: { content } }."},"requireApproval":{"type":"boolean","description":"Queue the scheduled post for approval even when the workspace does not require it."}},"required":["content","connectionIds"]},"PublicUpdatePostDto":{"type":"object","properties":{"content":{"type":"string","minLength":1,"description":"The shared copy. Per-platform overrides go in variants."},"connectionIds":{"minItems":1,"description":"Connection ids from GET /connections.","type":"array","items":{"type":"string"}},"scheduledAt":{"type":"string","format":"date-time","description":"When to publish (ISO 8601). Omit for a draft. Up to 5 minutes in the past is moved to now with a SCHEDULED_TIME_COERCED warning; earlier is SCHEDULED_TIME_IN_PAST."},"mediaUrls":{"maxItems":10,"description":"Public https image or video URLs. Downloaded server-side into overads storage. URLs already in overads storage are used as-is.","type":"array","items":{"type":"string"}},"mediaIds":{"maxItems":10,"description":"Ids of media completed through POST /media/:id/complete.","type":"array","items":{"type":"string"}},"platformSettings":{"type":"object","additionalProperties":{"type":"object"},"description":"Per-platform delivery settings: { instagram: { videoType, shareToFeed, coverUrl, firstComment }, facebook: { firstComment }, linkedin: { visibility }, twitter: { replyTo, quoteTweet } }. Unknown keys are refused with PLATFORM_SETTING_UNKNOWN."},"variants":{"type":"object","additionalProperties":{"type":"object"},"description":"Per-platform copy overrides: { [platform]: { content } }."},"requireApproval":{"type":"boolean","description":"Queue the scheduled post for approval even when the workspace does not require it."}}},"PublicUploadUrlDto":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video"]},"contentType":{"type":"string","example":"image/jpeg","description":"The Content-Type the PUT will send."},"bytes":{"type":"number","description":"Exact byte length of the file the PUT will send."}},"required":["kind","contentType","bytes"]},"UpdateWebhookDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"url":{"type":"string","description":"https only; re-checked against the SSRF guard."},"events":{"type":"array","items":{"type":"string","enum":["post.scheduled","post.published","post.failed","post.approval_required","post.approved","connection.error","workflow.run.completed","workflow.run.failed","proposal.staged","proposal.confirmed"]}},"active":{"type":"boolean","description":"false pauses the endpoint; true resumes it and resets the consecutive-failure counter."}}}},"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"Authorization","description":"sk_live_*"}}}}