> ## Documentation Index
> Fetch the complete documentation index at: https://docs.postbreeze.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Platforms

> Per-platform integration guides — content types, fields, media constraints, and analytics for every supported network.

Postbreeze ships first-party publishers for the platforms below. Every
platform shares the **same scheduling API** (`POST /api/v1/posts`) —
what changes is the per-platform options blob you pass on each target.

<CardGroup cols={2}>
  <Card title="Instagram" icon="instagram" href="/platforms/instagram">
    Feed, Reels, carousels. Business + Creator accounts only.
  </Card>

  <Card title="Facebook" icon="facebook" href="/platforms/facebook">
    Feed, Photo, Photo Carousel, Video, Reels. Page accounts only.
  </Card>

  <Card title="TikTok" icon="music" href="/platforms/tiktok">
    Video uploads + photo carousels with branded-content disclosure.
  </Card>

  <Card title="X (Twitter)" icon="x" href="/platforms/x">
    Tweets, threads (up to 25), replies. Pay-Per-Use billing.
  </Card>

  <Card title="LinkedIn" icon="linkedin" href="/platforms/linkedin">
    Personal + Company pages. Photo gallery + PDF carousel.
  </Card>

  <Card title="YouTube" icon="youtube" href="/platforms/youtube">
    Long-form + Shorts. First-comment via Community surface.
  </Card>

  <Card title="Pinterest" icon="pinterest" href="/platforms/pinterest">
    Single + carousel pins. Board, title, link per pin.
  </Card>

  <Card title="Threads" icon="at" href="/platforms/threads">
    Text, image, video, mixed-media carousels (up to 20 items).
  </Card>
</CardGroup>

## Capability matrix

| Platform                          | Post types                         | First comment | Carousel            | Alt text     | Analytics       |
| --------------------------------- | ---------------------------------- | ------------- | ------------------- | ------------ | --------------- |
| [Instagram](/platforms/instagram) | Feed, Reel                         | ✅             | 2–10 (mixed)        | ❌ Not in API | ✅               |
| [Facebook](/platforms/facebook)   | Feed, Photo, Carousel, Video, Reel | ✅             | 2–10 (photo only)   | ❌ Not in API | ✅               |
| [TikTok](/platforms/tiktok)       | Video, Photo Carousel              | ❌ Not in API  | 1–35 (photo only)   | ❌ Not in API | ✅               |
| [X](/platforms/x)                 | Tweet, Thread, Reply               | ✅             | 1–4 (photos only)   | ✅            | ✅               |
| [LinkedIn](/platforms/linkedin)   | Personal, Company, PDF Carousel    | ✅             | 2–20 (photo or PDF) | ✅            | ⚠️ MDP required |
| [YouTube](/platforms/youtube)     | Video upload                       | ✅             | ❌                   | ❌            | ✅               |
| [Pinterest](/platforms/pinterest) | Pin, Carousel pin                  | ❌ Not in API  | 2–5 (photo only)    | ❌            | ✅               |
| [Threads](/platforms/threads)     | Text, Image, Video, Carousel       | ✅             | 2–20 (mixed)        | ✅            | ✅               |

## The single API surface

Every platform reaches the same endpoint with the same payload shape.
Workspace is **inferred from the API key** — you don't pass it.

```bash theme={null}
POST /api/v1/posts
```

There are two accepted body shapes. Use the **flat shape** for the
common case (same caption to N platforms with shared media); use the
**nested shape** when you need fine-grained per-target overrides like
custom platform options.

<CodeGroup>
  ```json Flat (recommended) theme={null}
  {
    "content": "Shared caption — applied to every platform",
    "scheduledFor": "2026-06-01T12:00:00Z",
    "mediaItems": [
      { "type": "IMAGE", "url": "https://cdn.example.com/hero.jpg", "altText": "Hero" }
    ],
    "platforms": [
      {
        "accountId": "soc_ig_…",
        "captionOverride": "Optional per-platform caption",
        "firstComment": "Optional follow-up reply"
      },
      { "accountId": "soc_x_…" }
    ]
  }
  ```

  ```json Nested (full control) theme={null}
  {
    "caption": "Shared caption — used as fallback for every target",
    "scheduledAt": "2026-06-01T12:00:00Z",
    "mediaIds": ["med_…", "med_…"],
    "mediaAltText": { "med_…": "Alt text for X / LinkedIn / Bluesky" },
    "targets": [
      {
        "socialAccountId": "soc_…",
        "captionOverride": "Optional per-target caption",
        "firstComment": "Optional follow-up reply",
        "mediaIds": ["med_…"],
        "platformOptions": {
          "platform": "INSTAGRAM",
          "kind": "FEED"
        }
      }
    ]
  }
  ```
</CodeGroup>

Provide **exactly one** of `platforms` or `targets` — sending both
returns `400 MUST_SPECIFY_EXACTLY_ONE`.

<Tip>
  **Workspace inference.** API keys are bound to a single workspace at
  creation. SDK and REST callers don't pass `workspaceId` — the server
  reads it from the bearer credential. Create separate keys for separate
  brands.
</Tip>

See the [API reference](/api-reference/overview) for the full schema,
or pick a platform on the left for the per-platform options you'd pass.

## A note on media

Three precedence rules apply for what media a target actually publishes:

1. If the target has its own non-empty `mediaIds` → use those.
2. Otherwise → use the post-level `mediaIds` (after URL ingest).
3. If both are empty → text-only post (only some platforms accept
   this — Threads ✅, X ✅, Facebook ✅, LinkedIn ✅; the others
   reject).

This lets you publish the same post to 5 platforms with one shared
media list, then override just the TikTok target with a portrait video
while everyone else gets the landscape one.

## Provider behavior + retry policy

Postbreeze wraps every publish in a Temporal workflow that retries up
to **5 attempts** with exponential backoff (initial 10s, max 15 min,
coefficient 2). Non-retryable validation errors (e.g. caption too
long) terminate immediately and surface as `PostStatus.FAILED`.
Network blips, rate limits, and provider 5xx's get retried
automatically.

The full retry timeline:

* Attempt 1: immediate
* Attempt 2: \~10s later
* Attempt 3: \~30s later
* Attempt 4: \~5 min later
* Attempt 5: \~15 min later

Beyond that, the post target stays in `FAILED` and you can retry
manually via `POST /api/v1/posts/:id/retry`.

## See also

* [Authentication](/authentication) — how API keys work + workspace boundaries
* [Concepts → Scheduling](/concepts/scheduling) — what `scheduledFor` really means + timezone behavior
* [Concepts → Rate limits](/concepts/rate-limits) — global rate limits across all platforms
* [Concepts → Errors](/concepts/errors) — error envelope + categories
* [MCP](/mcp/install) — let Claude / Cursor schedule posts directly
