# flayr (remote · flayr.social)

Create, schedule and publish social posts to 13 platforms for your brands.

- Trust score: 78/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-04

## Components

- remote · `flayr.social`: 78/100 (this document), [markdown](https://verifymcp.io/servers/social-flayr-flayr/api-mcp.md), [page](https://verifymcp.io/servers/social-flayr-flayr/api-mcp)

## Channel facts

- Endpoint: `https://flayr.social/api/mcp`
- Transports: `streamable-http`
- Auth: `required`
- Version: `1.2.0`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-04.

- **Endpoint Security**: 97/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - Authorisation is enforced on tool calls, advertised via RFC 9728 protected-resource metadata. Discovery is public, which costs nothing: no tool can be invoked without a token.
  - HTTPS is enforced; there's no plaintext access path.
  - The HSTS (Strict-Transport-Security) header is present.
  - DNSSEC check failed: this domain isn't protected by DNSSEC.
  - The authorisation server supports Client ID Metadata Documents, the current MCP client-registration mechanism.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 54/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 21607 tokens (~211/item across 102 items; 102 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 20/100
  - Stability observed for 6 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 85/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 54% of tool parameters carry a description.
- **Tool Safety**: 97/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 8 of 9 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "list_publish_actions" implies "publish" and declares readOnlyHint instead, contradicting what its own name says it does.
  - An AI judge read all 103 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the flayr MCP server?

flayr is a hosted endpoint at https://flayr.social/api/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add --transport http social-flayr-flayr 'https://flayr.social/api/mcp'
```

### Cursor

```json
{
  "mcpServers": {
    "social-flayr-flayr": {
      "url": "https://flayr.social/api/mcp"
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "social-flayr-flayr": {
      "type": "http",
      "url": "https://flayr.social/api/mcp"
    }
  }
}
```

### Codex

```toml
[mcp_servers.social-flayr-flayr]
url = "https://flayr.social/api/mcp"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "social-flayr-flayr": {
      "type": "remote",
      "url": "https://flayr.social/api/mcp",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add social-flayr-flayr --url 'https://flayr.social/api/mcp' --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  social-flayr-flayr:
    url: "https://flayr.social/api/mcp"
```

### Netclaw

```json
{
  "McpServers": {
    "social-flayr-flayr": {
      "Transport": "http",
      "Url": "https://flayr.social/api/mcp"
    }
  }
}
```

### Vellum

```bash
assistant mcp add social-flayr-flayr -t streamable-http -u 'https://flayr.social/api/mcp'
```

### Other

```json
{
  "mcpServers": {
    "social-flayr-flayr": {
      "type": "http",
      "url": "https://flayr.social/api/mcp"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-04 (score 78, +1)

- [security] The server rewrote its instructions, which are the text every model session reads
- [security] New tool “decide_campaign_review_section”, which the server declares destructive
- [security] New tool “delete_growth_trigger”, which the server declares destructive
- [security] New tool “update_growth_trigger”, which the server declares destructive
- [security] Tool “decide_approval” rewrote its description, which is the text the model reads
- [security] Tool “get_job_status” rewrote its description, which is the text the model reads
- [security] Tool “list_approvals” rewrote its description, which is the text the model reads
- [security] Tool “list_brands” rewrote its description, which is the text the model reads
- [security] Tool “publish” rewrote its description, which is the text the model reads
- [security] Tool “update_content” rewrote its description, which is the text the model reads
- [functional improvement] Schema quality: 236 → 211
- [functional] New tool “account_refresh”
- [functional] New tool “ads_accounts_list”
- [functional] New tool “ads_boost_candidates”
- [functional] New tool “ads_campaign_get”
- [functional] New tool “ads_campaign_pause”
- [functional] New tool “ads_campaigns_list”
- [functional] New tool “ads_insights”
- [functional] New tool “ads_offer_update”
- [functional] New tool “ads_offers_list”
- [functional] New tool “ads_readiness”
- [functional] New tool “bodies_map”
- [functional] New tool “brand_business_details_get”
- [functional] New tool “brand_business_details_update”
- [functional] New tool “brand_profile_get”
- [functional] New tool “brand_profile_update”
- [functional] New tool “claims_check”
- [functional] New tool “close_experiment_round”
- [functional] New tool “create_growth_trigger”
- [functional] New tool “cta_check”
- [functional] New tool “cta_settings_get”
- [functional] New tool “cta_settings_update”
- [functional] New tool “ctas_create”
- [functional] New tool “ctas_generate”
- [functional] New tool “ctas_list”
- [functional] New tool “ctas_update”
- [functional] New tool “get_campaign_review”
- [functional] New tool “get_experiment_readout”
- [functional] New tool “get_experiment_rounds”
- [functional] New tool “get_growth_trigger”
- [functional] New tool “get_growth_trigger_stats”
- [functional] New tool “group_campaign_review”
- [functional] New tool “hooks_generate”
- [functional] New tool “hooks_list”
- [functional] New tool “hooks_update”
- [functional] New tool “labels_bulk_edit”
- [functional] New tool “list_growth_triggers”
- [functional] New tool “offers_capture”
- [functional] New tool “offers_list”
- [functional] New tool “offers_update”
- [functional] New tool “propose_repeat_winner”
- [functional] New tool “render_motion_ad”
- [functional] New tool “start_experiment_round”
- [cosmetic] “publish” added an optional parameter “strictMedia”
- [cosmetic] “update_content” added an optional parameter “aiGenerated”
- [cosmetic] “update_content” added an optional parameter “library”
- [cosmetic] “decide_approval” reworded the description of “note”
- [cosmetic] “publish” reworded the description of “instagram”
- [cosmetic] “query_insights” reworded the description of “breakdown”

### 2026-11-02 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 10 to 13. That category is still filling its 30-day observation window: 3 days of observed history at the previous scan, 4 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-31 (score 76, +1)

No change was recorded against any check on this day. Stability & Change Management went from 3 to 7. That category is still filling its 30-day observation window: 1 days of observed history at the previous scan, 2 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-30 (score 75, 0)

- [security] The server rewrote its instructions, which are the text every model session reads
- [functional improvement] Stability: unverified → 0.03

### 2026-10-29 (score 75)

First indexed and scored.

## MCP tools (102)

### `whoami` (~81 tokens)

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.)

### `list_brands` (~139 tokens)

List brands

[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). Each brand has its `website` and a `setup` checklist (hasBrandKey, hasWebsite, hasCtaGoal, adsConnected): set a missing website with brand_profile_update; a brand API key is created by the owner in Settings, never by an agent.
(Wraps GET /api/v2/brands.)

### `brand_profile_get` (~123 tokens)

Get a brand profile

[read-only] A brand's profile (website, companyName, tagline, industry, audience, values, pillars, hashtag sets), its default voice, colors and fonts. `brandId` comes from list_brands; a brand-bound connection may omit it.
(Wraps GET /api/v2/brand/intelligence.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. A brand-bound key is pinned to its own brand (may be omitted; another brand → 403); an account-wide key must name one.

### `brand_profile_update` (~288 tokens)

Update a brand profile

[writes flayr data · `write` scope] Fills in a brand's profile — e.g. `{ brandId, profile: { website: "https://…" } }`. Changes only the fields you send; the voice, colors and fonts stay as they are unless you pass them. Use it to clear list_brands setup items such as hasWebsite. Owners, admins and editors only; every change is audited against this connection.
(Wraps PATCH /api/v2/brand/intelligence.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. A brand-bound key is pinned to its own brand (may be omitted; another brand → 403); an account-wide key must name one.
- `colors` (object): REPLACES the brand palette, the same as saving Brand → Overview → Brand Colors: the stored list is primary, secondary, then palette (duplicates dropped); primary and secondary are its top two. 6-digi…
- `fonts` (object): Font family names. A role left out keeps its stored font. At least one is required.
- `profile` (object): Only the fields sent are changed. Unknown fields are a 422.
- `voice` (object): The brand default voice. Created (as the default) when the brand has none.

### `list_connected_accounts` (~152 tokens)

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.)

Input parameters:

- `brandId` (string): An id from GET /api/v2/brands, or "personal". Omitted → the key's bound brand, else the owner's personal accounts.

### `account_refresh` (~141 tokens)

Refresh a connected account's profile

[writes flayr data · `write` scope] Re-reads a connected account's handle, display name and avatar from the network (use after the user renames the account) and returns the stored values. Changes nothing else: no connect, disconnect or post. If the status is not `updated`, tell the user to reconnect the account at the returned `reconnectUrl`.
(Wraps POST /api/v2/accounts/{id}/refresh.)

Input parameters:

- `brandId` (string): The brand the account is connected to, as for GET /api/v2/accounts.
- `id` (string, required): An account id from GET /api/v2/accounts.

### `list_youtube_playlists` (~96 tokens)

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.)

Input parameters:

- `brandId` (string): An id from GET /api/v2/brands, or "personal". Omitted → the key's bound brand, else the owner's personal accounts.

### `list_pinterest_boards` (~125 tokens)

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.)

Input parameters:

- `brandId` (string): An id from GET /api/v2/brands, or "personal". Omitted → the key's bound brand, else the owner's personal accounts.

### `upload_media` (~177 tokens)

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.)

### `list_content` (~128 tokens)

List content

[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.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Omitted: the key's brand (brand-bound key) or the owner's active brand.
- `includeArchived` (boolean)
- `limit` (integer)

### `get_content` (~126 tokens)

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}.)

Input parameters:

- `id` (string, required)

### `create_content` (~1124 tokens)

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.)

Input parameters:

- `brandId` (string): An id from GET /api/v2/brands, or "personal".
- `contentType` (string)
- `copyFromContentId` (string): COPY TO BRAND (staff and the Agency plan): the id of an existing item (GET /api/v2/content) to copy into `brandId` as a new draft. Images, video, slides, documents and thumbnails are copied into new…
- `credits` (array): Creator credits for work this post uses. Added to the published caption above the flayr footer, one line each: model → "Print your own: <url> (model by <author>)"; other kinds → "Credit: <title> by <…
- `imageStorageId` (string)
- `imageUrl` (string)
- `labels` (array): Normalized "key:value" labels (keys and values are lower-cased, spaces become "-"). Linked keys: campaign:<media campaign id>, experiment:<experiment id> (+ variant:<name> or the experiment's compare…
- `platformText` (object): Per-platform captions: platform id → the caption that platform gets instead of `text`, e.g. { "twitter": "short version" }. Each must fit its platform: X 280 (a link counts 23, an emoji 2), Bluesky 3…
- `platforms` (array)
- `referralFooter` (boolean): Caption watermark: appends "Posted with flayr.social — try it: flayr.social/r/<code>/<ch>" to each platform's text (<code> is your referral code — your custom one if you claimed it; <ch> names the pl…
- `sourceLabel` (string)
- `text` (string)
- `videoStorageId` (string)
- `videoUrl` (string)
- `youtube` (object): Stored on the draft and used when it is published (POST /api/v2/publish with contentId). YouTube upload options, applied to YouTube targets only. madeForKids is always sent to YouTube (default false)…

### `update_content` (~1296 tokens)

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`, `videoWatermarked`, `library` and `aiGenerated` may be changed on published items too. `aiGenerated` is the post's AI label: false for real footage, null back to automatic. 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}.)

Input parameters:

- `aiGenerated` (boolean|null): The post's "AI-generated" label toggle: false = real footage (no AI label), true = always label, null = automatic (on when flayr made the media). Metadata: allowed on published items. Removing the `s…
- `credits` (array|null): Creator credits for work this post uses. Added to the published caption above the flayr footer, one line each: model → "Print your own: <url> (model by <author>)"; other kinds → "Credit: <title> by <…
- `id` (string, required)
- `imageStorageId` (string): Uploaded image storageId: the video's thumbnail, or on its own the post's single image. Not with imageUrl.
- `imageUrl` (string): http(s) image URL: the video's thumbnail, or on its own the post's single image. Not with imageStorageId.
- `images` (array): Carousel image URLs. Cannot be combined with the other media fields.
- `labels` (array|null): Replaces the labels ([] or null clears). Allowed on published items too. Normalized "key:value" labels (keys and values are lower-cased, spaces become "-"). Linked keys: campaign:<media campaign id>,…
- `library` (object): The Library item's own title and description, searchable from the Library. null or "" clears one. Metadata: allowed on published items. Not the same as `title` (the YouTube video title).
- `opus` (object|null): Link this row to an Opus Clip clip (owner only; allowed on published rows) so an Opus project import skips it instead of creating a duplicate. null unlinks.
- `platformText` (object|null): Per-platform captions: platform id → the caption that platform gets instead of `text`, e.g. { "twitter": "short version" }. Each must fit its platform: X 280 (a link counts 23, an emoji 2), Bluesky 3…
- `platforms` (array)
- `text` (string)
- `thumbnailFrameOffsetMs` (integer): Use this frame of the video (ms from the start) as the cover. Instagram and TikTok get the offset; YouTube/Facebook/LinkedIn need an image, so pass the captured frame as thumbnailStorageId / thumbnai…
- `thumbnailStorageId` (string|null): Custom video thumbnail: an image storageId from POST /api/v2/media/upload-url. Not with thumbnailUrl. null clears.
- `thumbnailUrl` (string|null): Custom video thumbnail: an http(s) image URL. Not with thumbnailStorageId. null clears.
- `title` (string): Shorthand for youtube.title (the video title). No < or >.
- `videoStorageId` (string): storageId from uploading to POST /api/v2/media/upload-url's uploadUrl. Not with videoUrl. Another account's upload is refused (422).
- `videoUrl` (string): http(s) URL of the new video. Not with videoStorageId.
- `videoWatermarked` (object|null): The item's own video already has the "Posted by flayr.social" watermark: publish it as-is. null clears.
- `youtube` (object): Merged into the stored YouTube options. YouTube upload options, applied to YouTube targets only. madeForKids is always sent to YouTube (default false). A missing title is derived from the first sente…

### `labels_bulk_edit` (~309 tokens)

Add or remove labels on many items

[writes flayr data · `write` scope] Adds and/or removes labels on up to 100 content items in one call: each item becomes (labels − remove) + add. Published items are fine (labels are metadata). Each item succeeds or fails on its own; read `results`. Removing `source:ai-generated` does not turn a post's AI label off: with no explicit toggle it follows how the media was made, and the result carries a `warning`. For real footage also call update_content with `aiGenerated: false` on each item.
(Wraps POST /api/v2/content/labels.)

Input parameters:

- `add` (array): Labels to add. Normalized "key:value" labels (keys and values are lower-cased, spaces become "-"). Linked keys: campaign:<media campaign id>, experiment:<experiment id> (+ variant:<name> or the exper…
- `ids` (array, required): Content ids (GET /api/v2/content).
- `remove` (array): Labels to remove. Not also in `add`.

### `delete_content` (~69 tokens)

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}.)

Input parameters:

- `id` (string, required)

### `combine_clips` (~345 tokens)

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.)

Input parameters:

- `audio` (string)
- `crossfadeMs` (integer): Crossfade overlap per join.
- `id` (string, required): The video to extend.
- `segments` (array, required): In playback order. Must include this video ({ "kind": "clip", "contentId": "<id>" }).
- `transition` (string)

### `get_job_status` (~106 tokens)

Get a render/generation job status

[read-only] Status of an async job (combine_clips, render_motion_ad, or an API video generation): pending, processing, completed or failed. For a combine or a motion ad, `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}.)

Input parameters:

- `jobId` (string, required)

### `render_motion_ad` (~278 tokens)

Render a motion ad from a Library card

[writes flayr data · `write` scope] Turns one Library image (the card) into a 6-15 s motion ad: hook (push-in on the card), up to 3 body beats (pans with the text), then a CTA end card in the brand color with the CTA and the link host. 9:16 unless `aspect` is 1:1 or 16:9. The text is used exactly as given; it is not rewritten. Saves a new draft in the card's brand (labels format:motion-ad, source:template); nothing is posted and the card is unchanged. Free. Returns a jobId: poll get_job_status.
(Wraps POST /api/v2/video-studio/templates/motion-ad-from-card/render.)

Input parameters:

- `aspect` (string)
- `body` (array): Up to 3 beats are used; blanks are dropped.
- `brandColor` (string): #RRGGBB; defaults to the brand profile color.
- `contentId` (string, required): The card: a Library image.
- `cta` (string, required)
- `ctaUrl` (string, required): http(s) link; its host is shown on the end card.
- `hook` (string, required)
- `textColor` (string): #RRGGBB.

### `publish` (~2313 tokens)

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. `instagram.trialReel: { enabled: true, graduation: "manual" | "auto" }` posts a Reel to non-followers first (Instagram Trial Reel; omitted graduation → admin default; ignored with a warning when not a Reel). `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. T…

Input parameters:

- `aiGenerated` (boolean): Label the post as AI-generated / synthetic media: YouTube containsSyntheticMedia, Instagram isAiGenerated, TikTok videoMadeWithAi, X madeWithAi, and the disclosure line (default "#AIgenerated") on pl…
- `allowDuplicateBooking` (boolean): A contentId that is already scheduled (a live booking still ahead) on one of these platforms is refused with 409 { code: "already_booked", bookedAt, bookedPlatforms }. Pass true only to book the same…
- `autoFirstComment` (boolean): When the target brand has "Auto first comment" on (dashboard: Brand → First comment), LinkedIn, Instagram, Facebook, Threads and YouTube targets that have no first comment of their own get the brand'…
- `brandId` (string): An id from GET /api/v2/brands, or "personal". With contentId it must match the item's brand.
- `content` (string): Post text. Required unless contentId is given.
- `contentId` (string): An existing content item id (GET /api/v2/content/{id} access rules). Cannot be combined with imageUrl / videoUrl.
- `facebook` (object): Facebook post type. "reel" posts a Reel (exactly one video, no images; anything else is a 422 with code content_type_invalid), "story" a Page Story, "feed" a regular post. Omitted → the content item'…
- `firstComment` (object): Explicit first comments per platform, posted right after the post goes out. Always wins over the brand's auto first comment. Threads: posted as a reply (it counts toward Threads' 250 posts/24h). Face…
- `followUpComments` (array): Comments after publish, sent once each (Idempotency-Key) after the post is live on that platform and its tokens resolve: {{postUrl}}, {{postUrl:<platform>}}, {{parentPostUrl:<post or draft id>}}, {{y…
- `idempotencyKey` (string): Unique per API key. A retry with the same key never posts twice.
- `imageUrl` (string)
- `instagram` (object): Instagram post type, same rules: "reel" (exactly one video — a single video is posted as a Reel), "story", or "feed". Omitted → the content item's stored platformSpecificData.instagram.contentType. `…
- `labels` (array): Labels for this post on top of the draft's own (the draft wins for type/cut/variant). Normalized "key:value" labels (keys and values are lower-cased, spaces become "-"). Linked keys: campaign:<media…
- `mediaCampaignId` (string): Link to a media campaign; with a brand target it must belong to that brand.
- `memo` (object): "When it posts" memo: the people-steps after the post goes live (reply to a comment, share to a personal account, pin a link, add a first comment flayr can't). Saved on the draft (else the post). Whe…
- `onPublishTasks` (array): Shorthand for memo.tasks (appended after them). Response: `memoId`; a memo that could not be saved is a `warnings` entry, never a failed publish.
- `pinterest` (object): FFE-428: Pinterest pin options for every Pinterest target. Overrides the content item's stored platformSpecificData.pinterest field by field. A board is required. boardId omitted → the brand's last-p…
- `platforms` (array, required)
- `poll` (object): A native poll. Only on twitter (X), linkedin and discord, and with no media: any other platform or media is a 422 (code poll_invalid), never a plain post. X: 2-4 choices of at most 25 characters, 5 m…
- `referralFooter` (boolean): Caption watermark: appends "Posted with flayr.social — try it: flayr.social/r/<code>/<ch>" to each platform's text (<code> is your referral code — your custom one if you claimed it; <ch> names the pl…
- `requiresApproval` (boolean)
- `reshare` (object): Share/repost another post: a flayr post id (`postId`) or a pasted link (`url`). Per platform: linkedin a native reshare with your text on top (not with media or a poll); twitter a quote of your own X…
- `scheduledAt` (string): Must be in the future. Omitted → publish now.
- `strictMedia` (boolean): Refuse (422 media_fit) when the video does not fit a booked platform (length, shape, smallest frame, loudness) instead of posting with `warnings`.
- `videoUrl` (string)
- `youtube` (object): Overrides the content item's stored YouTube options field by field. YouTube upload options, applied to YouTube targets only. madeForKids is always sent to YouTube (default false). A missing title is…

### `get_post` (~111 tokens)

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}.)

Input parameters:

- `id` (string, required): The post: `convexPostId` from POST /api/v2/publish, or `posts[].id` from GET /api/v2/content/{id}.

### `update_scheduled_post` (~496 tokens)

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}.)

