Skip to main content

Posts and scheduling

POST /posts schedules a post to one or more connected accounts. It needs the posts:write scope, is rate limited to 60 requests per 300 seconds, and supports the Idempotency-Key header.

Three content sources

The body must provide exactly one content source; they are checked in this order:

  1. suggestionId: publish a generated suggestion from POST /generations (the usual path)
  2. deck + format: publish a raw deck object (advanced; format is required when deck is provided)
  3. videoUrl or images: publish your own media as-is; URLs must be publicly reachable (thumbnailUrl is optional for videos)

caption and hashtags are optional; for suggestion-based posts they default to the suggestion's own values.

Target accounts

selectedAccounts is required: an object of platform keys mapping to arrays of account ids from GET /accounts, with at least one non-empty platform array.

json
{
	"selectedAccounts": {
		"tiktok": ["665f1b2a9c31a2b3c4d5e700"],
		"youtube": ["665f1b2a9c31a2b3c4d5e711"]
	}
}

Connecting accounts (OAuth) happens in the web app, not through the API. Other optional fields: ownerType (brand or influencer; influencerId is required when influencer), tiktokSendToInbox to send a TikTok draft instead of publishing, and workspaceId.

asap vs scheduled

  • schedule: "asap" posts as soon as the render is ready
  • schedule: "scheduled" requires a scheduledTime in the future; pass timezone as an IANA name (default UTC)
bash
viraloop posts create --suggestion <id> --accounts tiktok:<accountId> \
  --at "2026-07-04T15:00:00Z" --json

202 and polling

Deck-based posts are rendered server-side after the call returns, so the response is 202 with a statusUrl:

json
{
	"success": true,
	"data": {
		"id": "665f1b2a9c31a2b3c4d5e702",
		"status": "scheduled",
		"renderStatus": "pending",
		"statusUrl": "/api/v1/posts/665f1b2a9c31a2b3c4d5e702"
	}
}

Poll GET /posts/{id} (scope posts:read) until a terminal state:

  • Terminal status values: posted, partial, completed, failed
  • A renderStatus of failed is also terminal

The post includes per-platform results: status, postUrl and an analytics snapshot (views, likes, comments, shares). With the CLI, viraloop posts create --wait does the polling for you; do not use --wait for far-future posts, it waits until the post actually publishes.

Cancelling

DELETE /posts/{id} cancels a post that has not been published yet (status scheduled). Published or in-flight posts cannot be cancelled; that returns 409 with error type conflict.

bash
viraloop posts cancel <postId> --json

Calendar

GET /calendar returns the workspace's posts in a date window; from and to are required (ISO 8601), status is optional. Check what is already queued before scheduling more:

bash
viraloop calendar --from 2026-07-06 --to 2026-07-13 --json

Idempotency

Send an Idempotency-Key header (any unique string) on POST /posts to make retries safe: a successful response is stored and replayed if you retry with the same key and body, so a network timeout cannot double-post. Reusing a key with a different body returns 409. See errors and rate limits.