whoami
Check this connection
[read-only] Returns who this connection (an OAuth sign-in or an API key) belongs to: userId, the account's current plan tier, and brandId/brandName when the connection is bound to one brand (null = account-wide). Call this first to learn what the connection can reach.
(Wraps GET /api/v2/ping.)
Takes no parameters.
[read-only] Lists the brands this connection can file content into and publish through. Use an `id` as `brandId` on create_content, publish, queues and analytics tools; "personal" means the account owner's personal accounts. A connection bound to one brand sees only that brand (and personal is null).
(Wraps GET /api/v2/brands.)
Takes no parameters.
list_connected_accounts
List connected social accounts
[read-only] The social accounts a brand can post to — the same set publish would use for that `brandId`: platform, handle, display name, health (healthy, needs_reauth, disconnected) and whether flayr can post there right now (`postable`; Reddit is paused because Reddit closed its API to third-party apps). Call this before publish to pick platforms, and tell the user to reconnect any account that is not healthy at flayr.social/connect.
(Wraps GET /api/v2/accounts.)
ParametersbrandId
list_youtube_playlists
List YouTube playlists
[read-only] Playlists of the YouTube channel(s) a brand publishes to. Use a playlist id as `youtube.playlistId` on create_content or publish.
(Wraps GET /api/v2/youtube/playlists.)
ParametersbrandId
list_pinterest_boards
List Pinterest boards
[read-only] Boards of the Pinterest account(s) a brand publishes to. Use a board id as `pinterest.boardId` on publish. A board is required; the one you pick is remembered as the brand's default for next time, and a publish that uses the default says so (defaultBoardApplied).
(Wraps GET /api/v2/pinterest/boards.)
ParametersbrandId
upload_media
Get a media upload URL
[writes flayr data · `write` scope] Step 1 of attaching a LOCAL file (video or image) to a draft. Returns a single-use, short-lived `uploadUrl`. Step 2: POST the raw file bytes to it with the file's Content-Type, e.g. `curl -X POST -H "Content-Type: video/mp4" --data-binary @clip.mp4 "<uploadUrl>"` — the JSON response has a `storageId`. Step 3: pass it as `videoStorageId` or `imageStorageId` on create_content or update_content. For media already on the web, skip this and pass `videoUrl` / `imageUrl` instead. Returns 413 when the account's storage is full.
(Wraps POST /api/v2/media/upload-url.)
Takes no parameters.
[read-only] Lists content items (drafts), newest first. Each carries `postState` ("posted" / "scheduled" / null) and `posts[]` (up to 5): where it went out, per platform, with status, live URL and failure reason.
(Wraps GET /api/v2/content.)
ParameterslimitbrandIdincludeArchived
get_content
Get a content item and its post status
[read-only] One content item plus its live posting status: `postState` and `posts[]` (up to 20, newest first) with each post's status (scheduled · publishing · published · partial · failed · review_held · held), scheduledAt, and per-platform status, live URL and error. The item's own `status` stays "draft" after publishing — read `posts` to know whether it is live. Use this to confirm a publish landed.
(Wraps GET /api/v2/content/{id}.)
Parametersidrequired
create_content
Create a draft
[writes flayr data · `write` scope] Creates a DRAFT content item. Nothing is posted. Give `text` plus optional media (`videoUrl` / `imageUrl`, or `videoStorageId` / `imageStorageId` from upload_media), target `platforms`, `brandId` (from list_brands; omitted → the brand last active in the dashboard), per-platform captions (`platformText`), creator `credits`, `labels` and YouTube options. Returns the new `id` — pass it as `contentId` to publish or add_queue_items.
(Wraps POST /api/v2/content.)
ParameterstextlabelsbrandIdcreditsyoutubeimageUrlvideoUrlplatformscontentTypesourceLabelplatformTextimageStorageId+3 more
update_content
Edit a draft
[writes flayr data · `write` scope] Edits a draft in place; only the fields you pass change. Any media field replaces the draft's media as a set (keeps the id, so queue items still point at it). Refused (409) for archived or already-published items, and a media swap is refused once a queue booked it. `labels`, `opus` and `videoWatermarked` may be changed on published items too. A video draft's custom thumbnail/cover: `thumbnailStorageId` (an image from upload_media) or `thumbnailUrl`, or `thumbnailFrameOffsetMs` for a frame of the video; `thumbnailUrl: null` clears it. It is sent to YouTube (long-form), Facebook, LinkedIn, Instagram Reels, TikTok and Pinterest at publish.
(Wraps PATCH /api/v2/content/{id}.)
ParametersidrequiredopustexttitleimageslabelscreditsyoutubeimageUrlvideoUrlplatformsplatformText+6 more
delete_content
Delete a content item
[DESTRUCTIVE] DESTRUCTIVE: permanently deletes a content item from the library. Posts already live on a platform are not removed. Needs the `write` scope. Ask the user before calling.
(Wraps DELETE /api/v2/content/{id}.)
Parametersidrequired
combine_clips
Combine clips into a finished video
[writes flayr data · `write` scope] Extends a video draft (`id`) with more clips and image cards into ONE finished video, rendered by flayr and saved as a NEW linked draft (same brand, caption copied, credits merged, labelled variant:combined). Nothing is posted and the original is unchanged. Use it to show a finished print with painting/assembly clips before or after it. `segments` (2-8, in order) must include the video itself as { kind: "clip", contentId: id }; clips are Library videos (contentId) or uploads (storageId from upload_media) with inMs/outMs (or durationMs = the clip's full length); cards are images (contentId of a Library image or video thumbnail, or imageStorageId) with durationMs and optional text. `transition` cut|crossfade, `audio` keep|mute_extras|main_across. Max 60 min by default (long-form: cold open + trimmed recording + bumpers). Costs 25 Embers per started minute of output for the first 3 min, then 5 per further started minute by default (refunded on failure). Returns a jobId — poll get_job_status.
(Wraps POST /api/v2/content/{id}/combine.)
ParametersidrequiredaudiosegmentsrequiredtransitioncrossfadeMs
get_job_status
Get a render/generation job status
[read-only] Status of an async job (combine_clips, or an API video generation): pending, processing, completed or failed. For a combine, `resultContentId` is the new draft once completed; a failed combine is refunded automatically. Poll every 10-30 s — renders take about a minute.
(Wraps GET /api/v2/generate/status/{jobId}.)
ParametersjobIdrequired
publish
Publish or schedule a post (goes live)
[PUBLISHES to social accounts · `publish` scope] PUBLISHES TO REAL SOCIAL ACCOUNTS. Posts now, or at `scheduledAt` (ISO-8601, future). Prefer `contentId` (a draft from create_content: its text and media become the post) over inline `content`. The post goes out through the target brand's own connected accounts. Always pass an `idempotencyKey` so a retry never double-posts. The same safety gates as the dashboard apply: platform readiness/media rules are checked up front (422); if the brand requires scheduled-post review nothing is sent and you get { status: "pending-review", reviewId }; a missing required creator credit in a push-down brand is refused (422 attribution_missing) or held; the "Posted with flayr.social" caption watermark and video watermark follow the plan (409 watermark_pending means the watermarked video is still rendering — retry after retryAfterSeconds). `facebook: { contentType: "reel" }` posts a Facebook Reel (exactly one video), `"story"` a Story, `"feed"` a regular post; `instagram.contentType` works the same; omitted → the draft's saved post type. `pinterest: { boardId, title, link }` picks the board (ids from list_pinterest_boards; required: omitted → the brand's last-picked board for that account, flagged as defaultBoardApplied, else 422 pinterest_board_required), pin title and destination link. `requiresApproval: true` files it for human approval instead of posting. A draft that is already scheduled on one of the platforms is refused (409 already_booked): change the booked post instead, or pass `allowDuplicateBooking: true` only when the user asked to post it again. Needs the `publish` scope (or admin) and the Pro plan or higher; each publish counts against the plan's direct posts for the month. Confirm with the user before posting now. Then call get_content to see the live URLs.
(Wraps POST /api/v2/publish.)
ParametersmemopolllabelsbrandIdcontentreshareyoutubefacebookimageUrlvideoUrlcontentIdinstagram+13 more
get_post
Get a scheduled post's first comments
[read-only] A post (`id` = convexPostId from publish, or posts[].id from get_content): status, slot, platforms, and while it is scheduled the first comment each platform will publish and whether it can be changed now.
(Wraps GET /api/v2/posts/{id}.)
Parametersidrequired
update_scheduled_post
Edit a scheduled post (caption, first comments, platforms)
[PUBLISHES to social accounts · `publish` scope] CHANGES WHAT A SCHEDULED POST WILL PUBLISH on real accounts, keeping its slot, accounts, options, labels and draft. `firstComment` sets per-platform first comments (linkedin, facebook, instagram, threads, youtube; null removes; X/Bluesky have none); `text` / `platformText` change the caption; `applyAutoFirstComment: true` adds the brand's auto first comment where none is set; `removePlatforms` drops platforms from this booking (e.g. a duplicate TikTok). Updated in place when the platform scheduler allows it, otherwise re-booked at the same slot (status "rebooked", new postId). Refused within 5 minutes of the slot. Repeating the same edit returns "unchanged". Needs the `publish` scope (or admin) and the Pro plan or higher; each publish counts against the plan's direct posts for the month. Confirm the new text with the user first.
(Wraps PATCH /api/v2/posts/{id}.)
ParametersidrequiredtextfirstCommentplatformTextremovePlatformsapplyAutoFirstComment
cancel_scheduled_post
Cancel a scheduled post
[PUBLISHES to social accounts · `publish` scope] Takes a SCHEDULED post off the platform schedule and marks it cancelled (the draft stays, so it can be published again). Safe to repeat. Refused within 5 minutes of the slot, and when the platform will not remove it (some YouTube uploads). Needs the `publish` scope (or admin) and the Pro plan or higher; each publish counts against the plan's direct posts for the month. Confirm with the user first.
(Wraps DELETE /api/v2/posts/{id}.)
Parametersidrequired
list_reviews
List scheduled-post reviews
[read-only] Scheduled posts waiting for (or given) a human sign-off, oldest slot first. `canApprove` tells whether THIS connection may approve each one.
(Wraps GET /api/v2/reviews.)
ParametersstatuswindowbrandId
decide_review
Approve or hold a scheduled post
[PUBLISHES to social accounts · `publish` scope] decision "approve" sends a held post to the platform for its slot (GOES LIVE) — needs the explicit `review` scope (admin does not include it) and never works on a post the same connection created. decision "hold" takes a scheduled post off the platform schedule — needs `review` or `publish`. Agency plan. `id` is a reviewId or a convexPostId.
(Wraps POST /api/v2/reviews/{id}.)
ParametersidrequirednotedecisionrequiredpastSlotcollabSide
list_approvals
List content waiting for your approval
[read-only] Content held for approval that waits on the account owner's decision, newest first: posts filed with publish `requiresApproval: true`, drafts submitted for review, and workflow runs parked at an approval gate. `kind` is "manual" or "workflow". Needs the explicit `approve` scope (admin does not include it). Agency plan.
(Wraps GET /api/v2/approvals.)
ParametersstatusbrandId
decide_approval
Approve or reject held content
[PUBLISHES to social accounts · `publish` scope] Same rules as the dashboard: only the assigned approver may decide. On a "workflow" approval, decision "approve" resumes the parked run, whose next steps may publish (GOES LIVE); "reject" cancels it. On a "manual" approval, "approve" only marks the content approved — nothing is posted until you call publish with its contentId — and "reject" returns it to draft. Needs the explicit `approve` scope (admin does not include it). Agency plan. Repeating the same decision is safe (alreadyDecided: true). Confirm with the user before approving a workflow approval.
(Wraps POST /api/v2/approvals/{id}.)
Parametersidrequirednotedecisionrequired
list_queues
List posting queues
[read-only] Posting (drip) queues of one brand. A queue posts one item every cadenceHours at timeOfDay in its timezone, keeping `lookahead` items booked at the platform.
(Wraps GET /api/v2/queues.)
ParametersbrandId
get_queue
Get a posting queue
[read-only] A queue and its items in order, with each item's state (queued · publishing · scheduled · posted · failed · skipped), booked or projected slot, and attribution status.
(Wraps GET /api/v2/queues/{id}.)
ParametersidrequiredbrandId
create_queue
Create a posting queue
[PUBLISHES to social accounts · `publish` scope] Creates a posting queue. Once items are added it BOOKS REAL POSTS on the given platforms on its cadence (default daily 19:00 America/Chicago, 2 booked ahead). Use `startsAt` to hold everything until a date. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps POST /api/v2/queues.)
ParametersnamerequiredbrandIdyoutubestartsAttimezonelookaheadplatformsrequiredtimeOfDaycadenceHours
update_queue
Update, pause or resume a queue
[PUBLISHES to social accounts · `publish` scope] `status: "paused"` stops new bookings (already-booked posts stay booked); "active" resumes. Other fields (platforms, cadenceHours, timeOfDay, lookahead, startsAt, name) apply from the next booking. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps PATCH /api/v2/queues/{id}.)
ParametersidrequirednamestatusbrandIdstartsAtlookaheadplatformstimeOfDaycadenceHours
delete_queue
Delete a posting queue
[DESTRUCTIVE] DESTRUCTIVE: deletes the queue (its drafts stay in the library). With posts booked at the platform it is refused (409) unless `unschedule: true`, which first deletes those platform posts. Needs the `publish` scope (or admin) on the Agency plan or higher. Ask the user before calling.
(Wraps DELETE /api/v2/queues/{id}.)
ParametersidrequiredbrandIdunschedule
add_queue_items
Add drafts to a queue
[PUBLISHES to social accounts · `publish` scope] Appends drafts (content ids) to the end of a queue — they will be BOOKED AND POSTED on the queue's schedule. Use `contentIds`, or `items` for a per-item YouTube title or platforms. Drafts of another brand, duplicates, archived drafts and drafts without a video for a video platform come back in `rejected`. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps POST /api/v2/queues/{id}/items.)
ParametersidrequireditemsbrandIdcontentIds
queue_item_action
Skip, retry, unschedule, retitle or move a queue item
[PUBLISHES to social accounts · `publish` scope] `action`: "skip" passes over a queued item; "retry" puts a failed/skipped item back in line; "unschedule" deletes a booked platform post and puts the item back in line. Also `youtubeTitle`, per-item `platforms`, and `position` (0-based among waiting items). Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps PATCH /api/v2/queues/{id}/items/{itemId}.)
ParametersidrequiredactionitemIdrequiredbrandIdpositionplatformsyoutubeTitle
remove_queue_item
Remove an item from a queue
[DESTRUCTIVE] DESTRUCTIVE: removes an item (the draft stays in the library). A booked (`scheduled`) item is refused (409) unless `unschedule: true`, which deletes its platform post first. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps DELETE /api/v2/queues/{id}/items/{itemId}.)
ParametersidrequireditemIdrequiredbrandIdunschedule
reorder_queue_items
Reorder waiting queue items
[PUBLISHES to social accounts · `publish` scope] Send every waiting (queued) item id exactly once, in the new order. Booked, posted, failed and skipped items keep their places. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps POST /api/v2/queues/{id}/items/reorder.)
ParametersidrequiredbrandIditemIdsrequired
get_analytics
Analytics overview
[read-only] Engagement overview for the last 7, 30 or 90 days. Pro plan or higher.
(Wraps GET /api/v2/analytics.)
Parametersrange
query_insights
Run a custom insight query
[read-only] Read-only analytics query: one `metric` aggregated (median by default) over posts, broken down by up to a few dimensions (platform, weekday, hour, brand, campaign, postType, label:<key>, arm), at a fixed post age `window` (48h / 7d / 30d / lifetime) so posts are compared fairly. Every row carries its sample size n; platforms that do not report the metric are listed in `availability`, never counted as 0. Pro plan or higher.
(Wraps POST /api/v2/insights/query.)
ParametersdateToformatmetricwindowbrandIdfiltersformuladateFromintervalbreakdownaggregationtzOffsetMinutes
compare_labels
Compare posts by a label (A/B)
[read-only] Splits every labelled post by the values of one label key (e.g. key "cut" → cut:opus vs cut:raw) and reports medians with n per group at the 48h / 7d snapshot. Descriptive, not a significance test. Pro plan or higher.
(Wraps GET /api/v2/analytics/experiments.)
ParameterskeymetricwindowbrandIdinclude
list_experiments
List experiments
[read-only] A/B experiments. Content joins one by carrying the label `experiment:<id>` plus `<compareKey>:<variant>`.
(Wraps GET /api/v2/experiments.)
ParametersbrandIdallBrands
get_experiment
Get an experiment
[read-only] One experiment: hypothesis, variants, compareKey, window and status.
(Wraps GET /api/v2/experiments/{id}.)
Parametersidrequired
create_experiment
Create an experiment
[writes flayr data · `write` scope] Creates an A/B experiment. Then tag drafts with its `label` (experiment:<id>) and `<compareKey>:<variant>` via update_content `labels`, publish them, and read get_experiment_results.
(Wraps POST /api/v2/experiments.)
ParametersnamerequiredendAtnotesstatusbrandIdstartAtvariantscompareKeyhypothesisprimaryMetric
update_experiment
Update an experiment
[writes flayr data · `write` scope] Changes an experiment's fields (e.g. status "running" / "done", endAt, notes). Only the fields you pass change.
(Wraps PATCH /api/v2/experiments/{id}.)
ParametersidrequirednameendAtnotesstatusstartAtvariantscompareKeyhypothesisprimaryMetric
delete_experiment
Delete an experiment
[DESTRUCTIVE] DESTRUCTIVE: deletes the experiment record (content keeps its labels). Ask the user before calling.
(Wraps DELETE /api/v2/experiments/{id}.)
Parametersidrequired
get_experiment_results
Experiment results
[read-only] The experiment's posts split by its compareKey, with the leading variant by its primary metric and n per arm. Small samples: read medians with their n.
(Wraps GET /api/v2/experiments/{id}/results.)
Parametersidrequiredwindowinclude
list_text_templates
List formats (text templates)
[read-only] Reusable text shapes per brand: sections, length targets, hook patterns, CTA slots and guardrails. Use one to shape a draft's text before create_content.
(Wraps GET /api/v2/text-templates.)
ParametersbrandIdincludeArchived
get_text_template
Get a format
[read-only] One format (text template) with its full structure and example.
(Wraps GET /api/v2/text-templates/{id}.)
Parametersidrequired
create_text_template
Create a format
[writes flayr data · `write` scope] Creates a reusable format (text template) in a brand.
(Wraps POST /api/v2/text-templates.)
ParametersnamerequiredformatrequiredlengthrequiredsourceuseForbrandIdexamplectaSlotsplatformsectionsguardrailsmaxHashtags+2 more
update_text_template
Update a format
[writes flayr data · `write` scope] Changes a format; only the fields you pass change. `archived: true` hides it.
(Wraps PATCH /api/v2/text-templates/{id}.)
ParametersidrequirednameformatlengthsourceuseForexamplearchivedctaSlotsplatformsectionsguardrails+3 more
delete_text_template
Delete a format
[DESTRUCTIVE] DESTRUCTIVE: deletes a format (drafts started from it keep their text). Ask the user before calling.
(Wraps DELETE /api/v2/text-templates/{id}.)
Parametersidrequired
list_attribution_rules
List creator-credit rules
[read-only] The brand's "posts like this must carry a credit" rules and its mode (flag / push_down). In push_down mode publish refuses or holds a post missing a required credit — add `credits` with update_content first.
(Wraps GET /api/v2/attribution-rules.)
ParametersbrandId
list_share_rules
List share rules
[read-only] The brand's share rules: "when <account> publishes, reshare to <accounts> (or comment on it) after N minutes with text T", each with its approval mode.
(Wraps GET /api/v2/automations/reshare-rules.)
ParametersbrandId
create_share_rule
Create a share rule
[PUBLISHES to social accounts · `publish` scope] Creates a rule that reshares (or comments on) every new post of the source accounts. Defaults to needs_approval: each fire waits for approve in act_on_publish_action. Only LinkedIn (native reshare) and X (quote own / retweet) really repost; other platforms get a link share. Confirm with the user first. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps POST /api/v2/automations/reshare-rules.)
ParameterskindnamerequiredbrandIdenabledtargetsapprovaltemplatesourceLabeldelayMinutessourceAccountIdsrequired
list_publish_actions
List comments after publish and reshares
[read-only] Pending, sent and failed comments-after-publish and reshares of the brand (filter by `status`, e.g. needs_approval or failed), with what each waits on.
(Wraps GET /api/v2/automations/actions.)
ParametersstatusbrandId
act_on_publish_action
Approve, reject, retry or cancel a reshare / comment
[PUBLISHES to social accounts · `publish` scope] approve lets a rule's reshare / comment go out at its time; retry re-sends a failed one (same idempotency key); reject / cancel stop it. Confirm with the user before approving. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps POST /api/v2/automations/actions/{id}.)
Parametersidrequiredoprequired
[read-only] The brand's frames (borders, overlays, lower thirds, corner tags, quote-card frames, ribbons) plus the built-in flayr frames in the brand colors. Each has a `ref` to pass to apply_frame.
(Wraps GET /api/v2/frames.)
ParameterskindaspectincludeArchived
[read-only] One library frame with its sizes and content areas.
(Wraps GET /api/v2/frames/{id}.)
Parametersidrequired
create_frame
Add a frame to the library
[writes flayr data · `write` scope] Copy a flayr frame into the brand library (`defaultKey`, optional colors/texts), or add uploaded PNG/SVG art (upload_media first; `variants` of storageIds, the content area is detected from transparency).
(Wraps POST /api/v2/frames.)
ParameterskindnametagstextsrightsvariantsdefaultKeypaletteOverride
apply_frame
Frame an image
[writes flayr data · `write` scope] Draws a frame over an image and returns a new image URL (the original is untouched). Use the URL in create_content, or pass contentId + platform so that post publishes the framed image on that platform. Free by default; the same inputs again are free.
(Wraps POST /api/v2/frames/apply.)
ParametersfitzoomtextsaspectoffsetXoffsetYframeRefrequiredplatformcontentIdsourceUrlrequiredslideIndexpaletteOverride
get_frame_settings
Get auto-frame settings
[read-only] Which frame each platform's images get automatically when a post is published or scheduled.
(Wraps GET /api/v2/frames/settings.)
Takes no parameters.
update_frame_settings
Set auto-frame for a platform
[writes flayr data · `write` scope] Frame every image posted to a platform automatically (frameRef), or stop (frameRef null). Applies to posts published or scheduled from now on. Confirm with the user first.
(Wraps POST /api/v2/frames/settings.)
ParametersaspectframeRefrequiredplatformrequired