Input parameters:

- `applyAutoFirstComment` (boolean): Add the brand's auto first comment where the post has none.
- `firstComment` (object): The first comment the post publishes, per platform: a string sets it, null or "" removes it; platforms not named keep theirs. X and Bluesky have no first comment at the platform scheduler and are ref…
- `id` (string, required): The post: `convexPostId` from POST /api/v2/publish, or `posts[].id` from GET /api/v2/content/{id}.
- `platformText` (object|null): Per-platform captions, merged into the draft's: { "linkedin": "…" } sets one, null / "" clears it; null clears them all.
- `removePlatforms` (array): Platform ids to drop from this booking (e.g. a duplicate ["tiktok"]). Each must be on the post; dropping every platform is refused (use DELETE). The booking is rebuilt from the draft.
- `text` (string): The caption (every platform without its own).

### `cancel_scheduled_post` (~166 tokens)

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}.)

Input parameters:

- `id` (string, required): The post: `convexPostId` from POST /api/v2/publish, or `posts[].id` from GET /api/v2/content/{id}.

### `list_reviews` (~75 tokens)

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.)

Input parameters:

- `brandId` (string)
- `status` (string)
- `window` (string)

### `decide_review` (~262 tokens)

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}.)

Input parameters:

- `collabSide` (string): FFE-501 collab posts only: which brand this decision is for ("owner" = the shared character's brand, "grantee" = the brand that made the post). Needed only when the key's owner approves for both bran…
- `decision` (string, required)
- `id` (string, required): A convexPostId or a review id.
- `note` (string)
- `pastSlot`: Only for a held post whose slot passed: "now", or { at: ISO-8601 future time }.

### `list_approvals` (~140 tokens)

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". With status "rejected" it lists rejected content instead, with the reviewer's reason (`note`, `reasonChips`), who rejected it and when. Needs the explicit `approve` scope (admin does not include it). Agency plan.
(Wraps GET /api/v2/approvals.)

Input parameters:

- `brandId` (string)
- `status` (string)

### `decide_approval` (~234 tokens)

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. On "reject", pass the reviewer's reason as `note` (the submitter is told it); it is optional unless the org requires one (then 422 without it). 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}.)

Input parameters:

- `decision` (string, required)
- `id` (string, required): An approval id.
- `note` (string): On reject: the reason. Optional unless the org requires one.

### `list_queues` (~108 tokens)

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.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.

### `get_queue` (~114 tokens)

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}.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.
- `id` (string, required)

