# Publish to social networks

> Post one or several edited clips to multiple accounts at once, now or scheduled, and follow each publication.

A **post** publishes edits to connected accounts. Each edit and account pair becomes an **item**, with its own state: in a post to TikTok and YouTube, one item can go out and the other fail.

You do not need to render first. The post renders what is missing and publishes.

## Choose the accounts

```bash
curl https://api.cut.pro/api/v1/connections \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

Each connection carries `id`, `platform`, `username` and audience numbers. The `id` is the post's `connection_id`. If the account the person wants is not in the list, request the connection link and hand it to them:

```bash
curl "https://api.cut.pro/api/v1/connections/link?platform=tiktok" \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

The connection is made by the account owner, in the browser. When they come back, list again.

## Create the post

```bash
curl -X POST https://api.cut.pro/api/v1/posts \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "videos": [
      {
        "edit_id": "7412910087700001",
        "targets": [
          {
            "connection_id": "7390012299881100",
            "metadata": { "tiktok": { "title": "The chorus everyone knows #music", "privacyLevel": "PUBLIC_TO_EVERYONE" } }
          },
          {
            "connection_id": "7390012299881200",
            "metadata": { "youtube": { "title": "The chorus everyone knows", "categoryId": "10", "privacyStatus": "public" } }
          }
        ]
      }
    ],
    "scheduled_at": "2026-10-30T12:00:00.000Z"
  }'
```

```json title="201 Created"
{ "id": "7412930011223344", "item_count": 2, "scheduled_at": "2026-10-30T12:00:00.000Z", "status": "pending" }
```

- `edit_id` comes from the clip, the render or a [generated video](/en/generations). Several `videos` in a single post publish several clips at once.
- `scheduled_at` schedules it, in ISO 8601 in the future. Without it, it publishes now.

## Metadata per network

`metadata` takes a key with the network's name and, inside it, the network's own fields, with its own names (`privacyLevel`, `categoryId`). It is the only part of the API outside `snake_case`: the content goes to the network as is.

| Network | Required | Other fields |
|---|---|---|
| `youtube` | `title`, `categoryId`, `privacyStatus` | `description`, `tags`, `madeForKids`, `notifySubscribers`, `license`, `embeddable` |
| `tiktok` | `title`, `privacyLevel` | `disableDuet`, `disableComment`, `disableStitch`, `brandContentToggle`, `brandOrganicToggle`, `isAigc` |
| `instagram` | | `caption`, `shareToFeed`, `collaborators`, `userTags`, `audioName`, `thumbOffset`, `trialReel`, `firstComment` |
| `facebook` | | `description`, `postType` (`reel` or `feed`), `videoTitle`, `link`, `firstComment` |
| `threads` | | `text`, `replyControl`, `firstComment` |
| `twitter` | | `text`, `replySettings` |
| `linkedin` | `text` | `visibility` |
| `pinterest` | `boardId`, `title`, `description` | `link`, `coverImageUrl` |
| `bluesky` | | `text`, `languages` |

All of them accept `coverImageId` for the cover. The limits of each field, and the other networks, are in [`POST /posts`](/reference). A network that is still on its way rejects the whole request with `400 PLATFORM_UNAVAILABLE`, before creating anything.

## Follow along

```bash
curl https://api.cut.pro/api/v1/posts/7412930011223344 \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

Each item goes through `pending` → `rendering` → `publishing` → `completed` or `failed`. On `completed`, `published_url` is the video's link on the network. On `failed`, `error_code` says the reason.

The post summarizes the items:

| Post `status` | Means |
|---|---|
| `pending` | Scheduled or waiting to start. |
| `processing` | Items rendering or publishing. |
| `completed` | All items published. |
| `partial` | Some published, some failed. Treat it as a warning, not as success. |
| `failed` | No item published. |
| `cancelled` | Canceled. |

Poll every 10 to 30 seconds. An item that failed goes back to the queue with:

```bash
curl -X POST https://api.cut.pro/api/v1/posts/7412930011223344/items/7412930011223401/retry \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

## Change, bring forward or cancel

| Route | What it does |
|---|---|
| `PATCH /posts/{id}` | Changes `scheduled_at` (at least 1 minute ahead, or `null` to publish now) while every item is `pending`. With `items`, replaces the set of pending items: with `item_id` it updates, without it inserts, absent deletes. Published items do not change. |
| `POST /posts/{id}/publish` | Publishes a scheduled post now, or unblocks one that got stuck. |
| `DELETE /posts/{id}` | Cancels every item that has not gone out yet. What is already live stays live. |
| `DELETE /posts/{id}/items/{itemId}` | Cancels a `pending`, `rendering` or `failed` item. On a `completed` item, deletes the video from the network, on the networks that allow it. An item in `publishing` can no longer be stopped. |

> Deleting a published item removes the video from the person's account on the network. If the network refuses, the response is `400` with the reason (`DELETE_NOT_SUPPORTED`, `PERMISSION_MISSING`, `DELETE_LIMIT_REACHED`) and nothing changes.

## Errors

| Code | When |
|---|---|
| `INVALID_METADATA` | A network field is wrong or missing. `extra.field` and `extra.platform` say which. |
| `INVALID_CONNECTIONS` | Nonexistent or disconnected connection. `extra.invalid_connections` lists which. Ask to reconnect with `GET /connections/link`. |
| `EDITS_NOT_FOUND` | An edit that is not from this workspace, in `extra.invalid_edits`. |
| `DAILY_LIMIT_EXCEEDED` | The account exceeded the network's daily post limit. `extra` carries `daily_limit`, `used_today` and the connection. |
| `DUPLICATE_POST` | The same request (same edits, accounts and time) arrived again right after the first. The first one stands: do not repeat it. |
| `MEDIA_REFUNDED` | The edit is from an AI-generated video that was refunded. |
| `RECORDING_EXPIRED` | The edit is from a live clip whose recording expired. |
| `INVALID_SCHEDULED_AT` | Time in the past or outside the ISO 8601 format. |
