> ## Documentation index
> Fetch the complete documentation index at: https://dash.robopost.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Create posts

`POST /v1/scheduled_posts` creates one post per channel you list, all with the same content, scheduled for
the same time. You get back every post's ID and its full record.

**You need:** an [API key](https://dash.robopost.app/docs/api/authentication.md) and the IDs of the channels to post to
(`GET /v1/channels`).

## Schedule a post

```bash
curl -X POST https://public-api.robopost.app/v1/scheduled_posts \
  -H "Authorization: Bearer $ROBOPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: launch-2026-11-20-facebook-linkedin" \
  -d '{
    "text": "We just launched! https://example.com/launch",
    "channel_ids": ["<facebook channel id>", "<linkedin channel id>"],
    "image_object_ids": ["<media id>"],
    "schedule_at": "2026-11-20T15:00:00Z"
  }'
```

The response holds one entry per channel:

```json
{
  "scheduled_posts": [ { "id": "8c1f…", "channel_ids": ["<facebook channel id>"], "…": "…" } ],
  "posts": [
    {
      "id": "8c1f…",
      "revision": 2,
      "status": "scheduled",
      "channel_id": "<facebook channel id>",
      "social_network": "FACEBOOK",
      "schedule_at": "2026-11-20T15:00:00.000Z",
      "media": { "ready": true, "image_object_ids": ["<media id>"], "…": "…" },
      "…": "…"
    }
  ]
}
```

`posts` is the full record described in [List and read posts](https://dash.robopost.app/docs/api/list-and-read-posts.md); keep each
`id` to follow the post. `scheduled_posts` is the older, shorter shape, kept for existing integrations.

> **Tip:** always send an `Idempotency-Key`. If the connection drops before you get the answer, send the
> same request again with the same key: you get the original result instead of duplicate posts. See
> [Idempotency](https://dash.robopost.app/docs/api/idempotency.md).

## Add media

Use media from the team's library, or let Robopost download it from a public URL:

| Field | What to send |
| --- | --- |
| `image_object_ids` | IDs returned by [media upload](https://dash.robopost.app/docs/api/media.md) or found in the media library |
| `video_object_id` | One video ID |
| `gif_object_id` | One GIF ID |
| `image_urls`, `video_url`, `gif_url` | Public URLs pointing straight at the file |

With URLs, posts start in status `preparing_media` while Robopost downloads the files, then move to
`scheduled`. If a download fails, the post becomes `failed` with code `MEDIA_DOWNLOAD_FAILED` and the
reason; it is never silently dropped. Media IDs from another team are refused with `MEDIA_NOT_FOUND`.

## Set network options

Each network has its own optional settings object. Send only the ones you need:

```json
{
  "text": "Behind the scenes",
  "channel_ids": ["<youtube channel id>", "<tiktok channel id>"],
  "video_object_id": "<video id>",
  "schedule_at": "2026-11-20T19:00:00Z",
  "youtube_settings": { "videoTitle": "Behind the scenes", "videoType": "short", "videoPrivacyStatus": "public" },
  "tiktok_settings": { "title": "Behind the scenes", "privacyLevel": "PUBLIC_TO_EVERYONE" },
  "first_comment": "Full video on our channel",
  "made_with_ai": false
}
```

| Object | Common fields |
| --- | --- |
| `youtube_settings` | `videoTitle`, `videoDescription`, `videoType` (`video` or `short`), `videoPrivacyStatus`, `selfDeclaredMadeForKids` |
| `tiktok_settings` | `title`, `privacyLevel`, `disableComment`, `disableDuet`, `disableStitch`, `autoAddMusic` |
| `instagram_settings` | `postType`, `locationId` |
| `facebook_settings` | `postType` |
| `pinterest_settings` | `pinTitle`, `destinationLink` |
| `wordpress_settings` | `postTitle`, `postText`, `postSlug`, `postType`, `postCategories`, `postTags` |
| `gmb_settings` | `postTopicType`, offer and event fields |

The OpenAPI description (`/openapi.json`) lists every field and its allowed values. `first_comment` posts a
comment under the post right after it publishes, on networks that support it. `made_with_ai` sets the
network's AI label where the network allows apps to (TikTok and X).

## Post now, draft or collection

- **Publish as soon as possible:** set `schedule_at` to the current time or earlier.
- **Draft:** `"is_draft": true`. One draft is created for all the channels listed; it isn't published.
  A single-channel draft can be scheduled later with [`PATCH`](https://dash.robopost.app/docs/api/edit-and-delete-posts.md).
- **Collection post:** `"post_collection_id": "<id>"` adds the post to an evergreen
  [collection](https://dash.robopost.app/docs/posts/collections.md) instead of scheduling it.

## Repeat a post

```json
{ "is_recur": true, "recur_interval": "WEEKLY", "recur_until_dt_enabled": true, "recur_until_dt": "2027-03-01T00:00:00Z" }
```

`recur_interval` is one of `EVERY_3_HOURS`, `EVERY_6_HOURS`, `BI_DAILY`, `DAILY`, `WEEKLY`, `MONTHLY`,
`DAILY_SPECIFIC_TIME_SLOTS` (with `daily_recur_interval_time_slots`, for example `["09:00", "18:00"]`) or
`WEEKLY_SPECIFIC_TIME_SLOTS` (with `weekly_recur_interval_time_slots`). Each repetition is a new post.

## When creation is refused

| Status and code | Fix |
| --- | --- |
| `400 CHANNEL_NOT_FOUND` | The channel ID isn't one of this team's live channels |
| `400 CHANNEL_IDS_REQUIRED` | List at least one channel, or set `is_draft` |
| `400 MEDIA_NOT_FOUND` | A media ID isn't in this team's library |
| `409 PLAN_LIMIT_HIT` | The plan's scheduled-post limit is reached |
| `409 ACCOUNT_USAGE_FROZEN` | The subscription isn't active |
| `422` | The body doesn't match the schema; `detail` says which field |

## Next

[List and read posts](https://dash.robopost.app/docs/api/list-and-read-posts.md)