### `create_queue` (~411 tokens)

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.)

Input parameters:

- `brandId` (string)
- `cadenceHours` (integer): 1, 2, 3, 4, 6, 8, 12, or whole days (24, 48, …, 720).
- `lookahead` (integer): How many upcoming items are scheduled at the platform at once.
- `name` (string, required)
- `platforms` (array, required)
- `startsAt` (string): ISO 8601 with an offset (or Z). Nothing is booked before it; the first slot is the first timeOfDay (in the queue's timezone) at or after it, so a startsAt that is itself a slot is the first slot. A p…
- `timeOfDay` (string): 24-hour "HH:MM" local time.
- `timezone` (string): IANA timezone. Slots keep their local time across DST.
- `youtube` (object): Queue-wide YouTube defaults (a draft's own options win; titles are per item). YouTube upload options, applied to YouTube targets only. madeForKids is always sent to YouTube (default false). A missing…

### `update_queue` (~308 tokens)

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}.)

Input parameters:

- `brandId` (string)
- `cadenceHours` (integer): 1, 2, 3, 4, 6, 8, 12, or whole days (24, 48, …, 720).
- `id` (string, required)
- `lookahead` (integer)
- `name` (string)
- `platforms` (array): Replaces the queue's platforms for items booked from now on. Already-booked items keep theirs; items with their own `platforms` keep using them.
- `startsAt` (string|null): ISO 8601 with an offset (or Z). Nothing is booked before it; the first slot is the first timeOfDay (in the queue's timezone) at or after it, so a startsAt that is itself a slot is the first slot. A p…
- `status` (string)
- `timeOfDay` (string): 24-hour "HH:MM" local time.

### `delete_queue` (~196 tokens)

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}.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.
- `id` (string, required)
- `unschedule` (boolean): true: delete every booked post at the platform first (the dashboard's "Unschedule"), then delete. Without it a booked post is a 409 — nothing is ever left booked at the platform without its queue ite…

### `add_queue_items` (~157 tokens)

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.)

