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:
suggestionId: publish a generated suggestion fromPOST /generations(the usual path)deck+format: publish a raw deck object (advanced;formatis required whendeckis provided)videoUrlorimages: publish your own media as-is; URLs must be publicly reachable (thumbnailUrlis 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.
{
"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 readyschedule: "scheduled"requires ascheduledTimein the future; passtimezoneas an IANA name (default UTC)
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:
{
"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
statusvalues:posted,partial,completed,failed - A
renderStatusoffailedis 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.
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:
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.