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 in a series' destinationAccounts, keyed by platform ({"youtube": "<id>"]}, see [series).

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

The video must be rendered first (renderedVideoUrl present; see 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.

A YouTube channel gets each video once. Publishing a video to a channel that already has it (through any account record of that channel, and whether it was posted over the API, by a schedule or from the editor) answers 409 with type conflict and uploads nothing. To post a second copy on purpose, send "allowRepost": true. The same 409 answers while that video is still uploading to that channel; wait a minute and check the channel before retrying.

Instagram, Facebook, X, LinkedIn and Threads follow the same rule per connected account: a video that account already has (posted by a schedule, the editor or the API) answers 409 with type conflict until you send "allowRepost": true, and the same 409 answers while that video is still being posted to that account.

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.

Post now (POST /posts/post-now, scope posts:write)

Brings a scheduled video forward to now. Every pending posting task of the video moves to the current time and goes out within a minute or two, to every platform it was scheduled for; if the video is a series episode whose render is still waiting for its slot, the render runs first and the posts follow. The id is the video id you scheduled (or the episode's video id from GET /calendar or GET /series/{id}/videos).

bash
curl -s -X POST https://faceless.so/api/v1/posts/post-now \
  -H "Authorization: Bearer $FACELESS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"videoId": "665f1b2a9c31a2b3c4d5e801"}'

It only moves what is pending: a video that was never scheduled needs POST /posts, a video that still needs your approval answers 409 conflict (approve it first), and an episode of a deleted series, or of one paused for any reason but credits, answers 409 conflict (resume the series first; a series paused for credits still posts what is already paid for, so it works there). When nothing is pending it answers 400 invalid_input. 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 now <videoId>
faceless posts cancel <videoId>
faceless calendar --start-date 2026-08-01 --end-date 2026-08-31 --json