Input parameters:

- `brandId` (string)
- `contentIds` (array)
- `id` (string, required)
- `items` (array)

### `queue_item_action` (~229 tokens)

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}.)

Input parameters:

- `action` (string)
- `brandId` (string)
- `id` (string, required)
- `itemId` (string, required)
- `platforms` (array|null): This item's own platforms, used when it is booked; null → back to the queue's. Only before the item is booked (queued / failed / skipped).
- `position` (integer): 0-based index among the waiting (queued) items.
- `youtubeTitle` (string|null): null or "" → back to the draft's stored title.

### `remove_queue_item` (~203 tokens)

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}.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.
- `id` (string, required)
- `itemId` (string, required)
- `unschedule` (boolean): true: delete the booked post at the platform first (the dashboard's "Unschedule"), then delete. Without it a booked post is a 409 — nothing is ever left booked at the platform without its queue item.

### `reorder_queue_items` (~108 tokens)

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.)

Input parameters:

- `brandId` (string)
- `id` (string, required)
- `itemIds` (array, required)

### `get_analytics` (~51 tokens)

Analytics overview

[read-only] Engagement overview for the last 7, 30 or 90 days. Pro plan or higher.
(Wraps GET /api/v2/analytics.)

Input parameters:

- `range` (string)

### `query_insights` (~706 tokens)

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.)

Input parameters:

- `aggregation` (string): Default median; pooled for the rates engagementRate, ctr and completionRate. A rate is pooled (Σ numerator ÷ Σ denominator), mean, median or count, never sum or p90. Pooled applies to rates only.
- `brandId` (string): A brand id, or "all" (default: every profile you can see). A brand-bound key is always its own brand.
- `breakdown` (array): Up to 2 of: platform, brand, campaign, experiment, arm, weekday, hour, postType, or label:<key> (e.g. label:cut, label:line, label:source, label:hook, label:series). The multi-valued format key also…
- `dateFrom` (string): ISO date or epoch ms (publish time). Default: 90 days ago; ranges are capped at 400 days.
- `dateTo` (string): ISO date or epoch ms. Default: now.
- `filters` (object)
- `format` (string): csv = the result as a spreadsheet-safe CSV attachment (UTF-8 BOM, CRLF, cells starting with = + - @ are prefixed with '). Without `format`, `Accept: text/csv` also selects CSV. Errors are always JSON.
- `formula` (object): Optional (needs the Advanced insights feature): combine up to three series. Each series is a metric + aggregation over the same breakdown, window, interval, filters and range. `expression` (≤ 80 char…
- `interval` (string): Adds a time series bucketed by publish date.
- `metric` (string)
- `tzOffsetMinutes` (integer): Offset from UTC for weekday / hour / day buckets (e.g. -300).
- `window` (string): Snapshot age: the metric as it stood 48h / 7d / 30d after publish, or the latest refresh.

### `compare_labels` (~156 tokens)

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.)

Input parameters:

- `brandId` (string): A brand id, or "all" (default: every profile you can see). A brand-bound key is always its own brand.
- `include` (string)
- `key` (string)
- `metric` (string): Which metric "leading" ranks by.
- `window` (string)

### `list_experiments` (~125 tokens)

List experiments

[read-only] A/B experiments. Content joins one by carrying the label `experiment:<id>` plus `<compareKey>:<variant>`.
(Wraps GET /api/v2/experiments.)

Input parameters:

- `allBrands` (boolean): true: every brand of the organizations you own or administer, plus your personal brands. Refused (403) for a plain member and for a brand-bound key.
- `brandId` (string): A brand id, or "all" (default: every profile you can see). A brand-bound key is always its own brand.

### `get_experiment` (~49 tokens)

Get an experiment

[read-only] One experiment: hypothesis, variants, compareKey, window and status.
(Wraps GET /api/v2/experiments/{id}.)

Input parameters:

- `id` (string, required)

### `create_experiment` (~163 tokens)

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.)

Input parameters:

- `brandId` (string): A brand id, or "personal" (default).
- `compareKey` (string)
- `endAt` (string|null)
- `hypothesis` (string)
- `name` (string, required)
- `notes` (string)
- `primaryMetric` (string)
- `startAt` (string|null)
- `status` (string)
- `variants` (array)

### `update_experiment` (~141 tokens)

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}.)

Input parameters:

- `compareKey` (string)
- `endAt` (string|null)
- `hypothesis` (string)
- `id` (string, required)
- `name` (string)
- `notes` (string)
- `primaryMetric` (string)
- `startAt` (string|null)
- `status` (string)
- `variants` (array)

