Skip to content

Publishing

Publishing posts content to the social accounts an agent has connected, immediately or at a scheduled time, and reports per platform whether the post went live, with the link to it.

One request can target several platforms at once. Every request is validated against each platform’s rules (media type, item count, caption and title limits, required fields) before anything is queued, so a bad request fails fast with the platform and field named.

PlatformplatformContentMedia per postCaption limitTitleNotes
TikToktiktokone video or one image12,200ignoredPrivacy level and interaction settings are required by TikTok.
Instagraminstagramone video (posts as a Reel), one image, or a carousel of 2-10 images and videos102,200ignoredUp to 30 hashtags. Optional instagram_music mixes a track under the video items.
Facebook Pagefacebookone video163,206255Posts to the connected Page.
YouTubeyoutubeone video15,000 (description)100, requiredthumbnail_url (https, .jpg/.jpeg/.png, under 2 MB) becomes the custom thumbnail. youtube_options: visibility, shorts, tags, made_for_kids, notify_subscribers, category_id.
Xxtext, up to 4 images, or one video; threads and replies4280 per tweetignoredText-only posts allowed.
LinkedInlinkedintext, one video, 1-9 images (a multi-image grid, or a swipeable carousel with linkedin_options.carousel), or one document (PDF, PPT, PPTX, DOC, DOCX)93,000400Text-only posts allowed. Posts as the connected member. LinkedIn has no image carousel for organic posts: carousel: true builds one PDF from the images and posts it as a document. Images sent with a video: the images post, the video is left out and reported in warning. A document cannot be mixed with other media. linkedin_options: visibility, alt_text, carousel.

These rules are served live and are what the API validates against:

GET /v1/schedule/platforms

No authentication required. Each entry has platform, label, text_only, media (video, image, document), max_media_items, max_text, max_title, required, optional, ignored, options (the platform’s options object and the values each field accepts), editable_after_publish (the fields that can still be changed once the post is live — see Edit a post that is already live) and notes.

