Creating and producing posts
The calls that make a post exist: a stack, its footage, its texts, the render and the upload run. Two of them run in the background, and two of them can post for real.
create_stack
Create a new stack — the unit that becomes posts. Ideally from a stack preset (widgets, background, captions come along); brands, platforms and post_type (video/text/carousel/story) can be set directly. For carousels pass carousel_images or use assign_media afterwards; a story stack takes its one image/video via assign_media 'video' and posts it as an Instagram/Facebook story.
| Parameter | Type | Description |
|---|---|---|
name (required) |
string | Stack name. |
post_type |
enum: video, text, carousel, story |
What the stack produces (default video). |
preset |
string | Stack preset to apply (see list_presets). |
brands |
array of string | Brands to post as (see list_brands). |
platforms |
array of string | Platforms (e.g. insta, tiktok, youtube). |
carousel_images |
array of string | Ordered image paths (carousel stacks only). |
assign_media
Set a stack's media: 'video' (a file path, or 'inbox' for the newest unused inbox video) for video/story stacks, or 'images' (ordered paths) for carousel stacks — and, for TEXT stacks, the optional pictures posted with the text (up to 10; X/Twitter and Bluesky post the first 4, Threads/Facebook 10; an empty list clears them). Video is placed into the stack's content area via fit/rect and the result comes back as 'canvas_rect' on the 300x533 editor canvas — check it, and read 'warnings' when the clip hangs off the frame.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
video |
string | Video file path, or 'inbox' (video/story stacks). |
images |
array of string | Ordered image paths: the slides of a carousel stack, or the pictures of a text stack (empty list clears). |
fit |
enum: cover, contain, off |
How the footage fills the target area (the stack's current content area, or 'rect' when given): cover fills it completely and crops the overhang (default); contain shows the whole frame and letterboxes — a 16:9 source in the vertical frame becomes a full-width band with the background above and below; off keeps the stored geometry. |
rect |
array of number | Optional explicit target area [x, y, w, h] in canvas coordinates (300x533), e.g. [0, 182, 300, 169] for a centered 16:9 band. Overrides the stack's own area and fits with contain unless you pass fit=cover. |
Each text entry can be scoped to one brand, one platform, both or neither. Write the copy that actually differs per channel and let the rest fall back to the stack's own text instead of repeating the same description nine times.
set_post_texts
Write post texts directly onto a stack: title, description, first_comment, hashtags — per entry optionally scoped to one brand and/or one platform (omitted = all). Write the copy yourself in each brand's content language (see list_brands). For text stacks the description IS the post.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
entries (required) |
array of object | Text entries to apply. |
import_csv
Import a local CSV into a stack's texts — the editor's 'Import CSV' button: kind title_desc (platform;title;description), first_comment, text_widget, hook (one angle per line) or endcard. The CSV is copied into the stack's captions/ folder.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | |
kind (required) |
string | title_desc |
path (required) |
string | Path of the CSV file. |
language |
string | hook/endcard only: file the lines under this brand content language (code, e.g. 'de'); without it the lines go to every stack language + the flat list (a warning names the languages when there are several). |
remove_texts
Drop one kind of texts from a stack (title_desc, first_comment, text_widget, hook, endcard) and its CSV reference — the editor's 'Remove CSV'. confirm-gated.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | |
kind (required) |
string | |
confirm (required) |
boolean | Required true. Only pass it after the user explicitly confirmed this action in chat. |
generate_hooks
Generate AI hook angles (the opener text burned into the first seconds) for a stack and switch the hook widget on — or, with kind='endcard', the endcard lines (the closing card) and switch the endcard on. One set per brand content language. Requires an AI key in Settings → AI.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
kind |
enum: hook, endcard |
'hook' (default) or 'endcard'. |
topic |
string | What the clip is about. Optional when the stack has clip analysis enabled — the clip then briefs the AI. |
count |
integer | Number of angles, 2-8 (default 4). |
generate_texts
AI-generate one text kind for a stack via the configured AI key: title_desc, first_comment, text_widget, hook or endcard — per brand/platform/language, brand identity included, applied to the stack like the editor's 'Generate with AI'. Alternative: set_post_texts writes copy you author yourself. Topic optional when clip_context_enabled and an analyzed clip supply the briefing.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
kind (required) |
enum: title_desc, first_comment, text_widget, hook, endcard |
Which text kind to generate. |
topic |
string | The briefing/topic. |
count |
integer | Hook angles to generate (hook kind, default 4). |
include_hashtag_pool |
boolean | Offer the brand hashtag pools to the AI (title_desc). |
transcribe
Transcribe a stack's main content into the auto-captions transcript (local Whisper, may take a while). Needed before burned-in karaoke captions; renders reuse the cached result.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
translate_captions
Translate the cached transcript into one brand content language (cached per language, keyed on the source words — the render burns the translation in for brands with that language). Uses the configured AI key.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
language (required) |
string | Target language. |
force |
boolean | Re-translate over the cache. |
edit_transcript
Write a corrected auto-captions transcript back onto the transcribed timings (untouched passages keep theirs). With 'language' it edits that language's translation instead. [m:ss] markers are labels and may stay in the text.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
text (required) |
string | The corrected transcript. |
language |
string | Edit this translation instead of the transcript. |
reset_transcript
DELETE a stack's cached transcript (with every translation) so the next transcribe runs fresh — or just one language's cached translation. Requires confirm=true after the user confirmed.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
language |
string | Only clear this language's translation. |
confirm (required) |
boolean | Required true. Only pass it after the user explicitly confirmed this deletion in chat. |
analyze_clip
Watch the stack's main content with the configured AI and cache a clip brief (summary, beats, hook moments) with the stack. generate_texts injects it once clip_context_enabled is set. force=false reuses a cached brief.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
force |
boolean | Re-analyze even when cached (default true). |
render_batch returns the moment the batch starts and renders in the
background — poll render_status until it reports running: false. Only one batch
runs at a time; a second call answers already_running with the live status.
render_batch
Start rendering one or more stacks into their brand × platform video variants (ffmpeg render worker). Returns immediately — the render runs in the background and takes minutes per stack; poll render_status until running=false before scheduling. One batch at a time.
| Parameter | Type | Description |
|---|---|---|
stacks (required) |
array of string | Stack names to render. |
cancel_render
Stop the render batch started by render_batch: kills the active render worker (ffmpeg included) and drops the rest of the queue — the interrupted stack keeps its previous outputs and shows as unrendered/stale; re-run render_batch for what is still needed. Only a batch started in this server process is reachable.
No parameters.
resume_render
Resume an interrupted render queue (app closed / server restarted / cancelled mid-batch): starts render_batch for the remaining stacks of the saved queue; clears the state when nothing is left.
No parameters.
clear_render_queue
Forget an interrupted render queue without rendering (the hub's 'Discard' on the resume prompt). Outputs stay untouched.
No parameters.
Render one variant at reduced resolution, look at it with view_frame, fix
the layout, and only then start the full batch. It is the same pipeline, so what you
see is what the render produces.
render_preview
One complete preview render of a stack (single variant: first brand/platform) — the FULL render pipeline incl. captions, silence cut and music ducking, at reduced resolution; only the per-output randomizer pass is skipped, so the result IS the final video, just smaller. Much quicker than render_batch. LOOK at the result via view_frame, fix via set_widget_geometry/update_stack, then render_batch. The result carries motion_windows (when each hook/endcard title is fully legible on this file's timeline), look_at_seconds and the variant's layout.
| Parameter | Type | Description |
|---|---|---|
stack (required) |
string | Stack name. |
view_frame
SEE the result: returns real frames as images — from the stack's newest rendered video (source='rendered', default), its raw main content (source='main') or any image/video file path. at_seconds picks the moment(s), up to 4 per call; without it a rendered output is sampled AFTER the hook/endcard titles settled plus mid-core. For rendered outputs the payload carries motion_windows (hook/endcard windows on the timeline, settled_at = when the animated title is fully legible — frames before that show the entrance, not broken text) and layout (how the clip sits in the 9:16 frame — letterbox bands are intended). Look, then adjust via set_widget_geometry/update_stack and render again.
| Parameter | Type | Description |
|---|---|---|
stack |
string | Stack name. |
source |
string | 'rendered' (newest render, default), 'main' (raw footage) or a file path. |
at_seconds |
any | Timestamp(s) in seconds — number or list (max 4). Default for a rendered output: the settled moment of each title window plus the middle of the core. |
height |
integer | Frame height in px (240-1080, default 640). |
view_motion_template
SHOW a motion-title template as images (sample text on a dark backdrop) — look at a template before picking it via update_stack, the same way view_frame shows rendered results. Default frames: entrance, the template's signature moment (after the text settled), and a late loop state; the payload names settles_after_s for the text and the idle motion. First call per template captures (~10 s), later calls hit the cache.
| Parameter | Type | Description |
|---|---|---|
template (required) |
string | Template key from list_motion_templates. |
text |
string | Sample text (default 'Stop scrolling'). |
accent |
string | #rrggbb accent color (default: the template's own; at render time the brand identity accent applies). |
at_seconds |
any | Timestamp(s) 0..3.2 to show, max 4 (default: entrance / signature moment / late loop). |
Dry run by default; real posting needs approved: true.
schedule
Start the upload plan for rendered stacks (schedule/post via the configured upload mode) and return a run_id — the run continues in the background (real API tasks take ~30 s EACH), so poll upload_status until running=false; quick runs (dry runs, single tasks) already come back finished. Calling schedule again while a run is active returns already_running instead of double-posting. Exact duplicates — the same file/text this brand+platform already uploaded per upload history — are skipped and reported as skipped_duplicates unless allow_duplicates=true; a re-render (different file) only warns. GUARDED: without dry_run=false this is a dry run — in the API modes it records the planned posts into the app's review queue, where the user can approve them. dry_run=false WITHOUT approved posts exactly the review-approved entries and skips the rest; approved=true (only when the user explicitly approved posting in chat) posts the whole plan except review-rejected entries. Real posts respect per-account rate limits and may be skipped.
| Parameter | Type | Description |
|---|---|---|
stacks (required) |
array of string | Stack names to upload. |
start_date |
string | YYYY-MM-DD (default: today). |
days |
integer | Days to spread the posts over (default 1). |
shuffle |
boolean | Shuffle schedule dates. |
dry_run |
boolean | Plan only, post nothing (the default). |
approved |
boolean | The user explicitly approved real posting. Required for dry_run=false to take effect. |
allow_duplicates |
boolean | Re-post exact duplicates instead of skipping them (default false: a task whose exact file/text this brand+platform already uploaded is skipped and listed under skipped_duplicates). |
retry_tasks |
array of object | Retry filter: [{stack, brand, platform}] from a previous run's failed_tasks — only those tasks are attempted (the hub's 'Retry failed uploads'). |
A rebuild re-exports. It reuses the media links already recorded in the upload history, so it uploads nothing and posts nothing.
rebuild_csv
Rebuild ONE combined Metricool/Zernio import CSV from the upload history of every stack (uploads spread over several runs — abort, stragglers, retry — never produce one importable file). Recorded share links are reused, nothing is uploaded or posted; texts come from the current stack data and the schedule is planned fresh from start_date (default tomorrow). Multi-image Metricool posts and stacks whose renders were deleted are skipped with a log line.
| Parameter | Type | Description |
|---|---|---|
mode (required) |
enum: metricool_drive, late_csv |
Which CSV to rebuild (metricool_drive = Metricool bulk CSV, late_csv = Zernio bulk CSV). |
since |
string | Include uploads at or after this ISO date/datetime (empty = the whole history). |
start_date |
string | First day of the fresh schedule, YYYY-MM-DD (default tomorrow). |
The same gate applies here — and in the CSV upload modes a real run writes the Zernio or Metricool import file for you instead of posting.
run_assembly
Execute an assembly-line preset end to end: fresh stack from its stack preset, optional inbox footage + autocrop, AI copy, render, upload. GUARDED like schedule: the upload phase is a dry run by default (recording planned posts into the app's review queue); dry_run=false without approved posts only review-approved entries, approved=true (only when the user explicitly approved posting in chat) posts everything except review-rejected ones. 'overrides' overrides preset fields (see list_presets for keys).
| Parameter | Type | Description |
|---|---|---|
preset (required) |
string | Assembly-line preset name (see list_presets). |
overrides |
object | Field overrides, e.g. {"csv_topic": "..."}. |
dry_run |
boolean | Upload phase plans only, posts nothing (the default). |
approved |
boolean | The user explicitly approved real posting. Required for dry_run=false to take effect. |
decide_review
Approve / reject / undo (status pending) review-queue entries by id — the human approval the agentic upload gate reads. status='approved' needs confirm=true (only after the user approved these posts in chat); approved entries are posted by the next agentic real run.
| Parameter | Type | Description |
|---|---|---|
ids (required) |
array of string | Entry ids from get_review_queue. |
status (required) |
string | approved, rejected or pending (undo). |
confirm |
boolean | Required true. Only pass it after the user explicitly confirmed this action in chat. |