suggestionId | string | Optional | Source 1. Accepts the suggestion and posts it in one call. Format: id. |
|---|
contentId | string | Optional | Source 2. An already-accepted content piece. Format: id. |
|---|
deck | object | Optional | Source 3. A render blueprint. Requires `format`, and returns 202. |
|---|
format | enum | Optional | Required with `deck`, ignored otherwise. One of: walloftext, slideshow, greenscreen, talkinghead, ugc, characterswap, listicle, ranking, askmeanything, splitscreen, grid2x2, fadein, hookdemo, interview, productspokesperson, greenscreenmobile, clonevideo, statvideo. |
|---|
videoUrl | string | Optional | Source 4. Media you already have. Fetched into our own storage before the post publishes, so the post does not depend on your URL still answering at its scheduled time. Returns 202. Format: uri. |
|---|
images | string[] | Optional | Source 4. Image URLs, fetched into our own storage the same way as `videoUrl`. Every entry is stored, and only the FIRST becomes the post media, which is the one asset every selected platform receives. There is no photo carousel in this build: all three clients publish through their video endpoints, so an image post sends a single still on a video call and the entries after the first reach no platform at all. `POST /posts/preview` names them in `notSent`. Returns 202. |
|---|
thumbnailUrl | string | Optional | Cover frame. Only read alongside `videoUrl`. Format: uri. |
|---|
selectedAccounts | object | Required | Platform to account ids, for example {"tiktok":["ff9d429b-07c5-4320-a98c-ac6e1791b828"]}. At least one list must be non-empty. |
|---|
schedule | enum | Optional | When this post goes out, and whether it goes out at all yet. `asap` publishes as soon as the render finishes and the post reads `queued`. `scheduled` publishes at `scheduledTime`. `draft` saves it unfinished: the post reads `draft`, no publisher is queued for it, and **nothing sends it until you call POST /posts/{id}/publish**. A draft may carry a `scheduledTime` as well, and releasing it then sends it at that instant rather than at once, which is what lets you pencil in a slot for a post you have not finished writing. Default asap. One of: asap, scheduled, draft. |
|---|
scheduledTime | string | Optional | ISO 8601, in the future. Required when `schedule` is `scheduled`, and optional when it is `draft`. Format: date-time. |
|---|
timezone | string | Optional | IANA zone the schedule is read in, for example Europe/Lisbon. Default UTC. Format: timezone. |
|---|
caption | string | Optional | Overrides the platform body. Validated before the post is accepted against the limit of each platform that receives it, which is every selected platform unless `perPlatform` gives one its own words. |
|---|
hashtags | string[] | Optional | Appended to the caption, per platform convention. |
|---|
perPlatform | object | Optional | The words one platform gets instead of `caption` and `hashtags`, keyed by platform: `{"tiktok":{"caption":"…","hashtags":["…"]},"youtube":{"title":"…"}}`. Every field inside is optional and leaving one out means that platform inherits the post’s. A platform you do not name here publishes exactly what it always did, so a body without this field behaves identically to one written before it existed. `title` is only accepted for a platform that has a title field, which is YouTube; a title for TikTok or Instagram is a 400 rather than a value we store and never send. Naming a platform this post does not publish to is a 400 for the same reason. This is what makes a caption legal that one platform would refuse: the limit is checked against the text each account actually receives, so a 3,000-unit YouTube caption with a shorter TikTok one beside it is accepted where the single caption was not. It is keyed by platform and not by account: two TikTok accounts in one workspace get one TikTok caption. `firstComment` is the fourth field inside a block: the comment we post underneath the moment that network has the post, which is where hashtags go if you want the caption to read as a sentence. It is accepted for Instagram and YouTube and is a 400 for TikTok naming the platform, because TikTok publishes no comment-write endpoint in any public API, not a limit of this build, and no release of it will change the answer. It is also a 400 on a target set to `draft` or `inbox`, because a comment goes under a live post and neither of those produces one. Read `firstComment` on each target of `GET /posts/{id}` to see whether it landed: it is a SECOND call to the network, made after the post is live, so it can fail on its own, and when it does the post stays `posted` and that field carries the reason. |
|---|
ownerType | enum | Optional | Whose accounts this publishes from. Default brand. One of: brand, influencer. |
|---|
influencerId | string | Optional | Required when `ownerType` is `influencer`. Format: id. |
|---|
publishMode | enum | Optional | How far to take the publish. `publish` goes live. `draft` uploads the video and leaves it unpublished for you to finish, which only YouTube can do: it becomes a private upload in YouTube Studio. `inbox` delivers the video to the creator’s app as a notification, which only TikTok can do. One value applies to the whole post, so a post that names a YouTube and a TikTok account and asks for `draft` is a 400 naming TikTok, not a post that holds one back and publishes the other. Set `tiktokSendToInbox` alongside `draft` to hold the YouTube upload back and route the TikTok copy to the inbox, which is the closest TikTok gets to not publishing. Default publish. One of: publish, draft, inbox. |
|---|
tiktokSendToInbox | boolean | Optional | The TikTok-scoped spelling of `publishMode: "inbox"`, and it applies only to this post’s TikTok target. The video arrives in your TikTok notifications rather than the Drafts folder, and it carries no caption: TikTok’s inbox endpoint has no caption field, so the generated caption and hashtags stay on this post and you paste them in before publishing. This is not the same thing as `publishMode: "draft"`, which is an unpublished upload and which TikTok cannot do at all. Default false. |
|---|
tiktokOptions | object | Optional | TikTok publishing options for this post. `disableComment`, `disableDuet` and `disableStitch`, or the permissive spellings `allowComments`, `allowDuet` and `allowStitch`. Anything you do not name is left at the account default. |
|---|
declarations | object | Optional | Statements the platforms require from the account holder, not publishing options. `madeForKids` is YouTube’s audience declaration and is required whenever this post names a YouTube account: we will not answer it on your behalf, so a post that targets YouTube without it is a 400. `brandedContent` discloses a paid partnership to TikTok and `brandOrganic` discloses that you are promoting your own business; leave either out and nothing is sent, which is where TikTok’s own composer starts. `aiGenerated` is TikTok’s `is_aigc` and `containsSyntheticMedia` is YouTube’s `status.containsSyntheticMedia`: two fields rather than one, because the two platforms word the question differently and we never send one platform the answer you gave the other. Neither is required by its platform and neither is required here, so a post that states nothing about how its video was made publishes with the key absent. Almost everything scheduled from the Looptake composer is generated, so the composer and the automation planner will not submit without an answer, on the paths that carry one. A TikTok post routed to the creator’s inbox is asked nothing, because that endpoint takes the video and no fields at all, and an Instagram-only post is asked nothing because Instagram carries no declaration field. Neither refusal exists at this API, which accepts any of these stated or unstated, and Instagram receives none of these on any path. An unrecognised key in this object is a 400 rather than a field we ignore. |
|---|
workspaceId | string | Optional | Which brand this post belongs to. Format: id. |
|---|