# Publishing and scheduling

A rendered video can go out two ways: `POST /posts` publishes to one platform right now; `POST /posts/schedule` queues one or more platforms for a future time. Both are free. Supported platforms: `youtube`, `tiktok`, `instagram`, `x`, `facebook`, `linkedin`, `threads`.

Publishing requires a connected account for the platform. Connecting accounts (OAuth) happens in the dashboard, not over the API.

## Who can you post as? (GET /accounts, scope accounts:read)

```bash
curl -s https://faceless.so/api/v1/accounts \
  -H "Authorization: Bearer $FACELESS_API_KEY"
```

```json
{
  "success": true,
  "data": { "youtube": [{ "id": "665f1b2a9c31a2b3c4d5ee01", "channelName": "Deep Sea Facts" }], "tiktok": [], "instagram": [] }
}
```

Use an account's `id` as `authId` in `POST /posts` when the team has several accounts on one platform, and as `destinationAccounts` in series.

## Publish now (POST /posts, scope posts:write)

The video must be rendered first (`renderedVideoUrl` present; see [videos](/developers/docs/videos)). One platform per call:

```bash
curl -s -X POST https://faceless.so/api/v1/posts \
  -H "Authorization: Bearer $FACELESS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"videoId": "665f1b2a9c31a2b3c4d5e801", "platform": "youtube", "title": "The ocean has rivers underwater"}'
```

```json
{
  "success": true,
  "data": { "platform": "youtube", "videoId": "665f1b2a9c31a2b3c4d5e801", "postUrl": "https://youtube.com/shorts/dQw4w9WgXcQ" }
}
```

`title` and `description` are optional; when omitted the video's stored post metadata is used. `privacyStatus` (`public`, `unlisted`, `private`) applies to YouTube only. Rate limit: 10 per 300s.

## Schedule for later: metadata first, then schedule

Scheduling posts to multiple platforms at once, at a future time, and renders the video first if needed. It validates that every platform in the list already has its post metadata stored on the video, so the flow is always two steps.

### 1. Set per-platform metadata (PATCH /videos/{id}, scope videos:write)

```bash
curl -s -X PATCH https://faceless.so/api/v1/videos/665f1b2a9c31a2b3c4d5e801 \
  -H "Authorization: Bearer $FACELESS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "youtubePost": {"title": "The ocean has rivers underwater", "privacyStatus": "public"},
    "tiktokPost": {"title": "underwater rivers are real #ocean"}
  }'
```

The blocks per platform: `youtubePost` (`title` max 100, optional `description`, `privacyStatus`), `tiktokPost` (`title`), `instagramPost` (`caption`), `xPost` (`text` max 280), `facebookPost` (`description`), `linkedinPost` (`description`), `threadsPost` (`text` max 500).

### 2. Schedule (POST /posts/schedule, scope posts:write)

```bash
curl -s -X POST https://faceless.so/api/v1/posts/schedule \
  -H "Authorization: Bearer $FACELESS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"videoId": "665f1b2a9c31a2b3c4d5e801", "platforms": ["youtube", "tiktok"], "scheduledTime": "2026-08-01T18:00:00Z"}'
```

`scheduledTime` is ISO 8601 and must be in the future. A platform without its metadata block set is rejected with `invalid_input`; set it in step 1 and retry. Rate limit: 20 per 300s.

## Cancel (DELETE /posts/{id}, scope posts:write)

The id is the video id you scheduled. Cancels the pending scheduled posting; posts that already went out are unaffected. Cancelling something no longer scheduled returns `409 conflict`.

```bash
curl -s -X DELETE https://faceless.so/api/v1/posts/665f1b2a9c31a2b3c4d5e801 \
  -H "Authorization: Bearer $FACELESS_API_KEY"
```

## See what is queued (GET /calendar, scope posts:read)

Scheduled, posted and failed posts across all platforms in a date range. Check it before scheduling more:

```bash
curl -s "https://faceless.so/api/v1/calendar?startDate=2026-08-01&endDate=2026-08-31&platform=youtube" \
  -H "Authorization: Bearer $FACELESS_API_KEY"
```

Optional filters: `platform` and `status` (`scheduled`, `posted`, `failed`).

## With the CLI

```bash
faceless posts publish --video-id <videoId> --platform youtube --json
faceless videos update <videoId> --youtube-title "..." --tiktok-title "..."
faceless posts schedule --video-id <videoId> --platforms youtube,tiktok --scheduled-time "2026-08-01T18:00:00Z" --json
faceless posts cancel <videoId>
faceless calendar --start-date 2026-08-01 --end-date 2026-08-31 --json
```