Terminal window
curl "https://api.gen.pro/v1/schedule/platforms"
{
"platforms": [
{
"platform": "linkedin",
"label": "LinkedIn",
"text_only": true,
"media": ["video", "image", "document"],
"max_media_items": 9,
"max_text": 3000,
"max_title": 400,
"required": [],
"optional": ["description", "media_url", "media_urls", "title", "thumbnail_url", "linkedin_options"],
"ignored": ["thread", "reply_to_tweet_id"],
"editable_after_publish": [],
"options": {
"linkedin_options": {
"visibility": "public (default) | connections | loggedin",
"alt_text": "string[] — alternative text per image, in media order (images only)",
"carousel": "bool — post 2-9 images as a swipeable LinkedIn carousel (a PDF document built from the images, one page each) instead of the multi-image grid"
}
},
"notes": "Posts as the connected member. Text-only, one video, 1-9 images (a multi-image grid, or with linkedin_options.carousel a swipeable carousel = a PDF document built from the images), or one document URL (PDF, PPT, PPTX, DOC, DOCX; title is the document title, max 400). LinkedIn has no image carousel for organic posts. When images and a video are sent together LinkedIn gets the images and the video is left out (reported in `warning`). A document cannot be mixed with other media."
}
]
}
POST /v1/schedule/with-post
HeaderRequiredDescription
X-API-KeyYesYour API key.
Content-TypeYesapplication/json
Idempotency-KeyNoAny string up to 128 characters, unique per agent. Retrying with the same key returns the original post instead of creating a duplicate (idempotent_replay: true).
X-Timezone-OffsetNoLocal UTC offset in hours (for example -7). Times in the response are rendered in this offset; defaults to the agent’s time zone.
FieldTypeRequiredDescription
agent_idstringYesThe agent whose connected accounts post.
platformstring[]YesOne or more of tiktok, instagram, facebook, youtube, x, linkedin.
schedule_typestringNonow posts immediately. specific_time posts at scheduled_time. next_available (default) takes the agent’s next free calendar slot.
scheduled_timestringConditionalISO 8601 UTC, for example 2026-09-20T15:00:00Z. Required for specific_time; must be in the future.
descriptionstringConditionalCaption or post text. Hashtags inline. Required for a text-only X or LinkedIn post. Limited per platform (see the table above).
titlestringConditionalRequired for YouTube (max 100). Optional caption for Facebook (255) and LinkedIn (400). Ignored elsewhere.
media_typestringNoVIDEO (default), IMAGE, REELS, or TWEET_VIDEO. Use IMAGE when sending images.
media_urlstringConditionalPublic URL of the first (or only) media file: a video, an image, or for LinkedIn a PDF/PPT/PPTX/DOC/DOCX document. Required for TikTok, Instagram, Facebook and YouTube.
media_urlsstring[]NoFurther media URLs for the same post. Instagram: media_url plus media_urls totalling 2-10 images and videos post as one carousel. X: up to 4 images. LinkedIn: up to 9 images. One item elsewhere. The file extension decides image vs video vs document.
thumbnail_urlstringNoCustom thumbnail. Generated from the video when omitted. YouTube uploads it as the video’s thumbnail (https, .jpg, .jpeg or .png, under 2 MB); a thumbnail problem is reported as warning, the video still publishes.
threadobject[]NoX only. Ordered tweet segments, each { "text": "...", "media_urls": ["..."] }. Each segment is limited to 280 characters.
reply_to_tweet_idstringNoX only. Post as a reply to this tweet id.
tiktok_privacy_levelstringTikTokPUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY.
tiktok_disable_commentbooleanTikTokDisable comments.
tiktok_disable_duetbooleanTikTokDisable duets.
tiktok_disable_stitchbooleanTikTokDisable stitches.
tiktok_disclose_commercial_contentbooleanNoThe post promotes goods or services. Default false.
tiktok_your_brandbooleanNoPromotes the creator’s own brand. Default false.
tiktok_branded_contentbooleanNoPaid partnership. Default false.
instagram_musicobjectNoInstagram only. { "mode": "auto" } mixes a track from the GEN music library under every video item; { "track_url": "https://...mp3" } mixes that file instead. Needs at least one video item (Instagram cannot attach audio to images). The track used comes back as music_track on the platform status; when no library track is available the post publishes without music and music_track is null.
youtube_optionsobjectNoYouTube only. visibility: public (default), unlisted or private. shorts: true posts as a Short (adds #shorts; keep the video vertical and under 3 minutes). tags: string[], each 2+ characters, 400 characters in total. made_for_kids (default false), notify_subscribers (default true), category_id (default 22).
linkedin_optionsobjectNoLinkedIn only. visibility: public (default), connections or loggedin. alt_text: string[] with one entry per image, in media order (images only). carousel: true posts 2-9 images as a swipeable carousel, which on LinkedIn is a PDF document built from the images (one page each), instead of the multi-image grid; the post title becomes the document title.
strictbooleanNoDefault false: a platform that cannot take this content (for example Instagram with no media) is skipped and reported in platforms_skipped. Set true to get a 422 instead. Recommended for API clients.
{
"message": "Schedule and post created successfully",
"post_id": 48213,
"platforms": ["x", "linkedin"],
"platforms_status": [
{
"platform": "x",
"status": "scheduled",
"normalized_status": "publishing",
"platform_post_id": null,
"post_url": null,
"published_at": null,
"error": null,
"error_code": null,
"scheduled_time": "2026-09-20 08:00:00 -07:00"
},
{
"platform": "linkedin",
"status": "scheduled",
"normalized_status": "publishing",
"platform_post_id": null,
"post_url": null,
"published_at": null,
"error": null,
"error_code": null,
"warning": null,
"platform_options": { "linkedin_options": { "visibility": "connections" } },
"scheduled_time": "2026-09-20 08:00:00 -07:00"
}
],
"platforms_skipped": [],
"status_url": "/schedule/post/48213",
"idempotent_replay": false,
"post": { "id": 48213, "status": "scheduled", "description": "..." }
}
FieldDescription
post_idUse it with the status, update and delete endpoints.
platformsThe platforms you requested.
platforms_statusOne entry per platform that will be published. normalized_status, platform_post_id, post_url and published_at fill in as each platform completes. warning carries a non-fatal problem (for example thumbnail_failed: ... on YouTube while the video is live). platform_options echoes the options object you sent.
platforms_skippedPlatforms that cannot take this content, with a reason. Empty when strict is true (they return 422 instead).
status_urlPoll this (under /v1) for the final outcome.
idempotent_replaytrue when an Idempotency-Key matched an earlier post and that post was returned.

Anything a platform would reject is refused before queueing with 422:

{
"detail": {
"error": "YouTube requires a title",
"error_code": "platform_validation_failed",
"platform": "youtube",
"field": "title",
"errors": [
{ "platform": "youtube", "field": "title", "error": "YouTube requires a title" }
]
}
}

Checked per platform: caption over max_text, title over max_title, missing YouTube title, more media items than max_media_items (more than 10 on Instagram, 4 images on X, 9 images on LinkedIn, more than one elsewhere), images on a video-only platform (Facebook, YouTube), a document on a platform other than LinkedIn, mixing a document with other media on LinkedIn, more than 30 Instagram hashtags, instagram_music without a video item, a YouTube thumbnail_url that is not https or has a non-image extension (anything but .jpg/.jpeg/.png; extension-less URLs are checked by content type at upload), invalid option values (youtube_options.visibility, youtube_options.tags, youtube_options.category_id, linkedin_options.visibility, linkedin_options.alt_text on non-image media), tweet segments over 280 characters, unknown platform. The field is dotted for option objects, for example youtube_options.visibility. With strict: true, “no media on a media-only platform” is also a 422.

GET /v1/schedule/post/{post_id}

Returns the post with platforms_status, the same per-platform block as the create response, now filled in.

normalized_statusMeaning
acceptedStored, not started.
publishingScheduled, or the worker is uploading to the platform.
publishedLive on the platform. platform_post_id and post_url are set.
failedThe platform rejected it. error (and error_code, failed_step when known) explain why.
{
"id": 48213,
"status": "success",
"platforms_status": [
{
"platform": "youtube",
"normalized_status": "published",
"platform_post_id": "dQw4w9WgXcQ",
"post_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"published_at": "2026-09-20T15:00:12Z",
"error": null
},
{
"platform": "instagram",
"normalized_status": "failed",
"platform_post_id": null,
"post_url": null,
"error": "Media download failed: 403 Forbidden",
"error_code": "media_fetch_failed",
"failed_step": "download"
}
]
}

post_url is the public link to the live post: a YouTube watch URL, an Instagram permalink, a Facebook Page video URL, a TikTok video URL, an X status URL, or a LinkedIn post URL.

GET /v1/schedule/posts?agent_id={agent_id}&start_date={ISO date}&end_date={ISO date}
PATCH /v1/schedule/post/{post_id}
DELETE /v1/schedule/post/{post_id}

GET /schedule/posts lists the content calendar for a window of at most 60 days. PATCH accepts the same content fields as create plus scheduled_time and schedule_type; it only works before the post goes out (a post already sent, or created with schedule_type: now, cannot be edited). DELETE removes a scheduled post.

PATCH /v1/schedule/post/{post_id}/published

Fix a typo in a published description, retitle a video, change its tags or category — where the destination network supports it. This is the published-post counterpart to PATCH /schedule/post/{post_id}, which still refuses posts that have gone out.

Which fields each platform can change is served as editable_after_publish on every entry of GET /v1/schedule/platforms:

PlatformEditable after publishingWhy
YouTubetitle, description, tags, category_idThe Data API supports updating a live video’s snippet.
TikTok—No post-publication edit for this content.
Instagram—No post-publication edit for this content.
Facebook Page—No post-publication edit for this content.
X—Editing a sent tweet is a paid plan feature and is not exposed.
LinkedIn—The Posts API can partially update commentary; GEN has not wired that adapter yet.

The edit is sparse: only the fields in the body change. GEN reads the platform’s current metadata, merges your change onto it, writes it back, and returns what the platform held afterwards — so a title you did not send is never cleared, and sending the same request twice converges on the same result instead of stacking edits.

To clear a field rather than change it, send an explicit null ("description": null) or an empty list ("tags": []). title and category_id cannot be cleared: YouTube requires both, so a null there is a 422.

YouTube’s description limit is 5,000 bytes, not characters — emoji and CJK cost several bytes each, so a 2,000-character description of emoji is over the limit.

Terminal window
curl -X PATCH "https://api.gen.pro/v1/schedule/post/48213/published" \
-H "X-API-Key: $GEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"description": "Corrected sourcing figures in the pinned comment."}'
{
"post_id": 48213,
"results": [
{
"platform": "youtube",
"platform_post_id": "dQw4w9WgXcQ",
"result": "updated",
"updated_fields": ["description"],
"metadata": {
"title": "Factory price reveal",
"description": "Corrected sourcing figures in the pinned comment.",
"tags": ["sourcing", "beauty"],
"category_id": 22
},
"quota_units": 50
}
]
}

One entry per platform the post went out on:

resultMeaning
updatedThe platform accepted the edit. metadata is read back from the platform after the write, not echoed from the request.
unsupported_after_publishThis network has no post-publication edit for this content. Nothing was touched — this is a capability answer, not an authentication failure.
reauthorization_requiredGEN’s saved authorization for that account can no longer edit the post — the connection is missing, expired beyond refresh, or was granted without the write scope. The user reconnects it in the GEN app under Setup → Accounts.
failedThe platform rejected the edit; message carries its reason. This covers the 403s that reconnecting would not fix — quotaExceeded, a video the connected channel does not own, a suspended channel, a policy restriction.

A 422 means the request body itself is wrong: post_not_published (the post has not gone out — use PATCH /schedule/post/{post_id}), an empty body, or platform_validation_failed naming the field, either because the platform cannot change it after publishing or because it breaks the platform’s limits (the same limits a fresh publish is held to).

Posts reference media by URL, which must be publicly fetchable at post time. A render URL from the Generations API works directly. To publish a local file, upload it first:

POST /v1/social/upload_asset

Send multipart/form-data with file and agent_id. The file is also saved to the agent’s Assets.

{
"url": "https://cdn.gen.pro/....jpg",
"cdn_url": "https://cdn.gen.pro/....jpg",
"asset_id": "12345",
"content_resource_id": 12345,
"asset_registered": true
}

Use url as media_url or inside media_urls.

Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: launch-video-2026-09-20" \
-d '{
"agent_id": "your-agent-id",
"platform": ["youtube"],
"schedule_type": "now",
"media_type": "VIDEO",
"media_url": "https://cdn.gen.pro/outputs/render_xyz.mp4",
"title": "Factory price reveal",
"description": "What your $12 beauty sponge really costs.",
"strict": true
}'
Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["tiktok"],
"schedule_type": "specific_time",
"scheduled_time": "2026-09-20T15:00:00Z",
"media_url": "https://cdn.gen.pro/outputs/render_xyz.mp4",
"description": "The $2 taco at 2am #streetfood #tacotok",
"tiktok_privacy_level": "PUBLIC_TO_EVERYONE",
"tiktok_disable_comment": false,
"tiktok_disable_duet": false,
"tiktok_disable_stitch": false,
"strict": true
}'
Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["instagram"],
"schedule_type": "now",
"media_type": "REELS",
"media_url": "https://cdn.gen.pro/outputs/render_xyz.mp4",
"description": "Behind the scenes #reels",
"strict": true
}'
Section titled “Instagram carousel with auto-selected music”