### `delete_experiment` (~56 tokens)

Delete an experiment

[DESTRUCTIVE] DESTRUCTIVE: deletes the experiment record (content keeps its labels). Ask the user before calling.
(Wraps DELETE /api/v2/experiments/{id}.)

Input parameters:

- `id` (string, required)

### `get_experiment_results` (~84 tokens)

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.)

Input parameters:

- `id` (string, required)
- `include` (string)
- `window` (string)

### `get_experiment_readout` (~103 tokens)

Experiment readout

[read-only] n per arm, pooled reach engagement rate, a 90% interval and a hedged verdict (likely_winner, no_clear_winner or not_enough_data). Missing numbers are "not reported" (null), never 0. Say "likely", never "proven".
(Wraps GET /api/v2/experiments/{id}/readout.)

Input parameters:

- `id` (string, required)
- `window` (string)

### `get_experiment_rounds` (~78 tokens)

Variant test rounds

[read-only] A version test's rounds: each closed round's frozen snapshot, the open round's live numbers (null = not reported), the winner if one was recorded, and any repeat-winner proposal.
(Wraps GET /api/v2/experiments/{id}/rounds.)

Input parameters:

- `id` (string, required)

### `start_experiment_round` (~108 tokens)

Start a test round

[writes flayr data · `write` scope] Files the round's versions into the approval queue on the given platforms, in spaced slots with UTM links. Nothing posts until a person approves each one. Round 2 re-tests the arms kept from round 1.
(Wraps POST /api/v2/experiments/{id}/rounds.)

Input parameters:

- `id` (string, required)
- `platforms` (array, required)
- `startAt`: First slot; default now.

### `close_experiment_round` (~104 tokens)

Close a test round

[writes flayr data · `write` scope] Freezes the open round's numbers and keeps the best arms for the next round. A winner is recorded only when the readout says likely_winner, or when `pick` names an arm the user chose. Ask the user before passing `pick`.
(Wraps POST /api/v2/experiments/{id}/close-round.)

Input parameters:

- `id` (string, required)
- `pick` (string)

### `propose_repeat_winner` (~111 tokens)

Propose repeating the winner

[writes flayr data · `write` scope] Proposes slots to post the winning version again (default 3 over 2 weeks). Only a proposal: nothing is booked or posted until the user confirms it in flayr, and then each post still goes through approval.
(Wraps POST /api/v2/experiments/{id}/repeat-winner.)

Input parameters:

- `id` (string, required)
- `platforms` (array)
- `startAt`: First slot; default now.

### `list_text_templates` (~120 tokens)

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.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands), "personal", or "all" (default: everything the key owner can see). A brand-bound key is always pinned to its brand; any other value is 403.
- `includeArchived` (boolean)

### `get_text_template` (~48 tokens)

Get a format

[read-only] One format (text template) with its full structure and example.
(Wraps GET /api/v2/text-templates/{id}.)

Input parameters:

- `id` (string, required)

### `create_text_template` (~239 tokens)

Create a format

[writes flayr data · `write` scope] Creates a reusable format (text template) in a brand.
(Wraps POST /api/v2/text-templates.)

Input parameters:

- `brandId` (string)
- `ctaSlots` (array)
- `defaultLabels` (array): "key:value" labels every draft started from this format gets (plus the format's own `label`).
- `example` (string|null)
- `format` (string, required)
- `guardrails` (array)
- `hookPatterns` (array): "[slot]" placeholders are kept as written.
- `length` (object, required)
- `maxHashtags` (integer|null)
- `name` (string, required)
- `platform` (string|null): e.g. "linkedin"; null = any platform.
- `sections` (array): In order. "Start from a format" writes one `[you fill: Label — guidance]` placeholder per section.
- `source` (string|null): Where the shape came from, e.g. an import path.
- `useFor` (string|null): One line: when to pick this format.

### `update_text_template` (~256 tokens)

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}.)

Input parameters:

- `archived` (boolean)
- `ctaSlots` (array)
- `defaultLabels` (array): "key:value" labels every draft started from this format gets (plus the format's own `label`).
- `example` (string|null)
- `format` (string)
- `guardrails` (array)
- `hookPatterns` (array): "[slot]" placeholders are kept as written.
- `id` (string, required)
- `length` (object)
- `maxHashtags` (integer|null)
- `name` (string)
- `platform` (string|null): e.g. "linkedin"; null = any platform.
- `sections` (array): In order. "Start from a format" writes one `[you fill: Label — guidance]` placeholder per section.
- `source` (string|null): Where the shape came from, e.g. an import path.
- `useFor` (string|null): One line: when to pick this format.

### `delete_text_template` (~59 tokens)

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}.)

Input parameters:

- `id` (string, required)

### `list_attribution_rules` (~121 tokens)

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.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.

### `list_share_rules` (~115 tokens)

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.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.

### `create_share_rule` (~356 tokens)

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.)

Input parameters:

- `approval` (string): `needs_approval` (default): each fire waits for a person (POST …/actions/{id} `approve`). `auto`: runs on its own.
- `brandId` (string)
- `delayMinutes` (integer)
- `enabled` (boolean)
- `kind` (string): `reshare`: share the post to `targets`. `comment`: comment on the post itself through the source account.
- `name` (string, required)
- `sourceAccountIds` (array, required): Fires when a post goes live through any of these connected accounts.
- `sourceLabel` (string|null): Only posts carrying this label.
- `targets` (array)
- `template` (string): The reshare commentary or comment text. Tokens, resolved only once the URL exists: `{{postUrl}}` (this post, on the comment's platform), `{{postUrl:<platform>}}`, `{{parentPostUrl:<post or draft id>}…

### `list_publish_actions` (~116 tokens)

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.)

Input parameters:

- `brandId` (string): Account-wide keys: "personal" or an id from GET /api/v2/brands (omitted → the brand last active in the dashboard). A brand-bound key always uses its own brand.
- `status` (string)

### `act_on_publish_action` (~112 tokens)

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}.)

Input parameters:

- `id` (string, required)
- `op` (string, required)

### `add_post_comments` (~121 tokens)

Add comments after publish to a post

[PUBLISHES to social accounts · `publish` scope] Schedules comments on a post, sent once after it is live on that platform and its tokens resolve ({{postUrl}}, {{parentPostUrl:<post id>}}, {{youtubeUrl:<post id>}}), optionally at sendAt. Confirm with the user first. Needs the `publish` scope (or admin) on the Agency plan or higher.
(Wraps POST /api/v2/posts/{id}/comments.)

Input parameters:

- `comments` (array, required)
- `id` (string, required)

### `list_post_comments` (~63 tokens)

List a post's comments after publish

[read-only] A post's comments-after-publish and reshares, with status (waiting, needs_approval, sent, failed) and the resolved text.
(Wraps GET /api/v2/posts/{id}/comments.)

Input parameters:

- `id` (string, required)

### `list_frames` (~92 tokens)

List frames

[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.)

Input parameters:

- `aspect` (string)
- `includeArchived` (boolean)
- `kind` (string)

### `get_frame` (~43 tokens)

Get a frame

[read-only] One library frame with its sizes and content areas.
(Wraps GET /api/v2/frames/{id}.)

Input parameters:

- `id` (string, required)

### `create_frame` (~193 tokens)

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.)

Input parameters:

