# Platform settings

How platformSpecificData and the root-level tiktokSettings and facebookSettings objects resolve on POST /v1/posts, and which value wins when both are set.

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

Set per-platform options in one `POST /v1/posts` call, and know which value wins when the same option is set in two places.

## Before you start

An API key and a connected account. Accepted fields: "Platform fields" on each [platform page](/platforms), or all 15 schemas (Discord and Slack included) in [create post](/posts/create-post).

## Set an option on one platform

Put options in `platformSpecificData` on a `platforms[]` entry. Here, Instagram links go in the first comment.
<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: created } = await zernio.posts.createPost({
  body: {
    content: 'Golden hour at the pier.',
    mediaItems: [{ url: 'https://cdn.example.com/pier.jpg', type: 'image' }],
    platforms: [
      {
        platform: 'instagram',
        accountId: '66b2e19d8c3f5a7e9d0b1c2e',
        platformSpecificData: { firstComment: 'Docs link in bio.' }
      }
    ],
    scheduledFor: '2027-01-01T12:00:00',
    timezone: 'America/New_York'
  }
});

console.log(created.post.status);
```
</Tab>
<Tab value="Python">
```python
from zernio import Zernio

client = Zernio()

created = client.posts.create_post(
    content="Golden hour at the pier.",
    media_items=[{"url": "https://cdn.example.com/pier.jpg", "type": "image"}],
    platforms=[
        {
            "platform": "instagram",
            "accountId": "66b2e19d8c3f5a7e9d0b1c2e",
            "platformSpecificData": {"firstComment": "Docs link in bio."},
        }
    ],
    scheduled_for="2027-01-01T12:00:00",
    timezone="America/New_York",
)

print(created["post"]["status"])
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/posts" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Golden hour at the pier.",
    "mediaItems": [{ "url": "https://cdn.example.com/pier.jpg", "type": "image" }],
    "platforms": [
      {
        "platform": "instagram",
        "accountId": "66b2e19d8c3f5a7e9d0b1c2e",
        "platformSpecificData": { "firstComment": "Docs link in bio." }
      }
    ],
    "scheduledFor": "2027-01-01T12:00:00",
    "timezone": "America/New_York"
  }'
```
</Tab>
</Tabs>

Response (`201`):

```json
{
  "message": "Post scheduled successfully",
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "scheduled",
    "scheduledFor": "2027-01-01T17:00:00Z",
    "platforms": [
      { "platform": "instagram", "status": "pending" }
    ]
  }
}
```

## Send different options to several platforms

One post goes to three platforms:

- **X** (platform value `twitter`) as a thread
- **Instagram** with a first comment
- **TikTok** with the consent flags TikTok requires

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: launch } = await zernio.posts.createPost({
  body: {
    content: 'Our API launch, in 3 posts.',
    mediaItems: [{ url: 'https://cdn.example.com/launch.mp4', type: 'video' }],
    platforms: [
      {
        platform: 'twitter',
        accountId: '66b2e19d8c3f5a7e9d0b1c2d',
        platformSpecificData: {
          threadItems: [
            { content: 'Our API launch, in 3 posts.' },
            { content: '1/ Authentication is one API key.' },
            { content: '2/ Your first post is one call.' }
          ]
        }
      },
      {
        platform: 'instagram',
        accountId: '66b2e19d8c3f5a7e9d0b1c2e',
        platformSpecificData: { firstComment: 'Docs link in bio.' }
      },
      { platform: 'tiktok', accountId: '66b2e19d8c3f5a7e9d0b1c2f' }
    ],
    tiktokSettings: {
      privacy_level: 'PUBLIC_TO_EVERYONE',
      allow_comment: true,
      allow_duet: true,
      allow_stitch: true,
      content_preview_confirmed: true,
      express_consent_given: true
    },
    scheduledFor: '2027-01-01T12:00:00',
    timezone: 'America/New_York'
  }
});

console.log(launch.post.status);
```
</Tab>
<Tab value="Python">
```python
launch = client.posts.create_post(
    content="Our API launch, in 3 posts.",
    media_items=[{"url": "https://cdn.example.com/launch.mp4", "type": "video"}],
    platforms=[
        {
            "platform": "twitter",
            "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
            "platformSpecificData": {
                "threadItems": [
                    {"content": "Our API launch, in 3 posts."},
                    {"content": "1/ Authentication is one API key."},
                    {"content": "2/ Your first post is one call."},
                ]
            },
        },
        {
            "platform": "instagram",
            "accountId": "66b2e19d8c3f5a7e9d0b1c2e",
            "platformSpecificData": {"firstComment": "Docs link in bio."},
        },
        {"platform": "tiktok", "accountId": "66b2e19d8c3f5a7e9d0b1c2f"},
    ],
    tiktok_settings={
        "privacy_level": "PUBLIC_TO_EVERYONE",
        "allow_comment": True,
        "allow_duet": True,
        "allow_stitch": True,
        "content_preview_confirmed": True,
        "express_consent_given": True,
    },
    scheduled_for="2027-01-01T12:00:00",
    timezone="America/New_York",
)

print(launch["post"]["status"])
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/posts" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Our API launch, in 3 posts.",
    "mediaItems": [{ "url": "https://cdn.example.com/launch.mp4", "type": "video" }],
    "platforms": [
      {
        "platform": "twitter",
        "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
        "platformSpecificData": {
          "threadItems": [
            { "content": "Our API launch, in 3 posts." },
            { "content": "1/ Authentication is one API key." },
            { "content": "2/ Your first post is one call." }
          ]
        }
      },
      {
        "platform": "instagram",
        "accountId": "66b2e19d8c3f5a7e9d0b1c2e",
        "platformSpecificData": { "firstComment": "Docs link in bio." }
      },
      { "platform": "tiktok", "accountId": "66b2e19d8c3f5a7e9d0b1c2f" }
    ],
    "tiktokSettings": {
      "privacy_level": "PUBLIC_TO_EVERYONE",
      "allow_comment": true,
      "allow_duet": true,
      "allow_stitch": true,
      "content_preview_confirmed": true,
      "express_consent_given": true
    },
    "scheduledFor": "2027-01-01T12:00:00",
    "timezone": "America/New_York"
  }'
```
</Tab>
</Tabs>