Two to ten items (media_url plus media_urls, images and videos mixed) post as one carousel. instagram_music lays a track under every video item before upload.

Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["instagram"],
"schedule_type": "now",
"media_url": "https://cdn.gen.pro/outputs/clip_1.mp4",
"media_urls": ["https://cdn.gen.pro/outputs/still_2.jpg", "https://cdn.gen.pro/outputs/still_3.png"],
"description": "Three looks, one drop #carousel",
"instagram_music": { "mode": "auto" },
"strict": true
}'
Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["youtube"],
"schedule_type": "now",
"media_url": "https://cdn.gen.pro/outputs/render_xyz.mp4",
"thumbnail_url": "https://cdn.gen.pro/outputs/cover.jpg",
"title": "Factory price reveal",
"description": "What your $12 beauty sponge really costs.",
"youtube_options": { "visibility": "public", "shorts": true, "tags": ["sourcing", "beauty"] },
"strict": true
}'

The published post_url is https://www.youtube.com/shorts/{id} for a Short and https://www.youtube.com/watch?v={id} otherwise. If the thumbnail could not be set (wrong type, over 2 MB, channel not verified for custom thumbnails), the platform status carries warning: "thumbnail_failed: ..." and the video is still live.

Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["facebook"],
"schedule_type": "now",
"media_url": "https://cdn.gen.pro/outputs/render_xyz.mp4",
"title": "Launch day",
"description": "We are live.",
"strict": true
}'
Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["linkedin"],
"schedule_type": "now",
"description": "Three things we learned shipping our publishing API this week...",
"strict": true
}'
Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["linkedin"],
"schedule_type": "now",
"media_type": "IMAGE",
"media_url": "https://cdn.gen.pro/outputs/chart.png",
"title": "Q3 in one chart",
"description": "Our Q3 numbers, in one chart.",
"strict": true
}'

