Reference

    Preflight API

    The Preflight API is a REST API for publishing to social networks from your own code: upload a video, write a post, schedule it for every network at once — YouTube, TikTok, Instagram, Facebook, Threads, X, LinkedIn, Pinterest, Bluesky, Mastodon, Telegram, Discord and Tumblr — read its figures and take it down again, over HTTP, from your own server. Every post is checked against each network's rules before it is accepted. The same workspace is open to AI assistants through an MCP server.

    https://preflight.social/v1

    The same API as a schema: openapi.json (OpenAPI 3.1), for client generators and anything that reads a schema before prose. For assistants, the MCP server.

    The reference

    Quickstart: a first post in five calls

    Everything below is one curl each. The full bodies, every option and every error are on the pages above.

    1. 1Create an API key

      Under Settings → API keys, on any plan. Pick the scopes it needs — publishing a post needs posts:publish and uploading a file media:write — and send it as a bearer token on every call.

      curl https://preflight.social/v1/oauth/accounts \
        -H "Authorization: Bearer sk_live_…"
      
      → 200 { "data": [ { "id": "b4b5…", "platform": "youtube", "displayName": "Weekly Mix", "timezone": "Europe/Prague" }, … ] }
    2. 2Upload the file

      Hand over a public URL and the server fetches it, or declare the size and type and PUT the bytes to the signed link it returns. Poll GET /v1/media/{id} until status is ready.

      curl https://preflight.social/v1/media \
        -H "Authorization: Bearer sk_live_…" \
        -H "Content-Type: application/json" \
        -d '{ "kind": "video", "sourceUrl": "https://your-generator.example/render/abc.mp4",
              "filename": "weekly-mix.mp4", "durationSeconds": 3600, "width": 1920, "height": 1080 }'
      
      → 202 { "id": "9f3c…", "status": "ingesting", … }
    3. 3Check it against every network

      POST /v1/platforms/validate takes the same body as creating a post and returns the issues each network would raise — a caption too long for X, a video TikTok refuses — without creating anything. Errors block publishing; warnings do not.

      curl https://preflight.social/v1/platforms/validate \
        -H "Authorization: Bearer sk_live_…" \
        -H "Content-Type: application/json" \
        -d '{ "content": "Weekly Mix #14 is up.", "mediaIds": ["9f3c…"],
              "targets": [ { "platform": "youtube", "options": { "title": "Weekly Mix #14" } },
                           { "platform": "facebook" } ] }'
      
      → 200 { "ok": true, "issues": [] }
    4. 4Create the post

      One call, as many networks as you name, with an Idempotency-Key so a retried request cannot publish twice. scheduledAt schedules it; publishNow sends it straight out; queue takes each channel's next free slot.

      curl https://preflight.social/v1/posts \
        -H "Authorization: Bearer sk_live_…" \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: 7d0c2c9e-…" \
        -d '{ "content": "Weekly Mix #14 is up.", "mediaIds": ["9f3c…"],
              "scheduledAt": "2026-10-12T09:00:00Z",
              "targets": [ { "platform": "youtube", "options": { "title": "Weekly Mix #14", "privacy": "public" } },
                           { "platform": "facebook" } ] }'
      
      → 201 { "id": "a1c0…", "status": "scheduled", "targets": [ … ], "issues": [] }
    5. 5Read what happened

      A created post is not a published post: the call returns as soon as the work is queued. Read targets[].status, remoteUrl and metrics on the post, or subscribe a webhook to post.published and post.failed. account.needs_reconnect says when a channel has to be connected again.

      curl https://preflight.social/v1/posts/a1c0… \
        -H "Authorization: Bearer sk_live_…"
      
      → 200 { "id": "a1c0…", "status": "done",
              "targets": [ { "platform": "youtube", "status": "published",
                             "remoteUrl": "https://youtu.be/…", "metrics": { "views": 1200, … } }, … ] }

    Authentication

    Every request carries an API key as a bearer token. A key belongs to one workspace, is shown once, and carries scopes — posts:read, posts:write, posts:publish, media:read, media:write, webhooks:read, webhooks:write or *. Call it from a server, never from a browser. GET /v1/platforms, the capability sheet, needs no key.

    Authentication: API keys and scopes →

    Files

    A file is uploaded first and named in a post by its id: hand over a URL the server fetches, or PUT the bytes to signed links — one for a file up to 16 MB, 16 MB parts above that. Send the duration and dimensions of a video so a network's refusal is predicted before the upload. Limits by plan, how long files are kept, Google Drive, and the media object.

    Media uploads →

    Creating a post

    POST /v1/posts with content, mediaIds and a list of targets; when it goes out is scheduledAt, scheduledLocal, queue, publishNow or saveAsDraft. Several accounts on one network, a different caption per network, carousels and alt text, threads, AI captions and the dry run.

    Posts: create, schedule, read, change and delete →

    Per-network options

    What goes in a target's options: YouTube's title, privacy, tags, thumbnail, playlist, subtitles and native scheduling; TikTok's privacy level and photo posts; Instagram and Facebook placements, reels and first comments; Pinterest boards; Threads, Mastodon, Telegram, Discord, X, LinkedIn, Bluesky and Tumblr.

    Per-network options →

    Reading posts

    GET /v1/posts pages through every post with its per-network status, link and figures; PATCH changes a post that has not started and DELETE removes it here. A created post is not a published post — read targets[].status to find out what happened.

    Reading and changing posts →

    Endpoints

    Every address, grouped by what it touches: posts and one network's copy of a post (metrics, comments, editing and deleting the live post) on the posts page; files on the media page; webhooks on theirs. The whole list as a schema is openapi.json.

    Webhooks

    Subscribe an https address to post.published (once per target, when it is live), post.failed (once retrying has stopped) and account.needs_reconnect (once when a channel stops working). Each delivery is signed with HMAC-SHA256 over the timestamp and the raw body in x-preflight-signature-v2, and retried five times unless answered with a 2xx within 10 seconds. POST /v1/workspace/webhooks/{id}/test sends a signed sample on demand.

    Webhooks →

    MCP server

    The same workspace is available to AI assistants over the Model Context Protocol at https://preflight.social/mcp. Claude and other clients connect with the address alone and sign in through OAuth; anything else can send an API key as a bearer token. A key sees only the tools its scopes allow. How to connect Claude, ChatGPT, Cursor and Claude Code, and every tool with what it does, is on the MCP server page.

    Reading (posts:read): list_channels, list_posts (filters and a cursor), get_post, validate_post, next_queue_times, post_performance, post_insights, analytics_summary, audience, list_comments, list_pinterest_boards, list_youtube_playlists, tiktok_creator_info and get_usage (the plan and what is left of it this month). Files: create_media_upload, complete_media_upload (media:write) and get_media (media:read).

    Drafts (posts:write): draft_post, update_post on a draft, and delete_post. Everything that decides what goes out or what is live also needs posts:publish: create_post, update_post on a scheduled post or to give a draft a time, retry_post, edit_published_post, delete_published_post, act_on_comment, create_youtube_playlist and create_pinterest_board.

    create_post needs an idempotencyKey, and a reply through act_on_comment one too. Publishing at once needs confirmPublishNow; deleting a post or taking it down from a network needs confirmDelete. A TikTok post that is not a draft needs tiktok.privacy. Settings a network does not take come back as warnings rather than being dropped silently, and a refusal for a plan limit keeps its limit and upgradeTo.

    MCP server: connect and every tool →

    Errors and limits

    Failures return a stable code and a message meant to be read by a person; a 402 carries the plan's limit and the plan that would allow it in upgradeTo. Requests are limited to 600 a minute per address, with tighter limits per key on creating files, AI, account lookups, webhooks and MCP.

    A created post is not a published post. POST /v1/posts returns as soon as the work is queued. Read targets[].status, or subscribe to a webhook, to find out what actually happened.

    Errors and rate limits →

    Questions: info@preflight.social. The same API as a schema: openapi.json (OpenAPI 3.1).

    Write it once. Let the rules be our problem.

    Preflight checks every post against each network's rules before it leaves, then publishes it to twelve networks. Free plan, no card.