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

# Platform settings

> Per-platform options that go beyond a caption and a few images.

Every platform has its own knobs — Instagram's REEL vs FEED, X's
`replySettings`, TikTok's privacy and consent flags, YouTube's
`madeForKids`, LinkedIn's PDF carousels. Postbreeze exposes these
through a single field on each platform target:

```ts theme={null}
platforms: [
  {
    accountId: "soc_…",
    platformOptions: {
      platform: "INSTAGRAM",
      kind: "REEL",
    },
  },
]
```

`platformOptions` is a discriminated union — the `platform` field
picks which shape applies. Pass only the fields you want to override;
everything else falls back to a sensible default.

## Target-level fields (every platform)

These live **next to** `platformOptions`, not inside it. They apply to
any platform that supports them.

| Field             | Type               | Description                                                                                                                                                                                     |
| ----------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accountId`       | `string`           | Required. The `SocialAccount.id` to publish to.                                                                                                                                                 |
| `captionOverride` | `string` (≤10,000) | Per-platform caption that overrides the post-level `content`. Useful when one platform needs a different tone, length, or set of hashtags.                                                      |
| `firstComment`    | `string` (≤2,000)  | Auto-posts as the first reply once the main post is live. Supported on **Instagram, Facebook, X, YouTube, TikTok, LinkedIn, Threads**. Ignored on platforms that don't have a comments surface. |
| `platformOptions` | object             | Platform-specific options (this page). Discriminated by `platform`.                                                                                                                             |
| `mediaIds`        | `string[]`         | Overrides the post-level media for this target only. Use when one platform needs a different crop or asset.                                                                                     |

```json theme={null}
{
  "accountId": "soc_…",
  "captionOverride": "Different caption for X — shorter, punchier.",
  "firstComment": "Drop a 🔥 if you want the deep-dive thread.",
  "platformOptions": { "platform": "X", "replySettings": "following" }
}
```

The full Zod schema lives in
[`packages/shared/src/index.ts`](https://github.com/postbreeze/postbreeze/blob/main/packages/shared/src/index.ts).
This page is the human-readable copy.

## Instagram

| Field      | Type               | Description                                                  |
| ---------- | ------------------ | ------------------------------------------------------------ |
| `platform` | `"INSTAGRAM"`      | Discriminator. Required.                                     |
| `kind`     | `"FEED" \| "REEL"` | Which Instagram surface to publish to. Defaults to `"FEED"`. |

```json theme={null}
{
  "platform": "INSTAGRAM",
  "kind": "REEL"
}
```

**Constraints**

* 📐 Feed posts require aspect ratio between **4:5 (0.8)** and **1.91:1**.
* 📱 Reels must be a single **9:16** video, 3–90 seconds.
* 🎠 Feed carousels support up to **10** media items.
* 🚫 **Stories are not supported in v1** — they're rejected at validation.
* 🚫 Mixing image + video in one Feed post is rejected by the API.
* 💬 `firstComment` is supported on Feed posts and Reels (not stories — and stories don't ship in v1 anyway).

## Facebook

| Field       | Type                                                         | Description                                                                                                                |
| ----------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `platform`  | `"FACEBOOK_PAGE"`                                            | Discriminator. Required.                                                                                                   |
| `kind`      | `"FEED" \| "PHOTO" \| "PHOTO_CAROUSEL" \| "VIDEO" \| "REEL"` | Publishing surface. Defaults to `"FEED"`. With media attached, `FEED` is auto-normalized to `PHOTO` or `PHOTO_CAROUSEL`.   |
| `link`      | `string` (URL)                                               | Optional outbound link. With `kind: "FEED"`, Facebook auto-generates the link preview card from the URL's Open Graph tags. |
| `reelTitle` | `string` (≤2,200)                                            | Optional Reel title prepended to the description on publish.                                                               |

```json theme={null}
{
  "platform": "FACEBOOK_PAGE",
  "kind": "REEL",
  "reelTitle": "Behind the launch"
}
```

**Constraints**

* 🚫 Cannot mix videos and images in the same post.
* ✅ Up to **10** images for Feed photo carousels.
* 🎬 Reels must be **9:16**, between **3 and 90 seconds**.
* 📊 Reels are capped at **30 publishes per Page per 24h** by Meta.
* 🔗 Use `link` (with `kind: "FEED"`) for OG-card link previews.
* 💬 `firstComment` is supported on every surface except Reels.

## X (Twitter)

| Field           | Type                                            | Description                                                                                                                                  |
| --------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `platform`      | `"X"`                                           | Discriminator. Required.                                                                                                                     |
| `replySettings` | `"everyone" \| "following" \| "mentionedUsers"` | Who can reply. Defaults to `"everyone"`. Enforced server-side by X.                                                                          |
| `threadParts`   | `string[]` (each ≤4,000, up to 25 items)        | Tail of a thread. When non-empty, each entry posts as a separate tweet threaded under the first. `content` is the FIRST tweet in the thread. |
| `replyToId`     | `string`                                        | Optional tweet ID — turns this post into a reply to an existing tweet.                                                                       |

```json theme={null}
{
  "platform": "X",
  "replySettings": "following",
  "threadParts": [
    "Here's the second tweet in the thread.",
    "And the third — wrapping up with the link → https://example.com"
  ]
}
```

**Constraints**

* 🖼️ Up to **4** images **or** **1** video per tweet — never a mix.
* 🧵 `threadParts` adds up to **25** tweets after the root. Each entry inherits no media; only the root tweet carries the post's `mediaItems`.
* 💬 `firstComment` is published as a reply to the **root** tweet, not the last thread part.
* 🌍 Tweet text is always globally visible — X doesn't expose geo-restriction on text.

## LinkedIn (Personal)

| Field               | Type                        | Description                                                                                                                           |
| ------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `platform`          | `"LINKEDIN_PERSON"`         | Discriminator. Required.                                                                                                              |
| `visibility`        | `"PUBLIC" \| "CONNECTIONS"` | Who can see the post. Defaults to `"PUBLIC"`.                                                                                         |
| `postAsPdfCarousel` | `boolean`                   | Composite the attached images into a single PDF and post as a "document". LinkedIn renders it as a swipeable carousel under the post. |
| `pdfCarouselTitle`  | `string` (≤100)             | Title shown above the PDF carousel. Required when `postAsPdfCarousel` is `true`.                                                      |

```json theme={null}
{
  "platform": "LINKEDIN_PERSON",
  "visibility": "PUBLIC",
  "postAsPdfCarousel": true,
  "pdfCarouselTitle": "Q2 launch retro"
}
```

**Constraints**

* 🖼️ Up to **20** images per post.
* 🚫 Multi-video posts are not supported.
* 📄 Single **PDF document** posts supported — upload the PDF and attach with `type: "document"`. See [Media uploads → LinkedIn PDFs](/concepts/media-uploads#linkedin-pdfs).
* 🔗 Link previews are auto-generated when no media is attached.

## LinkedIn (Company)

Same shape as LinkedIn Personal, with the discriminator
`platform: "LINKEDIN_COMPANY"`. Use for organization pages the
connected user administers.

| Field               | Type                        | Description                                                                               |
| ------------------- | --------------------------- | ----------------------------------------------------------------------------------------- |
| `platform`          | `"LINKEDIN_COMPANY"`        | Discriminator. Required.                                                                  |
| `visibility`        | `"PUBLIC" \| "CONNECTIONS"` | Kept for shape parity — company actors **always post publicly**, no matter what's passed. |
| `postAsPdfCarousel` | `boolean`                   | See LinkedIn Personal above.                                                              |
| `pdfCarouselTitle`  | `string` (≤100)             | Title shown above the PDF carousel.                                                       |

```json theme={null}
{
  "platform": "LINKEDIN_COMPANY",
  "postAsPdfCarousel": true,
  "pdfCarouselTitle": "Quarterly numbers"
}
```

**Constraints**

* 🏢 One `SocialAccount` per administered organization. Use the
  per-org `accountId` to fan out to multiple pages.
* 📊 Org-page analytics require LinkedIn Marketing Developer Platform
  approval — until that lands, the analytics surface shows an
  "approval pending" banner. Posting still works.

## TikTok (Personal)

<Warning>
  **Required by TikTok.** `privacy` has **no default** — you must pass
  one of the four values below. Branded-content (`brandContentToggle:
    true`) is only allowed with `privacy: "PUBLIC_TO_EVERYONE"`.
</Warning>

| Field                | Type                                                                                      | Description                                                                                                                                                              |
| -------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `platform`           | `"TIKTOK_PERSONAL"`                                                                       | Discriminator. Required.                                                                                                                                                 |
| `privacy`            | `"PUBLIC_TO_EVERYONE" \| "MUTUAL_FOLLOW_FRIENDS" \| "FOLLOWER_OF_CREATOR" \| "SELF_ONLY"` | **Required.** TikTok's Content Sharing Guidelines forbid a default here — the creator must pick a value their `creator_info` API confirmed is allowed for their account. |
| `allowComments`      | `boolean`                                                                                 | Whether viewers can comment. Defaults to `false`.                                                                                                                        |
| `allowDuet`          | `boolean`                                                                                 | Whether viewers can duet. Defaults to `false`.                                                                                                                           |
| `allowStitch`        | `boolean`                                                                                 | Whether viewers can stitch. Defaults to `false`.                                                                                                                         |
| `autoAddMusic`       | `boolean`                                                                                 | Let TikTok auto-add a recommended music track. Defaults to `false`. Photo posts only.                                                                                    |
| `brandOrganicToggle` | `boolean`                                                                                 | "Your Brand" disclosure — content promotes the creator's own brand. Defaults to `false`. Surfaced to TikTok as `brand_organic_toggle`.                                   |
| `brandContentToggle` | `boolean`                                                                                 | "Branded Content" disclosure — paid partnership / third-party brand. Defaults to `false`. Surfaced to TikTok as `brand_content_toggle`.                                  |
| `photoTitle`         | `string` (≤90 UTF-16 runes)                                                               | Photo posts only — TikTok exposes a short metadata title separate from the caption. Ignored for video posts.                                                             |

```json theme={null}
{
  "platform": "TIKTOK_PERSONAL",
  "privacy": "PUBLIC_TO_EVERYONE",
  "allowComments": true,
  "allowDuet": false,
  "allowStitch": false,
  "brandContentToggle": true
}
```

**Constraints**

* 🎬 Videos publish via `PULL_FROM_URL`; the host of the media URL must be on TikTok's verified-domain list (your R2 public bucket).
* 📸 Photo carousels support up to **35** images.
* 📝 Video captions: up to 2,200 characters. Photo titles capped at **90** characters — use `content` for longer descriptions.
* 🔒 While the app is in TikTok **Sandbox**, `privacy` is forced to `"SELF_ONLY"` regardless of what you pass — that's a TikTok constraint until App Review lands.
* 🤝 Branded content **cannot** be `SELF_ONLY`, `MUTUAL_FOLLOW_FRIENDS`, or `FOLLOWER_OF_CREATOR`. Validation rejects the combination pre-flight.

## TikTok (Business)

Same shape as TikTok Personal, with the discriminator
`platform: "TIKTOK_BUSINESS"`. Use for accounts authenticated through
TikTok for Business.

```json theme={null}
{
  "platform": "TIKTOK_BUSINESS",
  "privacy": "PUBLIC_TO_EVERYONE",
  "allowComments": true,
  "brandContentToggle": false
}
```

The reserved `TIKTOK_BUSINESS` slot exists for accounts that grant
the Business OAuth flow; the publish shape and validation rules are
identical to Personal.

## YouTube

| Field          | Type                                  | Description                                                                                                                                                                        |
| -------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `platform`     | `"YOUTUBE"`                           | Discriminator. Required.                                                                                                                                                           |
| `visibility`   | `"PUBLIC" \| "UNLISTED" \| "PRIVATE"` | Defaults to `"PUBLIC"`.                                                                                                                                                            |
| `madeForKids`  | `boolean`                             | COPPA flag — required by YouTube on every upload. Videos marked made-for-kids have restricted features (no comments, no notifications, limited ad targeting). Defaults to `false`. |
| `youtubeTitle` | `string` (≤100)                       | Optional title that overrides the first line of `content`. YouTube splits title from description; this field carries the override.                                                 |

```json theme={null}
{
  "platform": "YOUTUBE",
  "visibility": "UNLISTED",
  "madeForKids": false,
  "youtubeTitle": "Launch day — full walkthrough"
}
```

**Constraints**

* 🎬 YouTube targets accept a **single video**, no images.
* ⏱️ Videos **≤ 3 minutes** in 9:16 are automatically published as **YouTube Shorts**; longer videos publish as regular videos. Postbreeze doesn't override this — it's YouTube's classification.
* 🖼️ Custom thumbnails work for regular videos only (not Shorts).
* 💬 `firstComment` is supported and posted as a top-level comment after upload completes.
* 🚫 Tags, category overrides, AI-disclosure flag (`containsSyntheticMedia`) are not exposed in v1 — defaults are applied server-side (`categoryId: "22"` "People & Blogs").

## Pinterest

| Field      | Type            | Description                                                                               |
| ---------- | --------------- | ----------------------------------------------------------------------------------------- |
| `platform` | `"PINTEREST"`   | Discriminator. Required.                                                                  |
| `board`    | `string`        | Pinterest board ID. **Required** by the API for any pin.                                  |
| `title`    | `string` (≤100) | Optional pin title — distinct from the post `content`, which becomes the pin description. |
| `link`     | `string` (URL)  | Optional outbound link the pin opens when clicked.                                        |

```json theme={null}
{
  "platform": "PINTEREST",
  "board": "1234567890123456789",
  "title": "Spring lookbook",
  "link": "https://example.com/spring"
}
```

**Constraints**

* 📌 Every pin requires a `board`. There is no default board fallback in v1.
* 🖼️ Pinterest carousels accept up to **5** images.
* 🚫 Video pins are not supported in v1.
* 🔗 `link` becomes the pin's destination — leave it unset for image-only inspiration pins.

## Threads

| Field         | Type                                         | Description                                                                                                                                                                                                  |
| ------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `platform`    | `"THREADS"`                                  | Discriminator. Required.                                                                                                                                                                                     |
| `kind`        | `"TEXT" \| "IMAGE" \| "VIDEO" \| "CAROUSEL"` | Publishing surface. Defaults to `"TEXT"`. The compose flow normally auto-derives it from the attached media (no media → TEXT, single image → IMAGE, etc.); set it explicitly if you want to force a surface. |
| `threadParts` | `string[]` (each ≤500, up to 25 items)       | Optional thread tail. When non-empty, each entry posts as a separate Threads post chained via `reply_to_id` under the previous one. `content` is the FIRST post in the thread.                               |

```json theme={null}
{
  "platform": "THREADS",
  "kind": "TEXT",
  "threadParts": [
    "Second post in the thread — explaining the why.",
    "Third post — the punchline."
  ]
}
```

**Constraints**

* 📝 Each post (root + every follow-up) is capped at **500 characters** — Threads has no "See more" fold. The publisher rejects pre-flight if any part is over.
* 🧵 `threadParts` adds up to **25** follow-ups after the root. Each follow-up is text-only; only the root post carries the attached `mediaItems`.
* 🎠 Carousels accept **2–20** mixed items (images and videos) on the root post.
* 🎬 Videos up to **5 minutes** / **1 GB**.
* 📊 Posting limits: **250** published posts / 24h, **1,000** replies / 24h. Each follow-up counts toward the reply quota.
* 💬 `firstComment` is posted as a reply to the **root** post, not the last thread part.

## Cross-platform example

One post fanned out to four platforms, each with its own
`platformOptions`, per-target caption override, and first-comment:

```ts theme={null}
await postbreeze.posts.create({
  content: "We just launched 🚀",
  mediaItems: [{ url: heroUrl, type: "image" }],
  platforms: [
    {
      accountId: "soc_ig_…",
      firstComment: "Link in bio! 🔗",
      platformOptions: { platform: "INSTAGRAM", kind: "FEED" },
    },
    {
      accountId: "soc_x_…",
      captionOverride: "We just launched 🚀 → https://example.com",
      platformOptions: {
        platform: "X",
        replySettings: "everyone",
        threadParts: [
          "Here's what's new — a thread 🧵",
          "Schedule once, publish everywhere. No spreadsheet required.",
        ],
      },
    },
    {
      accountId: "soc_li_…",
      firstComment: "What do you think? Drop a comment below 👇",
      platformOptions: {
        platform: "LINKEDIN_PERSON",
        visibility: "PUBLIC",
      },
    },
    {
      accountId: "soc_tt_…",
      platformOptions: {
        platform: "TIKTOK_PERSONAL",
        privacy: "PUBLIC_TO_EVERYONE",
        allowComments: true,
        allowDuet: false,
        allowStitch: false,
      },
    },
  ],
});
```

**Defaults that kick in if you omit `platformOptions`**

* Instagram → `kind: "FEED"`
* Facebook → `kind: "FEED"`
* X → `replySettings: "everyone"`
* LinkedIn (Personal/Company) → `visibility: "PUBLIC"`
* YouTube → `visibility: "PUBLIC"`, `madeForKids: false`
* Threads → `kind` derived from media

The exceptions are **TikTok** (`privacy` has no default) and
**Pinterest** (`board` has no default) — those two targets require
`platformOptions` with at least the required field, or the post is
rejected at validation.