A PDF, PPT, PPTX, DOC or DOCX in media_url posts as LinkedIn’s swipeable document. title is the document title (max 400; the file name when omitted).

Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["linkedin"],
"schedule_type": "now",
"media_url": "https://cdn.gen.pro/outputs/q3-deck.pdf",
"title": "Q3 in ten slides",
"description": "Our Q3 numbers, one slide at a time.",
"linkedin_options": { "visibility": "connections" },
"strict": true
}'

LinkedIn renders several images as a grid; its swipeable carousel is a document. carousel: true builds one PDF from the picked images and posts it as that document.

Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["linkedin"],
"schedule_type": "now",
"media_type": "IMAGE",
"media_url": "https://cdn.gen.pro/outputs/slide-1.png",
"media_urls": ["https://cdn.gen.pro/outputs/slide-2.png", "https://cdn.gen.pro/outputs/slide-3.png"],
"title": "Q3 in three slides",
"description": "Swipe through our Q3.",
"linkedin_options": { "carousel": true },
"strict": true
}'

LinkedIn with several images and alt text (grid)

Section titled “LinkedIn with several images and alt text (grid)”
Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["linkedin"],
"schedule_type": "now",
"media_type": "IMAGE",
"media_url": "https://cdn.gen.pro/outputs/chart-1.png",
"media_urls": ["https://cdn.gen.pro/outputs/chart-2.png", "https://cdn.gen.pro/outputs/chart-3.png"],
"description": "Q3 in three charts.",
"linkedin_options": { "alt_text": ["Revenue by month", "Retention by cohort", "Pipeline by stage"] },
"strict": true
}'

