openapi: 3.0.3 info: title: 'ViewsMax API Documentation' description: 'ViewsMax REST API: multi-platform posts, offers, tracking links, analytics, media, and connections. AI agents can also use the MCP server at /api/mcp — see https://viewsmax.com/ai.md and /api/ai for discovery.' version: 1.0.0 servers: - url: 'https://api.viewsmax.com' tags: - name: Discovery description: '' - name: Posts description: "\nCompose multi-platform posts (YouTube, TikTok, X, LinkedIn, Threads,\nInstagram, Bluesky) as drafts, publish immediately, or schedule. One post\nfans out to per-platform targets that each report their own publish\nstatus — publishing is asynchronous, so poll GET /api/posts/{id}." - name: 'Post Media' description: "\nUpload media (images / video) used by composed posts. Files are stored on the\nconfigured default disk (S3 in production) and returned as a public URL that\nthe platform publishing APIs (e.g. TikTok PULL_FROM_URL) can fetch." - name: Offers description: "\nOffers are products/campaigns being promoted (stored as tracking events).\nEach offer has an offer URL and conversion value, owns tracking links, and\naccumulates clicks and conversions. The stats and timeseries endpoints\npower the analytics dashboards." - name: 'Tracking Links' description: "\nTracked links tied to an offer. Place them in videos, posts, emails, or\nbios; ViewsMax records clicks and attributes conversions back to the link\n(and the post/platform) that drove them." - name: Content description: "\nPost and manage content (long-form body text + optional media file) that can be\nattached to an Offer. Media is stored privately and streamed back via the media\nendpoint. All endpoints are scoped to the authenticated user." - name: Connections description: "\nLegacy connection management (YouTube/TikTok/Instagram OAuth token\nexchange). Prefer the /api/social endpoints for new integrations." - name: 'Social Posts' description: "\nEarlier posting surface (predates /api/posts). Prefer the Posts endpoints\nfor new integrations; these remain for existing clients." - name: 'API Keys' description: "\nMCP API key management: one non-expiring key per user,\nrotate-to-invalidate. The key is a Sanctum personal access token scoped to\nthe `mcp` ability, stored hashed; only a display hint is recoverable, so the\nplaintext is returned exactly once from rotate(). The key authenticates the\nMCP server (/api/mcp) and the posts/offers/tracking REST surface; these\nmanagement endpoints themselves require a login session, not a key." - name: Plans description: '' - name: 'Feature Requests' description: '' - name: Auth description: "\nSession authentication. POST /api/login returns a bearer token with full\naccount access. AI agents should prefer a vmx_ API key (Settings → AI\nAssistant Access) or the MCP OAuth flow instead of storing passwords." - name: 'Audience Growth' description: "\nRead-only analytics over the daily snapshot tables (audience_snapshots,\npost_metric_snapshots) populated by audience:refresh / posts:refresh-metrics.\nRevenue Growth reuses TrackingEventController@getTimeseries and isn't here." - name: Boosts description: "\nPer-account Boost automations (X-only v1): Auto Repost retweets a post once\nit reaches a like threshold; Auto Promo replies to it with a promo comment.\nChecks run 6h apart, up to 3 times per post, and stop on success." - name: Brands description: "\nNamed groups of connected accounts. Selecting a brand in the composer\nauto-selects every account in it. Members may come from either account\nstore: social_accounts (X, Instagram, ...) or legacy connections\n(YouTube, TikTok)." - name: Endpoints description: '' - name: 'Image Generation' description: "\nAPIs for generating AI images using Flux 2" - name: Outliers description: "\nOutlier videos: content that massively over-performed its channel's average\n(`outlier_score` = views ÷ channel average views) across YouTube, TikTok and\nInstagram. Browse the shared database, pull in specific URLs, and get AI\nbreakdowns of why a video worked." - name: 'User Default Reference Image' description: "\nAPIs for managing a user's default reference image for image generation" components: securitySchemes: default: type: http scheme: bearer description: 'Authenticate with your ViewsMax API key (`vmx_...`) as a Bearer token: `Authorization: Bearer vmx_...`. Create or rotate it in the ViewsMax app under Settings → AI Assistant Access. Read-only keys can only call GET endpoints, and API keys are limited to the posts/offers/tracking/social/connections surface. Session tokens from `POST /api/login` also work and have full account access.' security: - default: [] paths: /api/ai: get: summary: 'AI capability discovery' operationId: aICapabilityDiscovery description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: name: ViewsMax summary: "Social posting + link tracking/analytics SaaS. AI agents act on a user's behalf: compose and schedule posts to YouTube, TikTok, X, LinkedIn, Threads, Instagram, and Bluesky; create offers and tracked links; read click, conversion, and revenue stats; research outlier videos (content that massively over-performed its channel) and get AI breakdowns of why they worked. User data is private — all access is authenticated." site: 'https://viewsmax.com' docs: agents: 'https://viewsmax.com/ai.md' llms_txt: 'https://viewsmax.com/llms.txt' api_reference: 'https://api.viewsmax.com/docs' openapi: 'https://api.viewsmax.com/docs.openapi' mcp: endpoint: 'https://api.viewsmax.com/api/mcp' transport: streamable-http auth: - type: oauth2 grant: authorization_code pkce: true scopes: - mcp - 'mcp:read' - 'mcp:write' authorization_server_metadata: 'https://api.viewsmax.com/.well-known/oauth-authorization-server' protected_resource_metadata: 'https://api.viewsmax.com/.well-known/oauth-protected-resource/api/mcp' dynamic_client_registration: true - type: api_key header: 'Authorization: Bearer ' key_prefix: vmx_ access_levels: - read - full obtain_at: 'https://viewsmax.com/dashboard/settings' tools: - name: list_connected_accounts description: 'List every social account the user has connected, plus a Beehiiv newsletter connection if there is one — a platform can appear more than once when several accounts are connected on it. Each entry carries social_account_id or connection_id (matching list_brands) plus its store. Posts can only publish to connected platforms; use get_connect_url for anything missing. Beehiiv is connected via an API key on the Connections page, not get_connect_url, and is used for newsletter reach, not posting.' access: read - name: list_brands description: 'List the user''s brands — named groups of connected accounts (e.g. a brand holding a TikTok, an X and a YouTube account). Pass a brand id to create_post as brand_id to post to every connected account in the brand at once. Accounts with a status other than "connected" are skipped when posting.' access: read - name: upload_media description: "Download a file from a URL and host it on ViewsMax storage for use in posts. Returns a media entry ({type, url, path}) to pass to create_post. Supports jpeg/png/webp/gif images and mp4/mov video. Each call stores a new copy of the file, so don't repeat a call that already succeeded." access: write - name: create_post description: 'Compose a social post for one or more platforms (tiktok, youtube, x, linkedin, threads, instagram, bluesky). Target either platforms[] or brand_id (from list_brands, posts to every connected account in the brand) — not both. status "draft" (default) saves without publishing, "posted" publishes immediately, "scheduled" publishes at scheduled_at. TikTok takes a video (with a url) or a photo slideshow (one or more image urls); YouTube requires a video with a path — both come from upload_media. Instagram needs an image or video url. TikTok photo slideshows accept options.tiktok.auto_add_music (boolean, default false) to let TikTok auto-add its recommended background music; there is no music option for video posts or for Instagram. Publishing/scheduling to TikTok requires options.tiktok.privacy_level (one of PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY) — there is no default; branded_content cannot be SELF_ONLY. Ask the user which TikTok privacy_level to use and wait for their answer; never choose it for them. Ask the user which YouTube privacy_status to use (public, unlisted, or private) and wait for their answer; without it the video is public. Caption character limits: tiktok: 2200, youtube: 5000, x: 280, linkedin: 3000, threads: 500, instagram: 2200, bluesky: 300. Publishing is asynchronous: check per-platform results with get_post. Each call creates a new post, so don''t repeat a call that already succeeded.' access: write - name: list_posts description: "List the user's posts, newest first, with per-platform publish status. Filter by status (draft, scheduled, posted) and/or a scheduled_at date window (from/to, ISO-8601). Returns one page at a time (25 by default); when has_more is true, ask for the next page." access: read - name: get_post description: "Fetch one post by id, including each platform target's publish status (pending, publishing, published, failed) and error message." access: read - name: update_post description: 'Edit a draft or scheduled post: caption, media, platforms, schedule, or status. Setting status to "posted" publishes immediately. Posts that have already been published cannot be edited. Ask the user which TikTok privacy_level to use and wait for their answer; never choose it for them. Ask the user which YouTube privacy_status to use (public, unlisted, or private) and wait for their answer; without it the video is public. Caption character limits: tiktok: 2200, youtube: 5000, x: 280, linkedin: 3000, threads: 500, instagram: 2200, bluesky: 300.' access: write - name: delete_post description: 'Delete a post and its platform targets. Does not remove content already published to the platforms.' access: write - name: list_offers description: "List the user's offers (tracked promotions) with their tracking links, goals, and per-offer click/conversion stats. Optional from/to date filter. Returns one page at a time (25 by default); when has_more is true, ask for the next page." access: read - name: create_offer description: "Create an offer (a promotion to track). Requires offer_url; optional name and goals (conversion events with a conversion_url and value). Subject to the user's plan offer limit. Each call creates a new offer, so don't repeat a call that already succeeded." access: write - name: get_offer description: 'Fetch one offer by id, with its tracking links and goals.' access: read - name: update_offer description: "Update an offer's name, offer_url, goals, or links." access: write - name: delete_offer description: 'Delete an offer and stop tracking it.' access: write - name: create_tracking_link description: "Create a tracking link for an offer, to place in a video description, email, social bio, etc. Placement is one of: video, email, x, linkedin, podcast, blog, website, tiktok, ad, instagram, beehiiv, other — attaching a youtube_video_id or beehiiv_post_id auto-sets the matching placement. Each call creates a new tracking link, so don't repeat a call that already succeeded." access: write - name: get_offer_stats description: "Aggregated analytics across the user's offers: video views, clicks, calls booked, email signups, and sales revenue. Optional from/to window." access: read - name: get_stats_timeseries description: 'Daily time-series of clicks and attributed revenue (one bucket per day, gaps zero-filled). Defaults to the last 28 days; filter with from/to and optionally a single offer via event_id.' access: read - name: disconnect_account description: 'Disconnect a social account by platform (e.g. x, youtube, tiktok). Fully disconnects it — nothing is left half-connected — and any pending posts targeting that platform will fail to publish.' access: write - name: get_connect_url description: 'Get the URL of the ViewsMax Connections page where the user can connect a social platform. The user must open the page in a browser and click Connect there — the OAuth approval happens on that page and cannot be completed inside this conversation.' access: write - name: create_feature_request description: "Submit a feature request to the ViewsMax team on the user's behalf. Don't use this for plan, billing, or purchase requests; tell the user to manage their plan in the ViewsMax app instead. Each call submits a new request, so don't repeat a call that already succeeded." access: write - name: list_outliers description: 'Browse outlier videos — content that massively over-performed its channel''s average (outlier_score = views ÷ channel average views) across YouTube, TikTok and Instagram. Without `query` this is the curated/featured feed; with `query` it returns title matches already in the database. If the response `status` is "queued" or "in_progress" no scrape has finished for that query yet — call search_outliers to start one, then re-run this tool. Filter by platform, score, views, subscribers, publish date, duration (long/shorts), channel ids or ISO country codes. Paginated (per_page ≤ 50).' access: read - name: search_outliers description: 'Start a background YouTube search (YouTube Data API) for outlier videos matching a keyword/topic. Returns immediately with status "queued"; results land in the shared outlier database over the next minute or two — poll list_outliers with the same `query` until its status is "done". Use exact_match to require the whole phrase. This search covers YouTube only: for TikTok and Instagram, add a single video by its link with fetch_outlier, or add a creator''s channel with add_outlier_channel to pull in their recent videos.' access: write - name: get_outlier description: 'Fetch one outlier video by platform + video id (as returned by list_outliers or fetch_outlier), including its channel, views, outlier score and engagement.' access: read - name: fetch_outlier description: 'Pull a specific video into the outlier database from its URL so it can be analysed (get_outlier, generate_outlier_breakdown, save_outlier). If the video is already known it is returned immediately; otherwise ingestion is queued (`queued: true`) — poll get_outlier with the returned platform + video_id.' access: write - name: get_outlier_breakdown description: 'Read the AI breakdown of an outlier video (hook, structure, why it worked, how to replicate it). `status` is none (never generated — call generate_outlier_breakdown), pending/processing (poll again), completed (`payload` holds the analysis) or failed (`error`).' access: read - name: generate_outlier_breakdown description: 'Queue an AI breakdown of an outlier video (transcript + analysis of the hook, structure and why it over-performed). Generation runs in the background and takes up to a couple of minutes — poll get_outlier_breakdown until status is completed. Re-running for a video that already has a breakdown returns the existing one instead of regenerating.' access: write - name: list_saved_outliers description: "The user's saved-outliers library (videos they bookmarked with save_outlier), newest first, with tags and the video snapshot taken when saved. Filter by a title/channel query, tag names, platforms, or creator name. Returns one page at a time (25 by default); when has_more is true, ask for the next page." access: read - name: save_outlier description: "Bookmark an outlier video into the user's library, optionally with tags (created on demand). Saving the same video again replaces its tags. The video must already be in the outlier database (list_outliers / fetch_outlier)." access: write - name: remove_saved_outlier description: "Remove a saved video from the user's outlier library by its saved id (from list_saved_outliers / save_outlier). The video itself stays in the outlier database." access: write - name: add_outlier_channel description: "Add a creator's channel to the outlier database from a profile URL or @handle (YouTube, TikTok, Instagram) and pull in their 10 most recent videos, scored against that channel's own median. Also adds the channel to the user's competitor list. Not for video links — use fetch_outlier for those. Returns `status: done` with the channel when it was pulled in the last 24 hours; otherwise `queued: true` with an ingest_id — poll get_outlier_channel_ingest until `done`, then list_outliers with `channels: [channel.id]` (and `duration_type: shorts` for TikTok/Instagram) to see the videos. No AI breakdowns are generated." access: write - name: get_outlier_channel_ingest description: 'Poll a channel add started by add_outlier_channel. `status` is queued, processing, done (then `channel` is set and `videos_added` says how many videos landed — use channel.id in list_outliers `channels`), or failed (then `error` explains why). Pulls take ~10-60 seconds; poll every 5-10 seconds.' access: read rest: base_url: 'https://api.viewsmax.com/api' auth: 'Same vmx_ API key as a Bearer token (posts, offers, tracking, stats, and outliers endpoints only; read-only keys are limited to GET).' openapi: 'https://api.viewsmax.com/docs.openapi' rate_limits: mcp_requests_per_minute: 120 create_post_per_hour: 180 upload_media_per_hour: 40 search_outliers_per_hour: 30 fetch_outlier_per_hour: 60 generate_outlier_breakdown_per_hour: 30 add_outlier_channel_per_hour: 10 properties: name: type: string example: ViewsMax summary: type: string example: "Social posting + link tracking/analytics SaaS. AI agents act on a user's behalf: compose and schedule posts to YouTube, TikTok, X, LinkedIn, Threads, Instagram, and Bluesky; create offers and tracked links; read click, conversion, and revenue stats; research outlier videos (content that massively over-performed its channel) and get AI breakdowns of why they worked. User data is private — all access is authenticated." site: type: string example: 'https://viewsmax.com' docs: type: object properties: agents: type: string example: 'https://viewsmax.com/ai.md' llms_txt: type: string example: 'https://viewsmax.com/llms.txt' api_reference: type: string example: 'https://api.viewsmax.com/docs' openapi: type: string example: 'https://api.viewsmax.com/docs.openapi' mcp: type: object properties: endpoint: type: string example: 'https://api.viewsmax.com/api/mcp' transport: type: string example: streamable-http auth: type: array example: - type: oauth2 grant: authorization_code pkce: true scopes: - mcp - 'mcp:read' - 'mcp:write' authorization_server_metadata: 'https://api.viewsmax.com/.well-known/oauth-authorization-server' protected_resource_metadata: 'https://api.viewsmax.com/.well-known/oauth-protected-resource/api/mcp' dynamic_client_registration: true - type: api_key header: 'Authorization: Bearer ' key_prefix: vmx_ access_levels: - read - full obtain_at: 'https://viewsmax.com/dashboard/settings' items: type: object properties: type: type: string example: oauth2 grant: type: string example: authorization_code pkce: type: boolean example: true scopes: type: array example: - mcp - 'mcp:read' - 'mcp:write' items: type: string authorization_server_metadata: type: string example: 'https://api.viewsmax.com/.well-known/oauth-authorization-server' protected_resource_metadata: type: string example: 'https://api.viewsmax.com/.well-known/oauth-protected-resource/api/mcp' dynamic_client_registration: type: boolean example: true tools: type: array example: - name: list_connected_accounts description: 'List every social account the user has connected, plus a Beehiiv newsletter connection if there is one — a platform can appear more than once when several accounts are connected on it. Each entry carries social_account_id or connection_id (matching list_brands) plus its store. Posts can only publish to connected platforms; use get_connect_url for anything missing. Beehiiv is connected via an API key on the Connections page, not get_connect_url, and is used for newsletter reach, not posting.' access: read - name: list_brands description: 'List the user''s brands — named groups of connected accounts (e.g. a brand holding a TikTok, an X and a YouTube account). Pass a brand id to create_post as brand_id to post to every connected account in the brand at once. Accounts with a status other than "connected" are skipped when posting.' access: read - name: upload_media description: "Download a file from a URL and host it on ViewsMax storage for use in posts. Returns a media entry ({type, url, path}) to pass to create_post. Supports jpeg/png/webp/gif images and mp4/mov video. Each call stores a new copy of the file, so don't repeat a call that already succeeded." access: write - name: create_post description: 'Compose a social post for one or more platforms (tiktok, youtube, x, linkedin, threads, instagram, bluesky). Target either platforms[] or brand_id (from list_brands, posts to every connected account in the brand) — not both. status "draft" (default) saves without publishing, "posted" publishes immediately, "scheduled" publishes at scheduled_at. TikTok takes a video (with a url) or a photo slideshow (one or more image urls); YouTube requires a video with a path — both come from upload_media. Instagram needs an image or video url. TikTok photo slideshows accept options.tiktok.auto_add_music (boolean, default false) to let TikTok auto-add its recommended background music; there is no music option for video posts or for Instagram. Publishing/scheduling to TikTok requires options.tiktok.privacy_level (one of PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY) — there is no default; branded_content cannot be SELF_ONLY. Ask the user which TikTok privacy_level to use and wait for their answer; never choose it for them. Ask the user which YouTube privacy_status to use (public, unlisted, or private) and wait for their answer; without it the video is public. Caption character limits: tiktok: 2200, youtube: 5000, x: 280, linkedin: 3000, threads: 500, instagram: 2200, bluesky: 300. Publishing is asynchronous: check per-platform results with get_post. Each call creates a new post, so don''t repeat a call that already succeeded.' access: write - name: list_posts description: "List the user's posts, newest first, with per-platform publish status. Filter by status (draft, scheduled, posted) and/or a scheduled_at date window (from/to, ISO-8601). Returns one page at a time (25 by default); when has_more is true, ask for the next page." access: read - name: get_post description: "Fetch one post by id, including each platform target's publish status (pending, publishing, published, failed) and error message." access: read - name: update_post description: 'Edit a draft or scheduled post: caption, media, platforms, schedule, or status. Setting status to "posted" publishes immediately. Posts that have already been published cannot be edited. Ask the user which TikTok privacy_level to use and wait for their answer; never choose it for them. Ask the user which YouTube privacy_status to use (public, unlisted, or private) and wait for their answer; without it the video is public. Caption character limits: tiktok: 2200, youtube: 5000, x: 280, linkedin: 3000, threads: 500, instagram: 2200, bluesky: 300.' access: write - name: delete_post description: 'Delete a post and its platform targets. Does not remove content already published to the platforms.' access: write - name: list_offers description: "List the user's offers (tracked promotions) with their tracking links, goals, and per-offer click/conversion stats. Optional from/to date filter. Returns one page at a time (25 by default); when has_more is true, ask for the next page." access: read - name: create_offer description: "Create an offer (a promotion to track). Requires offer_url; optional name and goals (conversion events with a conversion_url and value). Subject to the user's plan offer limit. Each call creates a new offer, so don't repeat a call that already succeeded." access: write - name: get_offer description: 'Fetch one offer by id, with its tracking links and goals.' access: read - name: update_offer description: "Update an offer's name, offer_url, goals, or links." access: write - name: delete_offer description: 'Delete an offer and stop tracking it.' access: write - name: create_tracking_link description: "Create a tracking link for an offer, to place in a video description, email, social bio, etc. Placement is one of: video, email, x, linkedin, podcast, blog, website, tiktok, ad, instagram, beehiiv, other — attaching a youtube_video_id or beehiiv_post_id auto-sets the matching placement. Each call creates a new tracking link, so don't repeat a call that already succeeded." access: write - name: get_offer_stats description: "Aggregated analytics across the user's offers: video views, clicks, calls booked, email signups, and sales revenue. Optional from/to window." access: read - name: get_stats_timeseries description: 'Daily time-series of clicks and attributed revenue (one bucket per day, gaps zero-filled). Defaults to the last 28 days; filter with from/to and optionally a single offer via event_id.' access: read - name: disconnect_account description: 'Disconnect a social account by platform (e.g. x, youtube, tiktok). Fully disconnects it — nothing is left half-connected — and any pending posts targeting that platform will fail to publish.' access: write - name: get_connect_url description: 'Get the URL of the ViewsMax Connections page where the user can connect a social platform. The user must open the page in a browser and click Connect there — the OAuth approval happens on that page and cannot be completed inside this conversation.' access: write - name: create_feature_request description: "Submit a feature request to the ViewsMax team on the user's behalf. Don't use this for plan, billing, or purchase requests; tell the user to manage their plan in the ViewsMax app instead. Each call submits a new request, so don't repeat a call that already succeeded." access: write - name: list_outliers description: 'Browse outlier videos — content that massively over-performed its channel''s average (outlier_score = views ÷ channel average views) across YouTube, TikTok and Instagram. Without `query` this is the curated/featured feed; with `query` it returns title matches already in the database. If the response `status` is "queued" or "in_progress" no scrape has finished for that query yet — call search_outliers to start one, then re-run this tool. Filter by platform, score, views, subscribers, publish date, duration (long/shorts), channel ids or ISO country codes. Paginated (per_page ≤ 50).' access: read - name: search_outliers description: 'Start a background YouTube search (YouTube Data API) for outlier videos matching a keyword/topic. Returns immediately with status "queued"; results land in the shared outlier database over the next minute or two — poll list_outliers with the same `query` until its status is "done". Use exact_match to require the whole phrase. This search covers YouTube only: for TikTok and Instagram, add a single video by its link with fetch_outlier, or add a creator''s channel with add_outlier_channel to pull in their recent videos.' access: write - name: get_outlier description: 'Fetch one outlier video by platform + video id (as returned by list_outliers or fetch_outlier), including its channel, views, outlier score and engagement.' access: read - name: fetch_outlier description: 'Pull a specific video into the outlier database from its URL so it can be analysed (get_outlier, generate_outlier_breakdown, save_outlier). If the video is already known it is returned immediately; otherwise ingestion is queued (`queued: true`) — poll get_outlier with the returned platform + video_id.' access: write - name: get_outlier_breakdown description: 'Read the AI breakdown of an outlier video (hook, structure, why it worked, how to replicate it). `status` is none (never generated — call generate_outlier_breakdown), pending/processing (poll again), completed (`payload` holds the analysis) or failed (`error`).' access: read - name: generate_outlier_breakdown description: 'Queue an AI breakdown of an outlier video (transcript + analysis of the hook, structure and why it over-performed). Generation runs in the background and takes up to a couple of minutes — poll get_outlier_breakdown until status is completed. Re-running for a video that already has a breakdown returns the existing one instead of regenerating.' access: write - name: list_saved_outliers description: "The user's saved-outliers library (videos they bookmarked with save_outlier), newest first, with tags and the video snapshot taken when saved. Filter by a title/channel query, tag names, platforms, or creator name. Returns one page at a time (25 by default); when has_more is true, ask for the next page." access: read - name: save_outlier description: "Bookmark an outlier video into the user's library, optionally with tags (created on demand). Saving the same video again replaces its tags. The video must already be in the outlier database (list_outliers / fetch_outlier)." access: write - name: remove_saved_outlier description: "Remove a saved video from the user's outlier library by its saved id (from list_saved_outliers / save_outlier). The video itself stays in the outlier database." access: write - name: add_outlier_channel description: "Add a creator's channel to the outlier database from a profile URL or @handle (YouTube, TikTok, Instagram) and pull in their 10 most recent videos, scored against that channel's own median. Also adds the channel to the user's competitor list. Not for video links — use fetch_outlier for those. Returns `status: done` with the channel when it was pulled in the last 24 hours; otherwise `queued: true` with an ingest_id — poll get_outlier_channel_ingest until `done`, then list_outliers with `channels: [channel.id]` (and `duration_type: shorts` for TikTok/Instagram) to see the videos. No AI breakdowns are generated." access: write - name: get_outlier_channel_ingest description: 'Poll a channel add started by add_outlier_channel. `status` is queued, processing, done (then `channel` is set and `videos_added` says how many videos landed — use channel.id in list_outliers `channels`), or failed (then `error` explains why). Pulls take ~10-60 seconds; poll every 5-10 seconds.' access: read items: type: object properties: name: type: string example: list_connected_accounts description: type: string example: 'List every social account the user has connected, plus a Beehiiv newsletter connection if there is one — a platform can appear more than once when several accounts are connected on it. Each entry carries social_account_id or connection_id (matching list_brands) plus its store. Posts can only publish to connected platforms; use get_connect_url for anything missing. Beehiiv is connected via an API key on the Connections page, not get_connect_url, and is used for newsletter reach, not posting.' access: type: string example: read rest: type: object properties: base_url: type: string example: 'https://api.viewsmax.com/api' auth: type: string example: 'Same vmx_ API key as a Bearer token (posts, offers, tracking, stats, and outliers endpoints only; read-only keys are limited to GET).' openapi: type: string example: 'https://api.viewsmax.com/docs.openapi' rate_limits: type: object properties: mcp_requests_per_minute: type: integer example: 120 create_post_per_hour: type: integer example: 180 upload_media_per_hour: type: integer example: 40 search_outliers_per_hour: type: integer example: 30 fetch_outlier_per_hour: type: integer example: 60 generate_outlier_breakdown_per_hour: type: integer example: 30 add_outlier_channel_per_hour: type: integer example: 10 tags: - Discovery security: [] '/api/posts/{post}/targets/{target}/retry': post: summary: "Re-queue publishing for a single failed platform target. Only a FAILED\ntarget can be retried, and only that target is touched — its siblings\n(which may already be published) are left alone, so a retry can never\ndouble-post to a platform that already succeeded." operationId: reQueuePublishingForASingleFailedPlatformTargetOnlyAFAILEDTargetCanBeRetriedAndOnlyThatTargetIsTouchedItsSiblingswhichMayAlreadyBePublishedAreLeftAloneSoARetryCanNeverDoublePostToAPlatformThatAlreadySucceeded description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Posts parameters: - in: path name: post description: 'The post.' example: architecto required: true schema: type: string - in: path name: target description: 'The target.' example: architecto required: true schema: type: string /api/posts: get: summary: "List the user's posts (with targets). Optional filters: status, and a\nscheduled_at date window (from/to) used by the calendar view." operationId: listTheUsersPostswithTargetsOptionalFiltersStatusAndAScheduledAtDateWindowfromtoUsedByTheCalendarView description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Posts post: summary: 'Create a post.' operationId: createAPost description: "Composes a post and fans it out to the given platforms. Save as a draft,\npublish immediately (status=posted), or schedule (status=scheduled with\nscheduled_at). Publishing is asynchronous — poll GET /api/posts/{id} for\nper-platform results." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Posts requestBody: required: false content: application/json: schema: type: object properties: caption: type: string description: 'The post text / caption.' example: 'Big news today!' platforms: type: array description: 'Target platforms: youtube, tiktok, instagram, x, linkedin, threads.' example: - tiktok - instagram items: type: string media: type: array description: 'Media entries, usually from POST /api/posts/media.' example: - [] items: type: object properties: type: type: string description: 'image or video.' example: image url: type: string description: 'Public URL of the media.' example: 'https://cdn.example/1.jpg' path: type: string description: 'Storage path (YouTube requires a video path).' example: posts/1/clip.mp4 status: type: string description: 'draft (default), scheduled, or posted.' example: draft scheduled_at: type: string description: 'ISO-8601 datetime; required when status is scheduled.' example: '2026-07-20T18:30:00Z' brand_id: type: integer description: 'The brand selected in the composer (informational).' example: 1 overrides: type: object description: 'Per-platform caption overrides, keyed by platform.' example: [] properties: { } options: type: object description: 'Per-platform publish options, keyed by platform.' example: [] properties: tiktok: type: object description: '' example: privacy_level: SELF_ONLY properties: privacy_level: type: string description: 'TikTok privacy, e.g. SELF_ONLY or PUBLIC_TO_EVERYONE.' example: SELF_ONLY auto_add_music: type: boolean description: "Auto-add TikTok's recommended music to a photo slideshow. Slideshows only; ignored for video. Defaults to false." example: false youtube: type: object description: '' example: privacy_status: public properties: privacy_status: type: string description: 'public, unlisted, or private.' example: public instagram: type: object description: '' example: cover_url: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' properties: cover_url: type: string description: 'Public cover image URL for an Instagram Reel.' example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' linkedin: type: object description: '' example: first_comment: architecto properties: first_comment: type: string description: 'A comment auto-posted right after publishing.' example: architecto '/api/posts/{id}': get: summary: '' operationId: getApiPostsId description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Posts put: summary: '' operationId: putApiPostsId description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Posts delete: summary: '' operationId: deleteApiPostsId description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Posts parameters: - in: path name: id description: 'The ID of the post.' example: architecto required: true schema: type: string /api/posts/media: post: summary: 'Upload a single post media file.' operationId: uploadASinglePostMediaFile description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Post Media' requestBody: required: false content: multipart/form-data: schema: type: object properties: type: type: string description: 'image|video. Required.' example: null file: type: string format: binary description: 'Image or video file. Required.' /api/posts/media/direct: post: summary: 'Create a direct-upload session.' operationId: createADirectUploadSession description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Post Media' requestBody: required: false content: application/json: schema: type: object properties: type: type: string description: 'image|video. Required.' example: architecto filename: type: string description: 'Original file name (display only). Required.' example: architecto mime: type: string description: 'File content type, e.g. video/mp4. Required.' example: architecto size: type: integer description: 'File size in bytes. Required.' example: 16 /api/posts/media/direct/complete: post: summary: 'Complete a direct upload.' operationId: completeADirectUpload description: "Finalizes the multipart upload (when upload_id is present), verifies the\nstored object against the declared type's size/mime limits (a presigned\nPUT cannot enforce size), and returns the media entry for the composer." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Post Media' requestBody: required: false content: application/json: schema: type: object properties: type: type: string description: 'image|video. Required.' example: architecto path: type: string description: 'Storage path returned by the session. Required.' example: architecto upload_id: type: string description: 'Multipart upload id (multipart only).' example: null parts: type: array description: 'Uploaded parts as {part_number, etag} (multipart only).' example: null items: type: object properties: part_number: type: integer description: 'Must be at least 1.' example: 22 etag: type: string description: '' example: architecto required: - part_number - etag /api/posts/media/direct/abort: post: summary: 'Abort a multipart direct upload.' operationId: abortAMultipartDirectUpload description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Post Media' requestBody: required: false content: application/json: schema: type: object properties: path: type: string description: 'Storage path returned by the session. Required.' example: architecto upload_id: type: string description: 'Multipart upload id. Required.' example: architecto /api/tracking-events/offers: get: summary: 'Get a list of distinct offer URLs used by the authenticated user.' operationId: getAListOfDistinctOfferURLsUsedByTheAuthenticatedUser description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers /api/tracking-events/stats: get: summary: 'Get aggregated stats for tracking events with date filtering and deduplication.' operationId: getAggregatedStatsForTrackingEventsWithDateFilteringAndDeduplication description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers /api/tracking-events/timeseries: get: summary: "Daily time-series of clicks and attributed revenue for the authenticated\nuser, used by the Analytics overview area charts. Returns one bucket per\nday across the requested window (gaps filled with zeros)." operationId: dailyTimeSeriesOfClicksAndAttributedRevenueForTheAuthenticatedUserUsedByTheAnalyticsOverviewAreaChartsReturnsOneBucketPerDayAcrossTheRequestedWindowgapsFilledWithZeros description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers /api/tracking-events/sources: get: summary: 'GA-style acquisition tables: where visitors actually came from.' operationId: gAStyleAcquisitionTablesWhereVisitorsActuallyCameFrom description: "GET /api/tracking-events/sources?from&to&event_id\n\nReturns `sources` (grouped by classified platform — google, x, direct…)\nand `referrers` (grouped by the FULL referrer URL), each with distinct\nvisitors + view counts. Pageview-based; falls back to link-click data\n(basis: \"clicks\") until the site has pageview beacons." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers /api/goal-types: get: summary: 'List the predefined conversion goal/event types (seeded reference list).' operationId: listThePredefinedConversionGoaleventTypesseededReferenceList description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers /api/tracking-events: get: summary: '' operationId: getApiTrackingEvents description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers post: summary: 'Store a newly created resource in storage.' operationId: storeANewlyCreatedResourceInStorage description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b nullable: true offer_url: type: string description: 'Must be a valid URL.' example: 'http://bailey.com/' goals: type: array description: 'Goals (conversion events) are optional at creation — added later on the offer page.' example: null items: type: object nullable: true properties: event_type: type: string description: "Built-in types (conversion, call booked, email-signup, newsletter, trial) plus\narbitrary user-defined \"custom\" events, which are stored verbatim. Must not be greater than 255 characters." example: m conversion_url: type: string description: '' example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' conversion_value: type: number description: 'Must be at least 0.' example: 8 nullable: true required: - event_type - conversion_url conversion_value: type: number description: '' example: 4326.41688 nullable: true links: type: array description: '' example: - [] items: type: object properties: youtube_video_id: type: string description: '' example: architecto nullable: true placement: type: string description: '' example: architecto nullable: true name: type: string description: '' example: architecto nullable: true description: type: string description: 'Must not be greater than 255 characters.' example: 'Et animi quos velit et fugiat.' nullable: true required: - offer_url '/api/tracking-events/{id}': get: summary: 'Display the specified resource.' operationId: displayTheSpecifiedResource description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers put: summary: 'Update the specified resource in storage.' operationId: updateTheSpecifiedResourceInStorage description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b nullable: true offer_url: type: string description: 'Must be a valid URL.' example: 'http://bailey.com/' goals: type: array description: '' example: null items: type: object nullable: true properties: event_type: type: string description: 'Must not be greater than 255 characters.' example: m conversion_url: type: string description: '' example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' conversion_value: type: number description: 'Must be at least 0.' example: 8 nullable: true required: - event_type - conversion_url conversion_value: type: number description: '' example: 4326.41688 nullable: true links: type: array description: '' example: null items: type: object nullable: true properties: id: type: integer description: '' example: 16 nullable: true youtube_video_id: type: string description: 'For updating existing links.' example: architecto nullable: true placement: type: string description: '' example: architecto nullable: true name: type: string description: '' example: architecto nullable: true description: type: string description: 'Must not be greater than 255 characters.' example: 'Et animi quos velit et fugiat.' nullable: true delete: summary: 'Remove the specified resource from storage.' operationId: removeTheSpecifiedResourceFromStorage description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Offers parameters: - in: path name: id description: 'The ID of the tracking event.' example: architecto required: true schema: type: string /api/tracking-links: post: summary: 'Create a new link.' operationId: createANewLink description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Tracking Links' requestBody: required: true content: application/json: schema: type: object properties: tracking_event_id: type: integer description: '' example: 16 youtube_video_id: type: string description: '' example: architecto nullable: true placement: type: string description: '' example: architecto nullable: true placements: type: array description: '' example: - architecto items: type: string beehiiv_post_id: type: string description: 'Must not be greater than 255 characters.' example: 'n' nullable: true x_post_id: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true instagram_media_id: type: string description: 'Must not be greater than 255 characters.' example: z nullable: true name: type: string description: '' example: architecto nullable: true description: type: string description: 'Must not be greater than 255 characters.' example: 'Et animi quos velit et fugiat.' nullable: true required: - tracking_event_id '/api/tracking-links/{id}': put: summary: 'Update the specified resource in storage.' operationId: updateTheSpecifiedResourceInStorage description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Tracking Links' requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: '' example: architecto nullable: true placement: type: string description: '' example: architecto placements: type: array description: '' example: - architecto items: type: string youtube_video_id: type: string description: '' example: architecto nullable: true beehiiv_post_id: type: string description: 'Must not be greater than 255 characters.' example: 'n' nullable: true x_post_id: type: string description: 'Must not be greater than 255 characters.' example: g nullable: true instagram_media_id: type: string description: 'Must not be greater than 255 characters.' example: z nullable: true description: type: string description: '' example: 'Eius et animi quos velit et.' nullable: true delete: summary: 'Remove the specified resource from storage.' operationId: removeTheSpecifiedResourceFromStorage description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Tracking Links' parameters: - in: path name: id description: 'The ID of the tracking link.' example: architecto required: true schema: type: string '/api/tracking-events/{event}/links': get: summary: 'List links for a specific event.' operationId: listLinksForASpecificEvent description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Tracking Links' parameters: - in: path name: event description: '' example: architecto required: true schema: type: string '/api/contents/{id}/media': get: summary: 'Download content media' operationId: downloadContentMedia description: 'Streams the stored media file for the content. Useful for large files.' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' 404: description: 'No media' content: application/json: schema: type: object example: message: 'This content has no media file.' properties: message: type: string example: 'This content has no media file.' tags: - Content parameters: - in: path name: id description: 'The content id.' example: 1 required: true schema: type: integer /api/contents: get: summary: 'List content' operationId: listContent description: "Returns the authenticated user's content, newest first. Optionally filter by offer or status." parameters: - in: query name: offer_id description: 'Filter to content attached to this offer.' example: 12 required: false schema: type: integer description: 'Filter to content attached to this offer.' example: 12 - in: query name: status description: 'Filter by status (draft|published).' example: published required: false schema: type: string description: 'Filter by status (draft|published).' example: published responses: 200: description: Success content: application/json: schema: type: object example: data: - id: 1 user_id: 1 offer_id: 12 title: 'Launch post' body: ... media_filename: promo.mp4 media_mime: video/mp4 media_size: 8388608 status: published created_at: '2026-06-16T12:00:00.000000Z' updated_at: '2026-06-16T12:00:00.000000Z' properties: data: type: array example: - id: 1 user_id: 1 offer_id: 12 title: 'Launch post' body: ... media_filename: promo.mp4 media_mime: video/mp4 media_size: 8388608 status: published created_at: '2026-06-16T12:00:00.000000Z' updated_at: '2026-06-16T12:00:00.000000Z' items: type: object properties: id: type: integer example: 1 user_id: type: integer example: 1 offer_id: type: integer example: 12 title: type: string example: 'Launch post' body: type: string example: ... media_filename: type: string example: promo.mp4 media_mime: type: string example: video/mp4 media_size: type: integer example: 8388608 status: type: string example: published created_at: type: string example: '2026-06-16T12:00:00.000000Z' updated_at: type: string example: '2026-06-16T12:00:00.000000Z' tags: - Content post: summary: 'Create content' operationId: createContent description: "Create a content post. Send as `multipart/form-data` when including a media file.\nThe `body` accepts long-form text. `media` accepts a single file up to 50 MB." parameters: [] responses: 201: description: Created content: application/json: schema: type: object example: data: id: 1 user_id: 1 offer_id: 12 title: 'Launch post' body: ... media_filename: promo.mp4 media_mime: video/mp4 media_size: 8388608 status: published properties: data: type: object properties: id: type: integer example: 1 user_id: type: integer example: 1 offer_id: type: integer example: 12 title: type: string example: 'Launch post' body: type: string example: ... media_filename: type: string example: promo.mp4 media_mime: type: string example: video/mp4 media_size: type: integer example: 8388608 status: type: string example: published 422: description: 'Validation error' content: application/json: schema: type: object example: message: 'The title field is required.' errors: title: - 'The title field is required.' properties: message: type: string example: 'The title field is required.' errors: type: object properties: title: type: array example: - 'The title field is required.' items: type: string tags: - Content requestBody: required: true content: multipart/form-data: schema: type: object properties: title: type: string description: 'The content title.' example: '5 ways to grow your channel' body: type: string description: 'The long-form body text (no length limit).' example: 'Once upon a time...' offer_id: type: integer description: 'The id of an offer (owned by you) to attach this content to.' example: 12 status: type: string description: 'The status: draft or published. Defaults to draft.' example: published media: type: string format: binary description: 'A media file to attach (image/video/document, max 50 MB).' required: - title '/api/contents/{id}': get: summary: 'Show content' operationId: showContent description: '' parameters: [] responses: 200: description: Success content: application/json: schema: type: object example: data: id: 1 title: 'Launch post' body: ... status: published properties: data: type: object properties: id: type: integer example: 1 title: type: string example: 'Launch post' body: type: string example: ... status: type: string example: published 404: description: 'Not found' content: application/json: schema: type: object example: message: 'No query results for model [App\Models\Content] 1' properties: message: type: string example: 'No query results for model [App\Models\Content] 1' tags: - Content put: summary: 'Update content' operationId: updateContent description: 'Update fields and/or replace the media file. Send `multipart/form-data` to replace media.' parameters: [] responses: 200: description: Updated content: application/json: schema: type: object example: data: id: 1 title: 'Updated title' status: published properties: data: type: object properties: id: type: integer example: 1 title: type: string example: 'Updated title' status: type: string example: published tags: - Content requestBody: required: false content: multipart/form-data: schema: type: object properties: title: type: string description: 'The content title.' example: 'Updated title' body: type: string description: 'The long-form body text.' example: 'New body text...' offer_id: type: integer description: 'The id of an offer (owned by you) to attach.' example: 12 status: type: string description: 'draft or published.' example: published media: type: string format: binary description: 'Replacement media file (max 50 MB).' delete: summary: 'Delete content' operationId: deleteContent description: 'Deletes the content and any stored media file.' parameters: [] responses: 204: description: Deleted content: text/plain: schema: type: string example: '' tags: - Content parameters: - in: path name: id description: 'The content id.' example: 1 required: true schema: type: integer /api/connections: get: summary: "List the authenticated user's connections." operationId: listTheAuthenticatedUsersConnections description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections '/api/connections/{id}': delete: summary: 'Delete a connection belonging to the caller.' operationId: deleteAConnectionBelongingToTheCaller description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections parameters: - in: path name: id description: 'The ID of the connection.' example: architecto required: true schema: type: string /api/connections/tiktok/creator-info: get: summary: "Return the TikTok creator's allowed posting options (privacy levels,\ncomment/duet/stitch availability, max duration). The composer must render\nthe privacy/interaction UI from this before a post can be published." operationId: returnTheTikTokCreatorsAllowedPostingOptionsprivacyLevelsCommentduetstitchAvailabilityMaxDurationTheComposerMustRenderThePrivacyinteractionUIFromThisBeforeAPostCanBePublished description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections /api/social/platforms: get: summary: 'List every platform the app supports plus whether it is configured.' operationId: listEveryPlatformTheAppSupportsPlusWhetherItIsConfigured description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections /api/social/accounts: get: summary: "List the authenticated user's connected accounts (optionally by platform)." operationId: listTheAuthenticatedUsersConnectedAccountsoptionallyByPlatform description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections '/api/social/accounts/{id}': delete: summary: 'Disconnect (delete) a connected account.' operationId: disconnectdeleteAConnectedAccount description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections parameters: - in: path name: id description: 'The ID of the account.' example: architecto required: true schema: type: string '/api/social/{platform}/auth-url': get: summary: "Build the OAuth authorization URL for a platform. The frontend opens this,\nthe user grants access, and the provider redirects back to redirect_uri\nwith a `code` to be sent to exchange()." operationId: buildTheOAuthAuthorizationURLForAPlatformTheFrontendOpensThisTheUserGrantsAccessAndTheProviderRedirectsBackToRedirectUriWithAcodeToBeSentToExchange description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections parameters: - in: path name: platform description: '' example: architecto required: true schema: type: string '/api/social/{platform}/exchange': post: summary: "Exchange an authorization code (returned to the frontend callback) for\ntokens and persist the connected account(s)." operationId: exchangeAnAuthorizationCodereturnedToTheFrontendCallbackForTokensAndPersistTheConnectedAccounts description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections requestBody: required: true content: application/json: schema: type: object properties: code: type: string description: '' example: architecto state: type: string description: '' example: architecto nullable: true redirect_uri: type: string description: 'Must be a valid URL.' example: 'http://bailey.com/' nullable: true required: - code parameters: - in: path name: platform description: '' example: architecto required: true schema: type: string '/api/social/{platform}/connect': post: summary: 'Connect a non-OAuth platform (e.g. Bluesky) with direct credentials.' operationId: connectANonOAuthPlatformegBlueskyWithDirectCredentials description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections parameters: - in: path name: platform description: '' example: architecto required: true schema: type: string /api/social/x/users/search: get: summary: 'Search X users by handle prefix.' operationId: searchXUsersByHandlePrefix description: '' parameters: - in: query name: q description: 'The typed handle fragment, with or without a leading @.' example: jane required: true schema: type: string description: 'The typed handle fragment, with or without a leading @.' example: jane - in: query name: social_account_id description: 'Search as a specific connected X account.' example: 1 required: false schema: type: integer description: 'Search as a specific connected X account.' example: 1 responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Connections requestBody: required: true content: application/json: schema: type: object properties: q: type: string description: 'Must not be greater than 50 characters.' example: b social_account_id: type: integer description: '' example: 16 nullable: true required: - q /api/social/x/posts: get: summary: "The user's X posts published through the app (both posting stores),\nnewest first — used to pin a tracking link to a live tweet. Pure DB\nread; the X API plan doesn't allow timeline reads." operationId: theUsersXPostsPublishedThroughTheAppbothPostingStoresNewestFirstUsedToPinATrackingLinkToALiveTweetPureDBReadTheXAPIPlanDoesntAllowTimelineReads description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Social Posts' /api/social/posts: get: summary: "List the user's posts (most recent first)." operationId: listTheUsersPostsmostRecentFirst description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Social Posts' post: summary: 'Create a post and publish (or schedule) it to the chosen accounts.' operationId: createAPostAndPublishorScheduleItToTheChosenAccounts description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Social Posts' requestBody: required: false content: application/json: schema: type: object properties: content: type: string description: 'Must not be greater than 10000 characters.' example: b nullable: true link: type: string description: 'Must be a valid URL. Must not be greater than 1024 characters.' example: 'n' nullable: true media: type: array description: '' example: null items: type: object nullable: true properties: url: type: string description: 'This field is required when media is present. Must be a valid URL.' example: 'http://bailey.com/' type: type: string description: '' example: video enum: - image - video nullable: true mime: type: string description: '' example: architecto nullable: true alt: type: string description: '' example: architecto nullable: true account_ids: type: array description: '' example: - 16 items: type: integer scheduled_at: type: string description: 'Must be a valid date. Must be a date after now.' example: '2052-10-25' nullable: true '/api/social/posts/{id}': get: summary: '' operationId: getApiSocialPostsId description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Social Posts' parameters: - in: path name: id description: 'The ID of the post.' example: architecto required: true schema: type: string '/api/social/posts/{id}/retry': post: summary: 'Retry the failed targets of a post.' operationId: retryTheFailedTargetsOfAPost description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Social Posts' parameters: - in: path name: id description: 'The ID of the post.' example: architecto required: true schema: type: string /api/user/api-key: get: summary: '' operationId: getApiUserApiKey description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'API Keys' /api/user/api-key/rotate: post: summary: '' operationId: postApiUserApiKeyRotate description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'API Keys' requestBody: required: false content: application/json: schema: type: object properties: access: type: string description: '' example: read enum: - read - full /api/plans: get: summary: 'Display a listing of plans.' operationId: displayAListingOfPlans description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Plans '/api/plans/{id}': get: summary: 'Display the specified plan.' operationId: displayTheSpecifiedPlan description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Plans parameters: - in: path name: id description: 'The ID of the plan.' example: architecto required: true schema: type: string /api/feature-requests: get: summary: 'List feature requests, sorted by top votes (default) or newest.' operationId: listFeatureRequestsSortedByTopVotesdefaultOrNewest description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Feature Requests' post: summary: 'Create a feature request and auto-upvote it for the creator.' operationId: createAFeatureRequestAndAutoUpvoteItForTheCreator description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Feature Requests' requestBody: required: true content: application/json: schema: type: object properties: title: type: string description: 'Must not be greater than 255 characters.' example: b description: type: string description: '' example: 'Eius et animi quos velit et.' category: type: string description: 'Must not be greater than 255 characters.' example: v nullable: true required: - title - description '/api/feature-requests/{id}/upvote': post: summary: "Toggle the caller's vote on a feature request." operationId: toggleTheCallersVoteOnAFeatureRequest description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Feature Requests' parameters: - in: path name: id description: 'The ID of the feature request.' example: architecto required: true schema: type: string /api/register: post: summary: 'Register a new user' operationId: registerANewUser description: '' parameters: [] responses: 422: description: '' content: application/json: schema: type: object example: success: false message: 'Validation failed' errors: password: - 'The password field confirmation does not match.' properties: success: type: boolean example: false message: type: string example: 'Validation failed' errors: type: object properties: password: type: array example: - 'The password field confirmation does not match.' items: type: string tags: - Auth requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Must not be greater than 255 characters.' example: b email: type: string description: 'Must be a valid email address. Must not be greater than 255 characters.' example: zbailey@example.net password: type: string description: 'Must be at least 8 characters.' example: '-0pBNvYgxw' marketing_consent: type: boolean description: '' example: false nullable: true required: - name - email - password security: [] /api/login: post: summary: 'Login user' operationId: loginUser description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid credentials' properties: success: type: boolean example: false message: type: string example: 'Invalid credentials' tags: - Auth requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'Must be a valid email address.' example: gbailey@example.net password: type: string description: '' example: '|]|{+-' required: - email - password security: [] /api/forgot-password: post: summary: 'Send password reset email' operationId: sendPasswordResetEmail description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'If an account with that email exists, we have sent a password reset link.' properties: success: type: boolean example: true message: type: string example: 'If an account with that email exists, we have sent a password reset link.' tags: - Auth requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'Must be a valid email address. Must not be greater than 255 characters.' example: gbailey@example.net required: - email /api/reset-password: post: summary: 'Reset password using token' operationId: resetPasswordUsingToken description: '' parameters: [] responses: 422: description: '' content: application/json: schema: type: object example: success: false message: 'Validation failed' errors: password: - 'The password field confirmation does not match.' properties: success: type: boolean example: false message: type: string example: 'Validation failed' errors: type: object properties: password: type: array example: - 'The password field confirmation does not match.' items: type: string tags: - Auth requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'Must be a valid email address. Must not be greater than 255 characters.' example: gbailey@example.net token: type: string description: '' example: architecto password: type: string description: 'Must be at least 8 characters.' example: ']|{+-0pBNvYg' required: - email - token - password /api/auth/verify-email: post: summary: "Verify a user's email via the magic-link token. Logs the user in on success." operationId: verifyAUsersEmailViaTheMagicLinkTokenLogsTheUserInOnSuccess description: 'Idempotent under React StrictMode double-calls.' parameters: [] responses: 422: description: '' content: application/json: schema: type: object example: message: 'This verification link is invalid or has expired.' properties: message: type: string example: 'This verification link is invalid or has expired.' tags: - Auth requestBody: required: true content: application/json: schema: type: object properties: token: type: string description: '' example: architecto required: - token /api/auth/resend-verification: post: summary: 'Resend a verification magic link. Always returns 200 (no account enumeration).' operationId: resendAVerificationMagicLinkAlwaysReturns200noAccountEnumeration description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true properties: success: type: boolean example: true tags: - Auth requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'Must be a valid email address.' example: gbailey@example.net required: - email /api/logout: post: summary: 'Logout user (revoke token)' operationId: logoutUserrevokeToken description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Auth /api/profile: get: summary: 'Get authenticated user profile' operationId: getAuthenticatedUserProfile description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Auth /api/refresh: post: summary: 'Refresh token (create new token and revoke old one)' operationId: refreshTokencreateNewTokenAndRevokeOldOne description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Auth /api/analytics/audience: get: summary: "Per-platform follower series over the date range, one entry per connected\naccount: current count, day-over-day delta across the range, and points." operationId: perPlatformFollowerSeriesOverTheDateRangeOneEntryPerConnectedAccountCurrentCountDayOverDayDeltaAcrossTheRangeAndPoints description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Audience Growth' /api/analytics/posts: get: summary: "Posts ranked by engagement (total, latest snapshot in range) with a\nday-over-day delta. Optional ?platform= filter." operationId: postsRankedByEngagementtotalLatestSnapshotInRangeWithADayOverDayDeltaOptionalplatformFilter description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - 'Audience Growth' /api/boosts/settings: get: summary: "All of the user's boost settings, keyed for the Boosts page." operationId: allOfTheUsersBoostSettingsKeyedForTheBoostsPage description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Boosts '/api/boosts/settings/{socialAccountId}': put: summary: "Create or update one feature's setting on one connected account." operationId: createOrUpdateOneFeaturesSettingOnOneConnectedAccount description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Boosts requestBody: required: true content: application/json: schema: type: object properties: feature: type: string description: '' example: architecto enabled: type: boolean description: '' example: false likes_threshold: type: integer description: 'Must be at least 1. Must not be greater than 1000000.' example: 22 promo_text: type: string description: '' example: architecto nullable: true required: - feature - enabled - likes_threshold parameters: - in: path name: socialAccountId description: '' example: architecto required: true schema: type: string /api/boosts/activity: get: summary: 'Recent boost activity (triggered/exhausted/failed checks), newest first.' operationId: recentBoostActivitytriggeredexhaustedfailedChecksNewestFirst description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Boosts /api/brands: get: summary: "List the authenticated user's brands with their member accounts." operationId: listTheAuthenticatedUsersBrandsWithTheirMemberAccounts description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Brands post: summary: 'Create a brand.' operationId: createABrand description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Brands requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Brand name, unique per user.' example: Acme social_account_ids: type: array description: 'Ids from the social accounts store.' example: - 1 - 2 items: type: integer connection_ids: type: array description: 'Ids from the legacy connections store (YouTube/TikTok).' example: - 3 items: type: integer required: - name '/api/brands/{id}': put: summary: "Update a brand. Member lists are replaced only when their key is present,\nso a rename-only payload never wipes membership." operationId: updateABrandMemberListsAreReplacedOnlyWhenTheirKeyIsPresentSoARenameOnlyPayloadNeverWipesMembership description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Brands delete: summary: "Delete a brand. Member accounts are untouched; posts keep existing but\nlose their brand link." operationId: deleteABrandMemberAccountsAreUntouchedPostsKeepExistingButLoseTheirBrandLink description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Brands parameters: - in: path name: id description: 'The ID of the brand.' example: architecto required: true schema: type: string /api/free-tools/transcript: post: summary: '' operationId: postApiFreeToolsTranscript description: '' parameters: [] responses: 422: description: '' content: application/json: schema: type: object example: success: false message: "That doesn't look like a valid youtube link." properties: success: type: boolean example: false message: type: string example: "That doesn't look like a valid youtube link." tags: - Endpoints requestBody: required: true content: application/json: schema: type: object properties: platform: type: string description: '' example: youtube enum: - youtube - tiktok - instagram url: type: string description: 'Must be a valid URL. Must not be greater than 2048 characters.' example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' required: - platform - url /api/health: get: summary: '' operationId: getApiHealth description: '' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: healthy timestamp: '2026-10-02T07:31:18.968796Z' service: 'Title Embedding API' version: 1.0.0 properties: status: type: string example: healthy timestamp: type: string example: '2026-10-02T07:31:18.968796Z' service: type: string example: 'Title Embedding API' version: type: string example: 1.0.0 tags: - Endpoints /api/user/settings: get: summary: '' operationId: getApiUserSettings description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Endpoints patch: summary: '' operationId: patchApiUserSettings description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Endpoints requestBody: required: false content: application/json: schema: type: object properties: notify_post_failures: type: boolean description: '' example: true locale: type: string description: '' example: es enum: - en - es - de - fr - pt nullable: true /api/user: get: summary: '' operationId: getApiUser description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Endpoints /api/user/mcp-activity: get: summary: '' operationId: getApiUserMcpActivity description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Endpoints /api/image/generate/config: get: summary: 'Get Image Generation Configuration' operationId: getImageGenerationConfiguration description: 'Retrieve configuration options for image generation including available methods, quality presets, defaults, and limits.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'Configuration retrieved successfully' data: enabled: true methods: - generate - head_swap qualities: - fast - normal - high - very_high defaults: quality: fast steps: 4 refine_enabled: false inpainting_enabled: true number_of_images: 1 limits: max_prompt_length: 2000 max_image_size_bytes: 10485760 max_additional_images: 3 max_number_of_images: 10 properties: success: type: boolean example: true message: type: string example: 'Configuration retrieved successfully' data: type: object properties: enabled: type: boolean example: true methods: type: array example: - generate - head_swap items: type: string qualities: type: array example: - fast - normal - high - very_high items: type: string defaults: type: object properties: quality: type: string example: fast steps: type: integer example: 4 refine_enabled: type: boolean example: false inpainting_enabled: type: boolean example: true number_of_images: type: integer example: 1 limits: type: object properties: max_prompt_length: type: integer example: 2000 max_image_size_bytes: type: integer example: 10485760 max_additional_images: type: integer example: 3 max_number_of_images: type: integer example: 10 tags: - 'Image Generation' /api/image/generate: get: summary: 'List Generated Images' operationId: listGeneratedImages description: "Get a paginated list of the authenticated user's generated images. Can be filtered by status and method." parameters: - in: query name: status description: 'Filter by status: `pending`, `processing`, `completed`, `failed`.' example: completed required: false schema: type: string description: 'Filter by status: `pending`, `processing`, `completed`, `failed`.' example: completed - in: query name: method description: 'Filter by method: `generate`, `head_swap`.' example: generate required: false schema: type: string description: 'Filter by method: `generate`, `head_swap`.' example: generate - in: query name: per_page description: 'Number of results per page.' example: 15 required: false schema: type: integer description: 'Number of results per page.' example: 15 responses: 200: description: '' content: text/plain: schema: type: string example: '{"success":true,"message":"Images retrieved successfully","data":{"current_page":1,"data":[...],"per_page":15,"total":100}}' tags: - 'Image Generation' post: summary: 'Create Image Generation Request' operationId: createImageGenerationRequest description: 'Create a new image generation request. Credits will be deducted based on the number of images requested (50 credits per image).' parameters: [] responses: 202: description: '' content: application/json: schema: type: object example: success: true message: 'Image generation started' data: id: 123 status: pending method: generate quality: normal estimated_time_seconds: 40 properties: success: type: boolean example: true message: type: string example: 'Image generation started' data: type: object properties: id: type: integer example: 123 status: type: string example: pending method: type: string example: generate quality: type: string example: normal estimated_time_seconds: type: integer example: 40 400: description: '' content: application/json: schema: oneOf: - description: '' type: object example: success: false message: 'For generate method, please upload a base image, provide an image URL, or set a default reference image at /user/default-image.' properties: success: type: boolean example: false message: type: string example: 'For generate method, please upload a base image, provide an image URL, or set a default reference image at /user/default-image.' - description: '' type: object example: success: false message: 'Reference image is required for head swap. You can upload a default image at /user/default-image.' properties: success: type: boolean example: false message: type: string example: 'Reference image is required for head swap. You can upload a default image at /user/default-image.' - description: '' type: object example: success: false message: 'Failed to download or validate base image from URL. Please check the URL and try again.' properties: success: type: boolean example: false message: type: string example: 'Failed to download or validate base image from URL. Please check the URL and try again.' 503: description: '' content: application/json: schema: type: object example: success: false message: 'Image generation feature is not enabled' properties: success: type: boolean example: false message: type: string example: 'Image generation feature is not enabled' tags: - 'Image Generation' requestBody: required: false content: multipart/form-data: schema: type: object properties: method: type: string description: 'The generation method. Must be one of: `generate`, `head_swap`. Default: `head_swap`.' example: generate nullable: true quality: type: string description: 'The quality preset. Must be one of: `fast`, `normal`, `high`, `very_high`. Default: `fast`.' example: normal nullable: true prompt: type: string description: 'Text prompt for generation (max 2000 characters).' example: 'A person standing confidently in front of a mountain' nullable: true base_image: type: string format: binary description: 'Required for `head_swap`, optional for `generate`. Image file (max 10MB). Provide either `base_image` or `base_image_url`.' base_image_url: type: string description: 'Required for `head_swap`, optional for `generate`. URL to download the base image from. Provide either `base_image` or `base_image_url`.' example: 'https://example.com/image.jpg' reference_image: type: string format: binary description: 'Optional reference image file (max 10MB). For `generate` method, this is the person to generate. For `head_swap` method, this is the source face.' nullable: true number_of_images: type: integer description: 'Number of images to generate (1-10). Default: `1`.' example: 2 nullable: true '/api/image/generate/{id}': get: summary: 'Get Generated Image Details' operationId: getGeneratedImageDetails description: 'Retrieve detailed information about a specific generated image.' parameters: [] responses: 200: description: '' content: text/plain: schema: type: string example: '{"success":true,"message":"Generated image retrieved successfully","data":{"id":123,"method":"generate","quality":"normal","status":"completed","prompt":"A person standing confidently","base_image_url":"https://...","reference_image_url":"https://...","result_image_url":"https://...","result_image_urls":["https://...","https://..."],"result_image_count":2,"megapixel":0.5,"steps":4,"refine_enabled":false,"inpainting_enabled":true,"number_of_images":2,"error_message":null,"processing_log":[...],"created_at":"2024-01-28T10:00:00Z","updated_at":"2024-01-28T10:01:00Z"}}' 404: description: '' content: application/json: schema: type: object example: success: false message: 'Generated image not found' properties: success: type: boolean example: false message: type: string example: 'Generated image not found' tags: - 'Image Generation' delete: summary: 'Delete Generated Image' operationId: deleteGeneratedImage description: 'Delete a generated image and all associated files.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'Generated image deleted successfully' properties: success: type: boolean example: true message: type: string example: 'Generated image deleted successfully' 404: description: '' content: application/json: schema: type: object example: success: false message: 'Generated image not found' properties: success: type: boolean example: false message: type: string example: 'Generated image not found' tags: - 'Image Generation' parameters: - in: path name: id description: 'Optional parameter. The ID of the generated image.' required: true schema: type: integer examples: omitted: summary: 'When the value is omitted' value: '' present: summary: 'When the value is present' value: 123 '/api/image/generate/{id}/status': get: summary: 'Get Image Generation Status' operationId: getImageGenerationStatus description: 'Check the processing status of a generated image. Use this to poll for completion.' parameters: [] responses: 200: description: '' content: application/json: schema: oneOf: - description: '' type: object example: success: true message: 'Status retrieved successfully' data: id: 123 status: processing is_complete: false is_successful: false properties: success: type: boolean example: true message: type: string example: 'Status retrieved successfully' data: type: object properties: id: type: integer example: 123 status: type: string example: processing is_complete: type: boolean example: false is_successful: type: boolean example: false - description: '' type: object example: success: true message: 'Status retrieved successfully' data: id: 123 status: completed is_complete: true is_successful: true result_image_url: 'https://...' result_image_urls: - 'https://...' result_image_count: 2 properties: success: type: boolean example: true message: type: string example: 'Status retrieved successfully' data: type: object properties: id: type: integer example: 123 status: type: string example: completed is_complete: type: boolean example: true is_successful: type: boolean example: true result_image_url: type: string example: 'https://...' result_image_urls: type: array example: - 'https://...' items: type: string result_image_count: type: integer example: 2 - description: '' type: object example: success: true message: 'Status retrieved successfully' data: id: 123 status: failed is_complete: true is_successful: false error_message: 'Error details...' properties: success: type: boolean example: true message: type: string example: 'Status retrieved successfully' data: type: object properties: id: type: integer example: 123 status: type: string example: failed is_complete: type: boolean example: true is_successful: type: boolean example: false error_message: type: string example: 'Error details...' 404: description: '' content: application/json: schema: type: object example: success: false message: 'Generated image not found' properties: success: type: boolean example: false message: type: string example: 'Generated image not found' tags: - 'Image Generation' parameters: - in: path name: id description: 'Optional parameter. The ID of the generated image.' required: true schema: type: integer examples: omitted: summary: 'When the value is omitted' value: '' present: summary: 'When the value is present' value: 123 '/api/image/generate/{id}/download': get: summary: 'Download Generated Image' operationId: downloadGeneratedImage description: 'Download the generated image file. Returns a binary PNG file.' parameters: [] responses: 400: description: '' content: application/json: schema: type: object example: success: false message: 'No result image available for download' properties: success: type: boolean example: false message: type: string example: 'No result image available for download' 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' 404: description: '' content: application/json: schema: type: object example: success: false message: 'Generated image not found' properties: success: type: boolean example: false message: type: string example: 'Generated image not found' tags: - 'Image Generation' parameters: - in: path name: id description: 'Optional parameter. The ID of the generated image.' required: true schema: type: integer examples: omitted: summary: 'When the value is omitted' value: '' present: summary: 'When the value is present' value: 123 /api/outliers: get: summary: 'Browse outliers' operationId: browseOutliers description: "Paginated outlier videos. Without `query` this is the curated feed; with\n`query` it returns title matches already in the database plus a `status`\n(queued / in_progress / done) for the background scrape of that term —\nstart one with the search endpoint." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: false content: application/json: schema: type: object properties: query: type: string description: '' example: architecto nullable: true min_score: type: number description: 'Must be at least 0.' example: 39 nullable: true min_views: type: number description: 'Must be at least 0.' example: 84 nullable: true max_views: type: number description: 'Must be at least 0.' example: 12 nullable: true min_subs: type: number description: 'Must be at least 0.' example: 77 nullable: true max_subs: type: number description: 'Must be at least 0.' example: 8 nullable: true published_before: type: string description: 'Must be a valid date.' example: '2026-10-02T07:31:19' nullable: true published_after: type: string description: 'Must be a valid date.' example: '2026-10-02T07:31:19' nullable: true sort_by: type: string description: '' example: recent enum: - score - date - views - recent nullable: true keyword_match: type: string description: '' example: architecto nullable: true duration_type: type: string description: '' example: null featured: type: boolean description: '' example: true nullable: true platform: type: string description: '' example: tiktok enum: - youtube - tiktok - instagram nullable: true channels: type: array description: '' example: - architecto items: type: string countries: type: array description: 'Must contain only letters. Must be 2 characters.' example: - ng items: type: string page: type: integer description: 'Must be at least 1.' example: 66 nullable: true per_page: type: integer description: 'Must be at least 1. Must not be greater than 100.' example: 17 nullable: true /api/outliers/channels: get: summary: 'List outlier channels' operationId: listOutlierChannels description: 'Distinct channels in the outlier database (for the `channels` filter of the browse endpoint).' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: false content: application/json: schema: type: object properties: platform: type: string description: '' example: instagram enum: - youtube - tiktok - instagram nullable: true q: type: string description: '' example: architecto nullable: true limit: type: integer description: 'Must be at least 1. Must not be greater than 100.' example: 22 nullable: true /api/outliers/channels/add: post: summary: 'Add a channel' operationId: addAChannel description: "Queue a pull of the creator's most recent videos. Returns HTTP 200 with\n`status: done` when the channel was already pulled in the last 24 hours,\notherwise HTTP 202 with an `ingest_id` to poll." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: true content: application/json: schema: type: object properties: platform: type: string description: 'Required for a bare @handle: youtube, tiktok or instagram.' example: tiktok nullable: true input: type: string description: 'Profile URL or @handle.' example: 'https://www.tiktok.com/@khaby.lame' max_videos: type: integer description: 'How many recent videos to pull (5-50, default 10).' example: 10 nullable: true required: - input '/api/outliers/channels/ingests/{id}': get: summary: 'Channel ingest status' operationId: channelIngestStatus description: "Poll an ingest started by the add endpoint until `status` is `done`\n(then `channel` is set) or `failed` (then `error` explains why)." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers parameters: - in: path name: id description: 'The ingest id.' example: 12 required: true schema: type: integer /api/outliers/search: post: summary: 'Start an outlier search' operationId: startAnOutlierSearch description: "Queue a background scrape for a keyword/topic. Poll the browse endpoint\nwith the same `query` until its `status` is `done`." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: true content: application/json: schema: type: object properties: term: type: string description: 'Keyword or topic.' example: 'faceless youtube automation' exact_match: type: boolean description: 'Match the whole phrase only.' example: false required: - term /api/outliers/fetch: post: summary: 'Fetch an outlier by URL' operationId: fetchAnOutlierByURL description: "Ingest a single video by URL so it can be analysed. Known videos return\nimmediately; otherwise ingestion is queued (HTTP 202, `queued: true`) —\npoll the show endpoint with the returned platform + video_id. YouTube goes\nthrough the YouTube API; TikTok/Instagram go through CaptAPI (no\nchannel-listing endpoint there, so those are pulled one URL at a time)." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: true content: application/json: schema: type: object properties: platform: type: string description: '' example: instagram enum: - youtube - tiktok - instagram url: type: string description: 'Must be a valid URL.' example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' required: - platform - url /api/outliers/tags: get: summary: 'List library tags' operationId: listLibraryTags description: 'Distinct tag names the user has applied to saved outliers.' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers /api/outliers/library: get: summary: 'List saved outliers' operationId: listSavedOutliers description: "The user's library, newest first." parameters: - in: query name: q description: 'Matches saved title or channel name.' example: hook required: false schema: type: string description: 'Matches saved title or channel name.' example: hook - in: query name: tags description: 'Only items carrying any of these tag names.' example: - architecto required: false schema: type: array description: 'Only items carrying any of these tag names.' example: - architecto items: type: string - in: query name: platforms description: 'youtube, tiktok, instagram.' example: - architecto required: false schema: type: array description: 'youtube, tiktok, instagram.' example: - architecto items: type: string - in: query name: creator description: 'Channel/creator name filter.' example: architecto required: false schema: type: string description: 'Channel/creator name filter.' example: architecto responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers post: summary: 'Save an outlier' operationId: saveAnOutlier description: "Bookmark a video into the library with a snapshot of its stats. Saving the\nsame video again replaces its tags." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: true content: application/json: schema: type: object properties: platform: type: string description: '' example: tiktok enum: - youtube - tiktok - instagram video_id: type: string description: '' example: architecto snapshot: type: object description: '' example: [] properties: { } tags: type: array description: 'Must not be greater than 50 characters.' example: - b items: type: string required: - platform - video_id - snapshot '/api/outliers/library/{id}': patch: summary: "Update a saved outlier's tags" operationId: updateASavedOutliersTags description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers requestBody: required: false content: application/json: schema: type: object properties: tags: type: array description: 'Must not be greater than 50 characters.' example: - b items: type: string delete: summary: 'Remove a saved outlier' operationId: removeASavedOutlier description: '' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers parameters: - in: path name: id description: 'The ID of the library.' example: architecto required: true schema: type: string '/api/outliers/{platform}/{videoId}': get: summary: 'Get an outlier' operationId: getAnOutlier description: 'A single outlier video with its channel.' parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers parameters: - in: path name: platform description: 'youtube, tiktok, or instagram.' example: youtube required: true schema: type: string - in: path name: videoId description: "The platform's video id." example: dQw4w9WgXcQ required: true schema: type: string '/api/outliers/{platform}/{videoId}/breakdown': get: summary: "Get an outlier's AI breakdown" operationId: getAnOutliersAIBreakdown description: "`status` is none (never generated), pending/processing (poll again),\ncompleted (`payload` holds the analysis) or failed (`error`)." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers post: summary: "Generate an outlier's AI breakdown" operationId: generateAnOutliersAIBreakdown description: "Queues generation (transcript + LLM analysis); poll the GET endpoint until\n`status` is completed. An existing breakdown is returned, not regenerated." parameters: [] responses: 401: description: '' content: application/json: schema: type: object example: success: false message: 'Invalid token' properties: success: type: boolean example: false message: type: string example: 'Invalid token' tags: - Outliers parameters: - in: path name: platform description: 'youtube, tiktok, or instagram.' example: youtube required: true schema: type: string - in: path name: videoId description: "The platform's video id." example: dQw4w9WgXcQ required: true schema: type: string /api/user/default-image: get: summary: 'Get Default Reference Image' operationId: getDefaultReferenceImage description: "Retrieve the authenticated user's current default reference image." parameters: [] responses: 200: description: '' content: application/json: schema: oneOf: - description: '' type: object example: success: true message: 'Default reference image retrieved successfully' data: has_default_image: true image_url: 'https://your-domain.com/storage/user-defaults/1/image.png' properties: success: type: boolean example: true message: type: string example: 'Default reference image retrieved successfully' data: type: object properties: has_default_image: type: boolean example: true image_url: type: string example: 'https://your-domain.com/storage/user-defaults/1/image.png' - description: '' type: object example: success: true message: 'No default reference image set' data: has_default_image: false image_url: null properties: success: type: boolean example: true message: type: string example: 'No default reference image set' data: type: object properties: has_default_image: type: boolean example: false image_url: type: string example: null tags: - 'User Default Reference Image' post: summary: 'Upload Default Reference Image' operationId: uploadDefaultReferenceImage description: 'Upload a new default reference image for the authenticated user. This image will be used as a fallback when no reference image is provided in generation requests.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'Default reference image uploaded successfully' data: has_default_image: true image_url: 'https://your-domain.com/storage/user-defaults/1/image.png' properties: success: type: boolean example: true message: type: string example: 'Default reference image uploaded successfully' data: type: object properties: has_default_image: type: boolean example: true image_url: type: string example: 'https://your-domain.com/storage/user-defaults/1/image.png' tags: - 'User Default Reference Image' requestBody: required: false content: multipart/form-data: schema: type: object properties: image: type: string format: binary description: 'Image file (max 10MB). Required.' delete: summary: 'Delete Default Reference Image' operationId: deleteDefaultReferenceImage description: "Remove the authenticated user's default reference image." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: success: true message: 'Default reference image removed successfully' properties: success: type: boolean example: true message: type: string example: 'Default reference image removed successfully' 400: description: '' content: application/json: schema: type: object example: success: false message: 'No default reference image to remove' properties: success: type: boolean example: false message: type: string example: 'No default reference image to remove' tags: - 'User Default Reference Image'