Response (`201`):

```json
{
  "message": "Post scheduled successfully",
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "scheduled",
    "scheduledFor": "2027-01-01T17:00:00Z",
    "platforms": [
      { "platform": "twitter", "status": "pending" },
      { "platform": "instagram", "status": "pending" },
      { "platform": "tiktok", "status": "pending" }
    ]
  }
}
```

<Callout type="warn">
TikTok publishes only with `content_preview_confirmed` and `express_consent_given` `true` and a `privacy_level` from the account's [creator info](/accounts/get-tiktok-creator-info). None of the three has a default.
</Callout>

## How it behaves

**`platformSpecificData` belongs to one platform entry.**

- **The entry's `platform` picks the shape.** One key can mean different things on two entries of the same request.
- **Misplaced fields are ignored.** A field at the body root or on the wrong entry is dropped; the post is still created and the `201` lists it in `warnings`:

```json
{
  "message": "Post scheduled successfully",
  "post": { "_id": "65f1c0a9e2b5af0012ab34cd", "status": "scheduled" },
  "warnings": [
    "Ignored root-level \"platformSpecificData\": it is only read per platform, as platforms[].platformSpecificData."
  ]
}
```

- **Three siblings override the top-level request:** `customContent` replaces `content`, `customMedia` replaces `mediaItems` ([media uploads](/guides/media-uploads)), and a per-platform `scheduledFor` replaces the top-level scheduled time.

**Root-level settings merge in and lose ties.**

- **`tiktokSettings` and `facebookSettings`** can go at the root. Each merges into every entry of that platform, as `platformSpecificData.tiktokSettings` or `platformSpecificData.facebookSettings`.
- **The entry wins**, key by key: entry keys keep their value, root-only keys are copied in.
- **Facebook:** `draft`, `carouselCards` and colored-background options live only inside `facebookSettings`. Every other Facebook field sits directly on `platformSpecificData`.
- **TikTok** reads nested `platformSpecificData.tiktokSettings`, then flat TikTok fields on `platformSpecificData`, which override it.
- **Both cases accepted:** snake_case and camelCase (`privacy_level`, `privacyLevel`).

**An update replaces a whole namespace.** On [`PUT /v1/posts/{postId}`](/posts/update-post):

- A `<platform>Settings` namespace you leave out is kept from the stored post.
- One you send replaces it whole (no deep merge): send every key to keep.
- A root-level namespace sent without a `platforms` array returns `400`.

## If it fails

| Error | Cause | Fix |
|---|---|---|
| `400` `invalid_field_value` | A `platformSpecificData` value the platform rejects; `code` and `param` name it (below). | Fix it and resend. A `400` creates nothing. |
| `400` on `PUT /v1/posts/{postId}` | A root-level namespace sent without a `platforms` array. | Include `platforms`. |

```json
{
  "error": "userTags[0].x must be between 0 and 1",
  "type": "invalid_request_error",
  "code": "invalid_field_value",
  "param": "platforms[1].platformSpecificData.userTags[0].x"
}
```

The [error handling guide](/guides/error-handling) describes the envelope.

## Related

- [Create post](/posts/create-post): every field for every platform, including Discord and Slack.
- [Platforms](/platforms): what each platform does with these fields, and its media limits.
- [Media uploads](/guides/media-uploads): `mediaItems`, `customMedia` and thumbnails.
- [Post lifecycle](/guides/post-lifecycle): per-platform status after publishing.