X supports a plain tweet, a tweet with up to 4 images, a tweet with a video, a thread, and a reply.

Terminal window
# Text-only tweet
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{ "agent_id": "your-agent-id", "platform": ["x"], "schedule_type": "now", "description": "gm from the API" }'
# Up to 4 images
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["x"],
"schedule_type": "now",
"description": "four pics",
"media_type": "IMAGE",
"media_urls": [
"https://cdn.example.com/1.jpg",
"https://cdn.example.com/2.jpg",
"https://cdn.example.com/3.jpg",
"https://cdn.example.com/4.jpg"
]
}'
# Thread
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["x"],
"schedule_type": "now",
"thread": [
{ "text": "1/ Here is a thread about our launch." },
{ "text": "2/ With a screenshot", "media_urls": ["https://cdn.example.com/shot.jpg"] },
{ "text": "3/ Wrapping up. Thanks for reading!" }
]
}'
# Reply
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["x"],
"schedule_type": "now",
"description": "Great point. Here is our take.",
"reply_to_tweet_id": "1780000000000000000"
}'

thread and reply_to_tweet_id apply to X only and are ignored for other platforms. If a thread fails partway, posting stops at the failed segment; the tweets already posted are recorded and the post is marked failed (not retried, to avoid duplicates).

Terminal window
curl -X POST "https://api.gen.pro/v1/schedule/with-post" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"platform": ["tiktok", "instagram", "youtube", "x", "linkedin"],
"schedule_type": "specific_time",
"scheduled_time": "2026-09-20T15:00:00Z",
"media_url": "https://cdn.gen.pro/outputs/render_xyz.mp4",
"title": "Factory price reveal",
"description": "What your $12 beauty sponge really costs. #sourcing",
"tiktok_privacy_level": "PUBLIC_TO_EVERYONE",
"tiktok_disable_comment": false,
"tiktok_disable_duet": false,
"tiktok_disable_stitch": false
}'

The 280-character X limit and the 100-character YouTube title apply to the shared description and title, so keep them within the tightest platform in the list, or send separate requests per platform.

Terminal window
KEY=your-api-key; AGENT=your-agent-id
# 1. Upload the file
URL=$(curl -s -X POST "https://api.gen.pro/v1/social/upload_asset" -H "X-API-Key: $KEY" \
-F "file=@image.jpg" -F "agent_id=$AGENT" | python3 -c "import sys,json;print(json.load(sys.stdin)['url'])")
# 2. Post it to X
POST_ID=$(curl -s -X POST "https://api.gen.pro/v1/schedule/with-post" -H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d "{\"agent_id\":\"$AGENT\",\"platform\":[\"x\"],\"schedule_type\":\"now\",\"media_type\":\"IMAGE\",\"media_urls\":[\"$URL\"],\"description\":\"from the API\",\"strict\":true}" \
| python3 -c "import sys,json;print(json.load(sys.stdin)['post_id'])")
# 3. Poll until published or failed, then print the link
curl -s "https://api.gen.pro/v1/schedule/post/$POST_ID" -H "X-API-Key: $KEY" \
| python3 -c "import sys,json; [print(p['platform'], p['normalized_status'], p['post_url'] or p['error']) for p in json.load(sys.stdin)['platforms_status']]"
StatusError codeDescription
401unauthorizedMissing or invalid API key.
403permission_deniedYou do not have access to this agent.
404not_foundUnknown post_id.
422platform_validation_failedA platform would reject this content. detail.platform, detail.field and detail.errors say which and why.
422validation_errorInvalid or missing fields.
422no_active_credit_purchaseThe agent’s workspace has no active credit purchase.

POST /v1/user_jobs with user_job_type: publish_content still works for single-platform TikTok posts. New integrations should use POST /v1/schedule/with-post, which covers every platform, validates up front, and returns the live post_url.