---
name: follow-fast
description: Run a social media content loop for apps with follow.fast. Generate slideshow posts for Instagram, Facebook, TikTok, Threads and Bluesky, review and approve them, check the calendar, and read which hooks, formats and times perform. Use when the user asks to generate or approve social posts, check what is scheduled, publish something now, or learn what works on their follow.fast account.
---

# follow.fast

follow.fast is a social media manager for apps. For each product (a **workspace**) it generates short slideshow posts in the product's brand voice: a **feed** variant for Instagram and Facebook (Threads and Bluesky repost it when the workspace has them connected and switched on in its posting plan; Bluesky shows the first 4 slides) and a **vertical** variant for TikTok. You review the generated **candidates**, approve the good ones, and follow.fast schedules them into the workspace's posting plan and publishes them. A few days later it scores each post on its views at 72 hours against that workspace's median, which tells you which hooks, formats and times to repeat.

Nothing is published until a candidate is approved (or `publish_now` is called). Generating is safe; approving and publishing are the commitments.

## Setup

1. In the web app open **Account -> API keys** and create a key. Choose **Read only** to just look around, **Read and write** to generate, approve and publish. The key (starts with `ff_`) is shown once.
2. Base URL: `https://app.follow.fast` (use your own host if self-hosted). Every request sends `Authorization: Bearer ff_...`.

### MCP (recommended for agents)

The same operations are available as MCP tools at `https://app.follow.fast/api/mcp` (Streamable HTTP, JSON). Read-only keys only see the read tools.

Claude Code:

```bash
claude mcp add --transport http follow-fast https://app.follow.fast/api/mcp \
  --header "Authorization: Bearer $FOLLOWFAST_API_KEY"
```

Claude Desktop (`claude_desktop_config.json`, via the `mcp-remote` bridge):

```json
{
  "mcpServers": {
    "follow-fast": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://app.follow.fast/api/mcp", "--header", "Authorization:${FF_AUTH}"],
      "env": { "FF_AUTH": "Bearer ff_your_key_here" }
    }
  }
}
```

Cursor (`~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "follow-fast": {
      "url": "https://app.follow.fast/api/mcp",
      "headers": { "Authorization": "Bearer ff_your_key_here" }
    }
  }
}
```

MCP tools: `list_workspaces`, `get_workspace`, `list_posts`, `get_post`, `list_candidates`, `get_candidate`, `generate_posts`, `get_generation_status`, `approve_candidate`, `reject_candidate`, `publish_now`, `retry_post`, `update_post`, `reschedule_post`, `delete_post`, `create_post`, `get_results`, `get_calendar`.

### REST

JSON over HTTPS under `/api/v1`. The OpenAPI 3.1 spec is at `/api/v1/openapi.json`; human docs at `/docs/api`. Responses are `{ "data": ..., "meta": ... }`; errors are `{ "error": { "code", "message", "details" } }` with a 4xx status. Send an `Idempotency-Key` header on POSTs you may retry. Limits: 60 requests/minute sustained (burst 120), then `429` with `Retry-After`.

```bash
export FF=https://app.follow.fast/api/v1
export H="Authorization: Bearer $FOLLOWFAST_API_KEY"
curl -s -H "$H" $FF/workspaces
```

## The daily loop

1. **Pick the workspace.** `GET /workspaces` -> note the `slug`. Check `accounts` to see which platforms are connected.
2. **Learn what works.** `GET /results?workspace=<slug>` shows per-dimension verdicts (hook, format, template, persona, time of day, slide count). `scale` options deserve more posts, `retire` ones fewer. A verdict needs at least 3 scored posts per option, so a new workspace has none yet.
3. **Check what is already queued.** `GET /calendar?workspace=<slug>` and `GET /candidates?workspace=<slug>`. Do not generate more if there is already a pile of unreviewed candidates.
4. **Generate.** `POST /workspaces/<slug>/generate` with `{"count": 3}` returns `202` and a `jobId`. Poll `GET /jobs/<jobId>` until `status` is `done` (it lists the new candidates). Candidates then render their slides: wait until each candidate's `state` is `ready`.
5. **Review.** `GET /candidates/<id>` returns the hook, slide texts, captions and image URLs (`GET` an image URL with your key to look at it). Judge it against the brand voice in `GET /workspaces/<slug>`. Fix captions with `PATCH /posts/<postId>` (`caption`, `hashtags`). Slide text is edited in the web app.
6. **Decide.** `POST /candidates/<id>/approve` schedules both variants into the next free slots; `POST /candidates/<id>/reject` discards it. Approve only what you would be happy to see published under the brand.
7. **Confirm.** `GET /calendar` shows the slots. Later, `GET /posts/<id>` has permalinks and metrics, and `GET /results` folds the 72-hour scores back into step 2.

```bash
# 4. generate 3 candidates (idempotent on retry)
curl -s -X POST -H "$H" -H "Content-Type: application/json" -H "Idempotency-Key: gen-$(date +%F)-1" \
  -d '{"count":3}' $FF/workspaces/my-app/generate

# poll
curl -s -H "$H" $FF/jobs/<jobId>

# review, then approve
curl -s -H "$H" $FF/candidates/<candidateId>
curl -s -X POST -H "$H" $FF/candidates/<candidateId>/approve

# move one post, or publish right away
curl -s -X PATCH -H "$H" -H "Content-Type: application/json" -d '{"scheduledAt":"2026-11-02T17:30:00Z"}' $FF/posts/<postId>
curl -s -X POST -H "$H" $FF/posts/<postId>/publish-now
```

## Bring your own content

`POST /posts` (or the `create_post` tool) creates draft posts from images you already have, so an agent can post its own graphics: JSON with `images: [{"url": "https://..."}, {"base64": "..."}]`, or multipart with `images` files.

```bash
curl -s -X POST -H "$H" \
  -F workspace=my-app -F caption="Three habits that stuck" -F hashtags="habits productivity" \
  -F images=@slide1.png -F images=@slide2.png -F images=@slide3.png $FF/posts
```

It creates a candidate (a carousel for Instagram/Facebook, plus Threads/Bluesky when connected and enabled or named in `platforms`, plus a TikTok photo post) in `draft` status. `platforms` takes `instagram`, `facebook_page`, `tiktok`, `threads`, `bluesky`. Approve it like a generated one. 2-10 images, 15 MB each.

## Rules and gotchas

- **Be careful with write tools.** `approve_candidate`, `publish_now`, `retry_post`, `delete_post` change what goes out under the brand. If the user asked only to look, use read tools. Prefer approving over `publish_now`: approval respects the posting plan and daily caps.
- **`approve` fails with 409** while the candidate is `rendering`/`pending`; wait for `ready`. It fails with 422 `tiktok_settings_incomplete` if TikTok Direct Post settings are not finished in the web app.
- **A `scheduledAt` of `null` after approval** means the posting plan had no free slot. Give the post a time with `PATCH /posts/<id>`.
- **Only unpublished posts can be edited or deleted.** A post that failed shows the reason in its target `error`: read it before `POST /posts/<id>/retry`.
- **Ids from another organization look like missing ids** (`404`): an unknown id and someone else's id are indistinguishable by design.
- **Respect the rate limit**: poll jobs every 10-15 seconds, not in a tight loop.
- Never print, log or commit the API key. Revoke it under Account -> API keys if it leaks.