- `defaultKey` (string): A defaults[].ref without "default:".
- `kind` (string): Default: guessed from the name.
- `name` (string)
- `paletteOverride` (object): Colors that win over the brand kit.
- `rights` (string): Who holds the rights to the art. Optional; a frame without it shows a reminder.
- `tags` (array)
- `texts` (object): Texts some frames print (ribbon/tag label, lower-third title/subtitle).
- `variants` (array): One per aspect ratio.

### `apply_frame` (~272 tokens)

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.)

Input parameters:

- `aspect` (string): Default: nearest to the source image.
- `contentId` (string): A content id: its publish swaps this framed image in for `platform`.
- `fit` (string)
- `frameRef` (string, required): `default:<key>` or `brand:<id>` from GET /api/v2/frames.
- `offsetX` (number)
- `offsetY` (number)
- `paletteOverride` (object): Colors that win over the brand kit.
- `platform` (string): Platform this framed image is for ("all" by default).
- `slideIndex` (integer): Image position in the post.
- `sourceUrl` (string, required): https image URL (flayr/Convex storage or a stock host).
- `texts` (object): Texts some frames print (ribbon/tag label, lower-third title/subtitle).
- `zoom` (number)

### `get_frame_settings` (~41 tokens)

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.)

### `update_frame_settings` (~91 tokens)

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.)

Input parameters:

- `aspect` (string)
- `frameRef` (string|null, required)
- `platform` (string, required)

### `offers_capture` (~157 tokens)

Capture offers (Hook Studio step 1)

[writes flayr data · `write` scope] Turns what the brand sells (pasted text, or answers to the offer interview) into draft offers in its offer bank, with prices from the plan catalog, Shopify or the text. Charges Embers once per call (refunded when nothing usable comes back). Needs the Pro plan or higher.
(Wraps POST /api/v2/hook-studio/offers.)

Input parameters:

- `answers` (array)
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `source` (string)
- `text` (string): What the brand sells, in any words.

### `offers_list` (~93 tokens)

List offers

[read-only] The brand's offer bank (filter by status: draft, confirmed, retired). Free. Needs the Pro plan or higher.
(Wraps GET /api/v2/hook-studio/offers.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `status` (string)

### `offers_update` (~201 tokens)

Update an offer

[writes flayr data · `write` scope] Edits an offer's fields or status; "confirmed" makes it usable for hooks and CTAs. Confirm changes with the person first. Free. Needs the Pro plan or higher.
(Wraps PATCH /api/v2/hook-studio/offers.)

Input parameters:

- `audience` (string)
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `currency` (string)
- `id` (string, required)
- `name` (string)
- `outcomes` (array)
- `pains` (array)
- `priceAmount` (number)
- `priceRef` (string)
- `priceSource` (string)
- `proof` (array)
- `proofUrl` (string)
- `status` (string)
- `summary` (string)

### `hooks_generate` (~205 tokens)

Generate hooks (Hook Studio step 2)

[writes flayr data · `write` scope] Writes a batch of draft hooks (on-screen card + spoken line) from the confirmed offers, claims-checked; blocked ones come back in `dropped`, unsaved. Charges Embers once per call (refunded when nothing usable comes back). Needs the Pro plan or higher.
(Wraps POST /api/v2/hook-studio/hooks/generate.)

Input parameters:

- `angles` (array): e.g. pain, outcome, curiosity, proof.
- `bodyIds` (array): Bodies (from POST /api/v2/hook-studio/bodies/map) the hooks should lead into.
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `count` (integer)
- `formats` (array): e.g. question, bold claim, number, POV.
- `topics` (array)

### `hooks_list` (~99 tokens)

List hooks

[read-only] The brand's hook bank (filter by status: draft, approved, retired), with each hook's claims result. Free. Needs the Pro plan or higher.
(Wraps GET /api/v2/hook-studio/hooks.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `status` (string)

### `hooks_update` (~166 tokens)

Update a hook

[writes flayr data · `write` scope] Edits a hook or sets its status. New copy is claims-checked again; a hook whose claims block cannot be approved. Free. Needs the Pro plan or higher.
(Wraps PATCH /api/v2/hook-studio/hooks.)

Input parameters:

- `angle` (string)
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `card` (string)
- `fit` (array)
- `format` (string)
- `id` (string, required)
- `line` (string)
- `status` (string)
- `style` (string)
- `topics` (array)

### `bodies_map` (~160 tokens)

Map bodies (Hook Studio step 3)

[writes flayr data · `write` scope] Maps library clips, Shopify products, features or texts to draft bodies (what each shows and proves) so hooks can lead into them. Charges Embers once per call (refunded when nothing usable comes back). Needs the Pro plan or higher.
(Wraps POST /api/v2/hook-studio/bodies/map.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `features` (array)
- `includeCollab` (boolean)
- `libraryContentIds` (array)
- `productGids` (array)
- `texts` (array)

### `ctas_generate` (~162 tokens)

Generate CTAs (Hook Studio step 4)

[writes flayr data · `write` scope] Writes a batch of draft calls to action for the confirmed offers, claims-checked against where they point. Charges Embers once per call (refunded when nothing usable comes back). Needs the Pro plan or higher.
(Wraps POST /api/v2/hook-studio/ctas/generate.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `count` (integer)
- `destination` (string): An https link every CTA may point at, or `bio` for link in bio.
- `offerIds` (array)
- `platforms` (array)

### `ctas_list` (~117 tokens)

List CTAs

[read-only] The brand's CTA bank (filter by status: draft, approved, retired), with each CTA's goal (goalKey, isPrimary, paidEligible, adButton) and claims result. Free. Needs the Pro plan or higher.
(Wraps GET /api/v2/hook-studio/ctas.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `status` (string)

### `ctas_create` (~269 tokens)

Add a CTA

[writes flayr data · `write` scope] Adds a call to action written by the person, claims-checked, optionally tagged as a goal (goalKey; isPrimary makes it the brand's primary goal and demotes the previous one). Nothing here creates or runs an ad. Free. Needs the Pro plan or higher.
(Wraps POST /api/v2/hook-studio/ctas.)

Input parameters:

- `adButton` (string): The ad button. Default: the ads config default CTA.
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `card` (string, required)
- `destination` (string, required): An https link, or "bio" for the link in bio.
- `goalKey` (string): The goal this CTA serves, a short slug such as `tech-checkup`.
- `isPrimary` (boolean): The brand's primary goal. Setting it demotes the previous primary.
- `line` (string, required)
- `offerId` (string)
- `paidEligible` (boolean): Ads may use this CTA. Default: true for a primary, false otherwise.
- `platforms` (array, required)
- `status` (string)

### `ctas_update` (~274 tokens)

Update a CTA

[writes flayr data · `write` scope] Edits a CTA, its status or its goal fields (goalKey, isPrimary, paidEligible, adButton). New copy or a new destination is claims-checked again; a CTA whose claims block cannot be approved. Free. Needs the Pro plan or higher.
(Wraps PATCH /api/v2/hook-studio/ctas.)

Input parameters:

- `adButton` (string): The ad button. Default: the ads config default CTA.
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `card` (string)
- `destination` (string): An https link, or "bio" for the link in bio.
- `goalKey` (string): The goal this CTA serves, a short slug such as `tech-checkup`.
- `id` (string, required)
- `isPrimary` (boolean): The brand's primary goal. Setting it demotes the previous primary.
- `line` (string)
- `offerId` (string)
- `paidEligible` (boolean): Ads may use this CTA. Default: true for a primary, false otherwise.
- `platforms` (array)
- `status` (string)

### `cta_settings_get` (~117 tokens)

Get goals and CTA check settings

[read-only] The brand's goals (source tagged, implicit from the website, or none; the primary goal) and its CTA check settings (ctaLevel, ctaPaidLevel, organicSupportingHosts, autoApplyPrimaryCta). Free. Needs the Pro plan or higher.
(Wraps GET /api/v2/hook-studio/cta-settings.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.

### `cta_settings_update` (~237 tokens)

Update CTA check settings

[writes flayr data · `write` scope] Changes only the CTA check settings sent: ctaLevel (off, warn, block), ctaPaidLevel (warn or block), organicSupportingHosts and autoApplyPrimaryCta. Only the brand's owners and admins can change them. Confirm changes with the person first. Free. Needs the Pro plan or higher.
(Wraps PATCH /api/v2/hook-studio/cta-settings.)

Input parameters:

- `autoApplyPrimaryCta` (boolean): Studio adds the primary CTA to a post with none. Default: true.
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `ctaLevel` (string): The CTA check on organic posts. Default: warn.
- `ctaPaidLevel` (string): The CTA check on paid posts, never off. Default: block.
- `organicSupportingHosts` (array): Hosts an organic post may link to besides a goal. Default: the website, youtube.com, youtu.be, github.com.

### `claims_check` (~132 tokens)

Check copy for claims

[read-only] Checks a hook, CTA, caption or ad against the brand's claims sources (verified prices, banned phrases, competitors, destinations). No model call, nothing stored, free. Needs the Pro plan or higher.
(Wraps POST /api/v2/hook-studio/claims/check.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `context` (string, required)
- `destination` (string)
- `platforms` (array)
- `text` (string, required)

### `cta_check` (~237 tokens)

Check a CTA before an ad

[read-only] Checks whether a draft, post or link points at the brand's goal page (the same check the ad and boost routes run in paid mode; a block there refuses the ad). Run it before every boost or ad and paste the result. Nothing stored, free. Needs the Pro plan or higher.
(Wraps POST /api/v2/cta-check.)

Input parameters:

- `adButton` (string): The ad button, e.g. LEARN_MORE.
- `brandId` (string): A brand id from GET /api/v2/brands. Required for an account-wide key; a brand-bound key may omit it.
- `caption` (string): The caption, when it differs from the draft.
- `contentId` (string): A draft id.
- `linkUrl` (string): The link the ad or post would use.
- `mode` (string): Default: paid.
- `objective` (string): The ad objective, e.g. traffic, link_clicks, engagement. Click objectives can block; others warn.
- `platforms` (array)
- `postId` (string): A post id.

### `list_growth_triggers` (~105 tokens)

List comment-to-get triggers

[read-only] The brand's keyword triggers: a comment matching a keyword gets a public reply and/or a DM. Shows whether each is active and its counts.
(Wraps GET /api/v2/growth-triggers.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.

### `get_growth_trigger` (~99 tokens)

Get a comment-to-get trigger

[read-only] One trigger with its keywords, texts, link, cap and counts.
(Wraps GET /api/v2/growth-triggers/{id}.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `id` (string, required)

### `get_growth_trigger_stats` (~107 tokens)

Comment-to-get trigger stats

[read-only] Matched comments, unique commenters, replies, DMs and failures, in total and per day.
(Wraps GET /api/v2/growth-triggers/{id}/stats.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `id` (string, required)

### `create_growth_trigger` (~365 tokens)

Create a comment-to-get trigger (inactive)

[writes flayr data · `write` scope] Creates a keyword trigger, always INACTIVE: nothing is sent until a person turns it on. DMs work on Instagram and Facebook only; the reply text must not contain a keyword.
(Wraps POST /api/v2/growth-triggers.)

Input parameters:

- `accountId` (string): A connected account id (GET /api/v2/accounts); omitted = every account of the brand.
- `brandId` (string)
- `campaignKey` (string): utm_campaign for {{link}}, and the campaign column of the logs.
- `delaySeconds` (number): Wait before replying; capped at 20 at send time.
- `dmText` (string): Private reply (Instagram and Facebook only; refused on an account of another platform).
- `dmVariations` (array)
- `keywords` (array, required): Case-insensitive. exact = the whole comment; startsWith = its start; contains = anywhere (substring, so "hi" also matches "this").
- `link` (string): http(s) URL behind {{link}}.
- `maxSends` (integer): After this many sends, reply only overCapText.
- `name` (string, required)
- `overCapText` (string)
- `postIds` (array): Only comments on these posts; omitted = all posts.
- `priority` (number): Higher is checked first; the first matching trigger wins.
- `repeatPolicy` (string)
- `replyText` (string): Public reply. {{name}} = the commenter, {{link}} = `link` with UTM tags. Must not contain a keyword (it would trigger itself).
- `replyVariations` (array): Alternatives picked at random with replyText.

### `update_growth_trigger` (~434 tokens)

Update or turn on a comment-to-get trigger

[PUBLISHES to social accounts · `publish` scope] Changes a trigger; only the fields you pass change. `active: true` makes it reply to and DM real people from then on and needs the publish scope: confirm with the user first. `active: false` pauses it.
(Wraps PATCH /api/v2/growth-triggers/{id}.)

Input parameters:

- `accountId` (string): A connected account id (GET /api/v2/accounts); omitted = every account of the brand.
- `active` (boolean)
- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `campaignKey` (string): utm_campaign for {{link}}, and the campaign column of the logs.
- `delaySeconds` (number): Wait before replying; capped at 20 at send time.
- `dmText` (string): Private reply (Instagram and Facebook only; refused on an account of another platform).
- `dmVariations` (array)
- `id` (string, required)
- `keywords` (array): Case-insensitive. exact = the whole comment; startsWith = its start; contains = anywhere (substring, so "hi" also matches "this").
- `link` (string): http(s) URL behind {{link}}.
- `maxSends` (integer): After this many sends, reply only overCapText.
- `name` (string)
- `overCapText` (string)
- `postIds` (array): Only comments on these posts; omitted = all posts.
- `priority` (number): Higher is checked first; the first matching trigger wins.
- `repeatPolicy` (string)
- `replyText` (string): Public reply. {{name}} = the commenter, {{link}} = `link` with UTM tags. Must not contain a keyword (it would trigger itself).
- `replyVariations` (array): Alternatives picked at random with replyText.

### `delete_growth_trigger` (~104 tokens)

Delete a comment-to-get trigger

[DESTRUCTIVE] DESTRUCTIVE: deletes a trigger (its logs stay). Ask the user before calling.
(Wraps DELETE /api/v2/growth-triggers/{id}.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `id` (string, required)

### `ads_offers_list` (~122 tokens)

List ad platform offers

[read-only] The brand's new-advertiser offers per ad network, one row per ad account (or a network not connected yet): status, spend so far, what is left to spend, the daily pace and the deadline, and how to claim. Read-only.
(Wraps GET /api/v2/ads/offers.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands). Required for an account-wide key; a brand-bound key is pinned to its brand and any other value is 403.

### `ads_offer_update` (~162 tokens)

Record an ad platform offer claim

[writes flayr data · `write` scope] Records the claim of one offer row (`claimId` = a row id from ads_offers_list): status, the date it was claimed, and notes. Owners and admins of the brand only. A record only: it never creates, turns on or funds an ad.
(Wraps PATCH /api/v2/ads/offers/{claimId}.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands). Required for an account-wide key.
- `claimId` (string, required)
- `claimedAt` (string|null): YYYY-MM-DD (or ISO timestamp); null clears it.
- `notes` (string|null)
- `status` (string)

### `brand_business_details_get` (~124 tokens)

Get a brand's business details

[read-only] A brand's legal name, registered address, website, currency and timezone: what ad networks ask for when you open an ads account. `hasTaxId` says whether a tax id is on file; the tax id itself is never returned. Use an `id` from list_brands.
(Wraps GET /api/v2/brands/{id}/business-details.)

Input parameters:

- `id` (string, required): A brand id from GET /api/v2/brands. A brand-bound key may only name its own brand (another brand → 403).

### `brand_business_details_update` (~193 tokens)

Update a brand's business details

[writes flayr data · `write` scope] Sets a brand's legal name, registered address, currency (ISO, like USD) and/or timezone (IANA, like America/New_York). Only the fields sent change; a brand with no details yet needs all four. Do not send a tax id: it is refused. A brand owner or admin adds it in flayr (Brand › Overview).
(Wraps PATCH /api/v2/brands/{id}/business-details.)

Input parameters:

- `currency` (string): 3-letter ISO currency code.
- `id` (string, required): A brand id from GET /api/v2/brands. A brand-bound key may only name its own brand (another brand → 403).
- `legalName` (string)
- `registeredAddress` (object): Replaced as a whole when sent.
- `timezone` (string): IANA time zone.

### `ads_boost_candidates` (~187 tokens)

Posts worth boosting

[read-only] The brand's published posts ranked for a boost (reach, engagement rate, link clicks, recency), with score parts, the call-to-action link found, and whether an ad account is connected for the network. Removed posts, posts with no call to action and networks a boost can't run on are left out. Read-only: nothing is boosted or charged.
(Wraps GET /api/v2/ads/boost-candidates.)

Input parameters:

- `brandId` (string): A brand id from GET /api/v2/brands. Omitted: the key's brand (a brand-bound key) or the owner's active brand.
- `days` (integer): How many days back to read (clamped).
- `include` (string): Also list posts on networks a boost can't run on.
- `limit` (integer): How many candidates to return (clamped).

### `ads_accounts_list` (~94 tokens)

List ad accounts

[read-only] The brand's ad account with its currency, spend limit, balance (minor units) and readiness.
(Wraps GET /api/v2/ads/accounts.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.

### `ads_campaigns_list` (~135 tokens)

List ad campaigns

[read-only] The brand's ad campaigns with status, budget, start/end dates, max spend, spend to date, ad sets and ads. Filter by status or platform.
(Wraps GET /api/v2/ads/campaigns.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `platform` (string): e.g. facebook, instagram
- `status` (string): e.g. active, paused

### `ads_campaign_get` (~103 tokens)

Get an ad campaign

[read-only] One ad campaign by its Flayr id, with its ad sets and ads.
(Wraps GET /api/v2/ads/campaigns/{id}.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `id` (string, required)

### `ads_insights` (~147 tokens)

Ad insights

[read-only] Impressions, clicks, spend, link clicks and leads per campaign for a date range (default the last 30 days, at most 93), with totals per currency.
(Wraps GET /api/v2/ads/insights.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `from` (string): YYYY-MM-DD; default 30 days ago
- `level` (string)
- `to` (string): YYYY-MM-DD; default today

### `ads_readiness` (~92 tokens)

Ads readiness

[read-only] Whether each connected network and ad account can run ads, and what to fix first.
(Wraps GET /api/v2/ads/readiness.)

Input parameters:

- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.

### `ads_campaign_pause` (~192 tokens)

Pause or stop an ad campaign

[writes flayr data · `write` scope] Pauses a live ad campaign on its ad network so it stops spending (`action: "pause"` or `"stop"`; both leave it paused). It cannot resume or activate a campaign; that is done in the Flayr app. Confirm with the user first.
(Wraps POST /api/v2/ads/campaigns/{id}/pause.)

Input parameters:

- `action` (string): stop = a pause you mean as final; the network shows paused either way.
- `brandId` (string): A brand id (GET /api/v2/brands) or "personal"; default: the key owner's active brand. A brand-bound key is always pinned to its brand; any other value is 403.
- `id` (string, required)
- `idempotencyKey` (string): Retrying with the same key never runs the change twice.

### `get_campaign_review` (~146 tokens)

Get a campaign's review sections

[read-only] A campaign's pieces grouped into review sections (episode, week, channel set, variant group or the creator's own). Each piece has preview URLs (video / images / audio), caption, labels, its bookings (channels, time, review state) and QA checks; each section has its status, round and every previous fix note. Share `reviewUrl` so a person can preview and decide in flayr.
(Wraps GET /api/v2/campaigns/{id}/review.)

Input parameters:

- `id` (string, required): Campaign id.
- `pieces` (boolean): false = sections and counts only.
- `sectionId` (string): Only this section.

### `group_campaign_review` (~97 tokens)

Group a campaign into review sections

[writes flayr data · `write` scope] Puts the campaign's not-yet-sectioned pieces into review sections (groupBy: week, channel_set, variant_group, episode or phase; default the brand's setting). Existing sections and decisions are kept.
(Wraps POST /api/v2/campaigns/{id}/review.)

Input parameters:

- `groupBy` (string)
- `id` (string, required): Campaign id.

### `decide_campaign_review_section` (~258 tokens)

Approve, fix or drop a campaign review section

[PUBLISHES to social accounts · `publish` scope] action "approve" approves the section's pending pieces (or pieceIds) and their held posts GO LIVE at their slots — needs the explicit `review` scope (admin does not include it); QA-failed pieces are skipped unless overrideQa. "fix" sends the pieces back with a `note` (required): their posts are taken off the schedule and the section opens a new round; other sections are untouched. "drop" takes the posts off the schedule and drops the pieces. "ready" puts fixed pieces back up for review. fix/drop/ready need `review` or `publish`. Agency plan. Confirm with the user first.
(Wraps POST /api/v2/campaigns/{id}/review/sections/{sectionId}.)

Input parameters:

- `action` (string, required)
- `id` (string, required): Campaign id.
- `note` (string): Required for fix: what to change. Quoted in the next round.
- `overrideQa` (boolean): Approve pieces that failed QA anyway.
- `pieceIds` (array): Only these pieces ("approve selected"). Default: every eligible piece in the section.
- `sectionId` (string, required)

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/social-flayr-flayr/api-mcp#diagnostics

## Score history

- 2026-11-04: 78
- 2026-11-03: 77
- 2026-11-02: 77
- 2026-11-01: 76
- 2026-10-31: 76
- 2026-10-30: 75
- 2026-10-29: 75

## Common questions

### What is the flayr MCP server?

flayr is an MCP server listed in the public MCP registry as social.flayr/flayr. Create, schedule and publish social posts to 13 platforms for your brands. This page covers its hosted endpoint (https://flayr.social/api/mcp).

### Is the flayr MCP server safe to use?

flayr scores 78 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the flayr MCP server expose?

flayr exposes 102 tools: whoami, list_brands, brand_profile_get, brand_profile_update, list_connected_accounts, and 97 more. Their descriptions and schemas cost roughly 21,119 tokens of context every time the server is loaded.

### Does the flayr MCP server require authentication?

Yes. flayr asked us for credentials when we connected, so you will need to authorise it in your MCP client before it can do anything.

### Is the flayr MCP server still maintained?

flayr is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- Remote endpoint: https://flayr.social/api/mcp
- Website: https://flayr.social/
- Changelog RSS feed: https://verifymcp.io/servers/social-flayr-flayr/api-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/social-flayr-flayr/api-mcp.json
- HTML version of this page: https://verifymcp.io/servers/social-flayr-flayr/api-mcp
