Skip to Content

Posts

Create, schedule, update, and publish social media posts across multiple platforms simultaneously.

Base URL

https://api.voxburst.io/v1/posts

Create Post

POST /v1/posts

Creates a new post. Posts can be created as drafts, scheduled for a future time, or published immediately.

Required scopes: posts:write

Request Body

FieldTypeRequiredDescription
contentstringYesPost content. Used as the default for all platforms unless overridden (max 10,000 characters).
accountIdsstring[]YesIDs of connected accounts to post from. The platform is inferred from each account.
scheduledForstringNoISO 8601 datetime for scheduled publishing
contentTypestringNoContent type hint: TEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, REEL
saveAsDraftbooleanNoIf true, saves as draft regardless of scheduledFor
platformOverridesobjectNoPer-platform content overrides — keyed by platform constant (e.g. "TWITTER"). See Content Overrides
platformMetadataobjectNoPer-platform metadata overrides — keyed by platform constant (e.g. "TIKTOK"). See Platform Metadata
mediastring[]NoIDs of previously uploaded media files
tagsstring[]NoInternal tags for organization (not posted to platforms)
personaIdstringNoID of a persona profile to associate with this post
queuebooleanNoIf true, picks the next available queue slot instead of requiring scheduledFor. Mutually exclusive with scheduledFor.
threadPostsobject[]NoThread items for X/Threads posts (max 25 items). Each item: { sequence: number, content: string, mediaIds?: string[] }. Sequences must be 1-based and contiguous.
firstCommentstringNoText to auto-post as a comment immediately after publishing (max 2,200 characters). Silently skipped on platforms that do not support comments — no error is returned. See supported platforms below.
firstCommentDelayintegerNoSeconds to wait before posting the first comment (0–60, default 0). Capped at 60s by the Lambda timeout. Only applies on platforms where firstComment is supported.
isManualSavebooleanNoWhen true, bypasses the post.created webhook debounce window so the webhook fires immediately
metadataobjectNoArbitrary key-value metadata. See Metadata Constraints.

Metadata Constraints

The metadata field accepts an arbitrary key-value object with the following limits. Requests violating these limits receive a 422 error.

RuleLimit
Key lengthMax 100 characters
String value lengthMax 1,000 characters
Array value item countMax 100 items
Array value item lengthMax 200 characters each

Platform Metadata

platformMetadata accepts per-platform metadata overrides. Keys are platform constants (e.g. "TIKTOK"); values are objects whose fields are merged into the post’s metadata for that platform. This is separate from platformOverrides, which overrides content/caption.

Recognized keys

TIKTOK.tiktokPrivacyLevel (string) — Override the TikTok privacy level for this post. The value you pass is requested from TikTok; the actual level applied depends on the connected account’s creator permissions. If the requested level is not allowed for the account, the post may be automatically downgraded to SELF_ONLY (see publishWarning in the response).

Valid values:

ValueDescription
PUBLIC_TO_EVERYONEVisible to all TikTok users
MUTUAL_FOLLOW_FRIENDSVisible to mutual followers only
SELF_ONLYVisible only to the account owner
FOLLOWER_OF_CREATORVisible to followers of the creator

Available values depend on what the connected TikTok account’s permissions allow. Accounts in unaudited or restricted states may only be allowed SELF_ONLY.

INSTAGRAM.instagramAudioId (string) — Attach a licensed audio track to an Instagram Reel. Use the id returned by GET /v1/instagram/audio/search. Only applies when the post contains a single video — ignored for images, carousels, and Stories. Requires a Facebook Login-connected Instagram account.

INSTAGRAM.instagramAudioVolume (number, 0.0–1.0, default 1.0) — Volume of the attached audio track relative to maximum. 0.0 is silent, 1.0 is full volume.

INSTAGRAM.instagramVideoVolume (number, 0.0–1.0, default 1.0) — Volume of the original video’s audio track. Set to 0.0 to mute the original video audio entirely (music-only), or blend with instagramAudioVolume for a mix.

{ "content": "Summer vibes 🌊", "accountIds": ["acc_instagram_example"], "platformMetadata": { "INSTAGRAM": { "instagramAudioId": "1234567890", "instagramAudioVolume": 0.8, "instagramVideoVolume": 0.3 } } }
{ "content": "New video!", "accountIds": ["acc_tiktok_example"], "platformMetadata": { "TIKTOK": { "tiktokPrivacyLevel": "FOLLOWER_OF_CREATOR" } } }

First comment — platform support

firstComment is accepted on all post creation requests but is only posted on platforms that expose a comment API. On unsupported platforms it is silently skipped — the post publishes normally and no error is returned.

PlatformFirst comment supported
Instagram
LinkedIn
Facebook✗ †
Threads
TikTok
YouTube
Reddit
Twitter / X
Bluesky
Pinterest
Snapchat
Telegram
Google Business
Mastodon

† Facebook first comment — pending Meta API approval. The pages_manage_engagement permission required for Facebook first comments is currently under review by Meta. Posts that include firstComment will publish normally — the comment is silently skipped on Facebook until the permission is approved and re-enabled.

Example Request

curl -X POST https://api.voxburst.io/v1/posts \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "content": "Hello from VoxBurst!", "accountIds": ["acc_123", "acc_456"], "scheduledFor": "2026-04-20T14:00:00Z" }'

Response

{ "id": "post_abc123", "content": "Hello from VoxBurst! 🚀", "status": "scheduled", "scheduledFor": "2026-03-01T14:00:00Z", "createdAt": "2026-02-20T10:00:00Z", "updatedAt": "2026-02-20T10:00:00Z", "platforms": [ { "postPlatformId": "pp_111", "platform": "twitter", "accountId": "acc_123", "accountName": "My Twitter", "status": "pending", "platformPostId": null, "platformPostUrl": null, "publishedAt": null, "publishWarning": null, "error": null, "attempts": [] }, { "postPlatformId": "pp_222", "platform": "linkedin", "accountId": "acc_456", "accountName": "My LinkedIn", "status": "pending", "platformPostId": null, "platformPostUrl": null, "publishedAt": null, "publishWarning": null, "error": null, "attempts": [] } ] }

The word “platforms” appears in three distinct contexts in this API — they are not interchangeable:

  1. Targeting accounts (request) — use accountIds to specify which connected accounts to post from. The platform is inferred from each account. There is no platforms field on post creation or update.
  2. Publish state (response) — the platforms array in every post response tracks per-destination publish status, errors, and URLs. This is read-only.
  3. Content overrides (request) — the platformOverrides object lets you customize content per platform constant (e.g. "TWITTER"). See Content Overrides below.

The platforms array in the response tracks per-platform publish state. After publishing, each entry’s status will be published (with a platformPostUrl) or failed (with an error message). The postPlatformId is used with the Fix Platform endpoint to resubmit a specific failed platform. A non-null publishWarning indicates a non-fatal condition that occurred during publishing — see PostPlatform fields below.

Full Post Response Fields

FieldTypeDescription
idstringPost ID
contentstringPost content
contentTypestring | nullTEXT, IMAGE, VIDEO, CAROUSEL, STORY, REEL, or THREAD
statusstringSee Post Status
scheduledForstring | nullISO 8601 publish time
publishedAtstring | nullISO 8601 timestamp when publishing completed
platformRemovedAtstring | nullISO 8601 timestamp when the post was deleted from the platform
firstCommentstring | nullContent of a comment auto-posted after publishing
firstCommentDelaynumber | nullSeconds to wait before posting the first comment
queuePositionnumber | nullPosition in the publishing queue, if queued
tagsstring[]Internal tags (not published to platforms)
metadataobject | nullArbitrary key-value metadata — see Metadata Constraints
mediaobject[]Attached media files with full metadata
platformsobject[]Per-platform publish state — see PostPlatform fields
threadPostsobject[]Thread items for X/Threads posts
recurringPostIdstring | nullID of the recurring rule that generated this post
isRecurringboolean | nulltrue if this post is the source of a recurring rule
personaIdstring | nullPersona associated with this post
createdAtstringISO 8601 creation timestamp
updatedAtstringISO 8601 last-updated timestamp

PostPlatform fields

Each entry in the platforms array represents one publish destination. Fields marked (success only) are absent when status is not published.

FieldTypeDescription
postPlatformIdstringID of this PostPlatform record — used with Fix Platform
platformstringPlatform constant (lowercase, e.g. "twitter")
accountIdstringConnected account ID
accountNamestringDisplay name of the connected account
statusstringpending, publishing, published, failed, or skipped
platformPostIdstring | nullPlatform’s native post ID (success only)
platformPostUrlstring | nullPublic URL of the post (success only)
publishedAtstring | nullISO 8601 timestamp when this platform succeeded (success only)
publishWarningstring | nullNon-fatal warning set during publishing. null when no warning occurred. See values below.
errorstring | nullError message when status is failed
attemptsobject[]History of publish attempts

publishWarning values

ValueDescription
TIKTOK_SELF_ONLY_FALLBACKThe requested TikTok privacy level was rejected by the API (typically because the application is in an unaudited state). The post was automatically retried with SELF_ONLY visibility and published successfully.

Content Overrides

Use platformOverrides to customize content per platform constant. This is separate from accountIds (which targets accounts) and from the platforms array in responses (which tracks publish state):

platformOverrides schema

platformOverrides is a map keyed by platform constant (e.g. "TWITTER"). Each value supports:

FieldTypeRequiredDescription
contentstringNoOverride caption/text for this platform only (max 10,000 characters)
{ "content": "Announcing our new feature!", "accountIds": ["acc_123", "acc_456"], "platformOverrides": { "TWITTER": { "content": "New feature alert! 🎉 #voxburst" }, "LINKEDIN": { "content": "We're excited to announce our latest feature that helps teams schedule social media content more efficiently. Read more on our blog." } } }

platformOverrides only overrides content — it does not override media, contentType, or other fields per platform. To post different media per platform, create separate posts targeting each platform’s account.

threadPosts schema

Used with contentType: "THREAD" for X (Twitter) only. Threads (Meta) does not use this field — individual Threads posts do not support reply-chain threading through the API. Each item represents one post in the thread.

FieldTypeRequiredDescription
sequenceintegerYes1-based position in the thread. Must be contiguous starting from 1.
contentstringYesText of this thread item (max 280 characters for X)
mediaIdsstring[]NoMedia to attach to this thread item (max 4 items)

Constraints:

  • Max 25 thread items per post
  • Sequences must start at 1 and be contiguous (1, 2, 3 … — gaps are rejected)
  • When contentType: "THREAD", each item counts as 1 post against your plan quota
{ "content": "Thread opener — first tweet", "accountIds": ["acc_twitter_abc"], "contentType": "THREAD", "threadPosts": [ { "sequence": 1, "content": "1/ This is the opening tweet of the thread." }, { "sequence": 2, "content": "2/ The second tweet in the thread.", "mediaIds": ["media_abc123"] }, { "sequence": 3, "content": "3/ Closing thoughts. Follow for more." } ] }

Get Post

GET /v1/posts/:id

Retrieve a single post by ID.

curl https://api.voxburst.io/v1/posts/post_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

List Posts

GET /v1/posts

List all posts with optional filtering. Uses cursor-based pagination.

Query Parameters

ParameterTypeDescription
statusstringFilter by status: draft, scheduled, publishing, published, failed, partial, cancelled, unpublished, archived, all
platformstringFilter by platform
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)
limitnumberResults per page (default: 20, max: 100)
cursorstringPagination cursor from previous response
curl "https://api.voxburst.io/v1/posts?status=scheduled&limit=50" \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "data": [...], "pagination": { "next_cursor": "eyJpZCI6InBvc3RfYWJjMTIzIn0", "has_more": true, "limit": 50 } }

Update Post

PATCH /v1/posts/:id

Update a post. Posts in draft, scheduled, approved, failed, or partial status can be updated.

Required scopes: posts:write

Request Body

All fields are optional. Only include the fields you want to change.

FieldTypeDescription
contentstringNew post content (max 63,206 characters)
contentTypestringContent type: TEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, REEL
scheduledForstringNew scheduled time (ISO 8601)
clearSchedulebooleanIf true, removes the scheduled time and reverts the post to draft
saveAsDraftbooleanIf true, saves as draft regardless of scheduledFor
accountIdsstring[]Replace the set of target accounts
mediastring[]Replace attached media
platformOverridesobjectPer-platform content overrides
platformMetadataobjectPer-platform metadata overrides — keyed by platform constant (e.g. "TIKTOK"). See Platform Metadata
tagsstring[]Replace post tags
personaIdstringReplace the associated persona
threadPostsobject[]Replace thread items (X/Threads only). Same schema as Create Post.
firstCommentstringReplace the first comment text (max 2,200 characters)
firstCommentDelayintegerSeconds before the first comment is posted (0–60). Capped at 60s by the Lambda timeout. Only applies on platforms that support firstComment.
platformRemovedAtstring | nullISO 8601 timestamp when the post was removed from the platform, or null to clear
metadataobjectReplace post metadata key-value pairs
versionstringOptimistic concurrency token — the post’s updatedAt ISO string from when you loaded the post. If the post has been modified since, the request returns 409 Conflict. Omit to skip the check.
curl -X PATCH https://api.voxburst.io/v1/posts/post_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "content": "Updated content!", "scheduledFor": "2026-03-02T14:00:00Z", "version": "2026-02-20T10:00:00.000Z" }'

Published posts cannot be modified. To change published content, delete the original post and create a new one.


Delete Post

DELETE /v1/posts/:id

Delete a post. Posts in publishing or published status cannot be deleted.

Required scopes: posts:write

curl -X DELETE https://api.voxburst.io/v1/posts/post_abc123 \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response (200):

{ "success": true }

Deleting a post in a non-deletable status (e.g. publishing) returns 409 Conflict. Deleting a post removes it from VoxBurst but does not delete it from social platforms if already published. You must delete published posts manually on each platform.


Publish Now

POST /v1/posts/:id/publish

Immediately publish a draft or scheduled post.

Required scopes: posts:write

Query Parameters

ParameterTypeDescription
syncbooleanIf true, waits for publishing to complete before responding (up to timeout seconds)
timeoutintegerMax seconds to wait when sync=true (default: 30, max: 120)

Request Headers

HeaderDescription
X-Callback-UrlOptional HTTPS URL to POST a completion callback to when publishing finishes
curl -X POST "https://api.voxburst.io/v1/posts/post_abc123/publish?sync=true&timeout=60" \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "X-Callback-Url: https://your-webhook-url.com/hooks/publish-done"

Retry Failed Post

POST /v1/posts/:id/retry

Retries all failed platform targets on a failed or partial post. Successfully published platforms are never re-posted — only platforms with a FAILED status are re-queued. Retries are subject to exponential backoff and a per-platform maximum of 3 attempts.

Required scopes: posts:write

This endpoint is idempotent when called with an Idempotency-Key header. Provide a unique key per retry attempt to avoid duplicate submissions.

Backoff Schedule

Each platform tracks its own retryCount. The delay before the next attempt doubles with each failure:

Retry attemptDelay
1st retry2 minutes
2nd retry4 minutes
3rd retry8 minutes
After 3rdPlatform exhausted — use Fix Platform to reset

Example Request

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/retry \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Idempotency-Key: retry-post_abc123-attempt-1"

Response

{ "id": "post_abc123", "status": "publishing", "retriedPlatforms": [ { "platform": "instagram", "accountId": "acc_789", "retryCount": 1, "nextRetryAt": "2026-04-02T14:02:00Z" } ], "skippedPlatforms": [ { "platform": "facebook", "accountId": "acc_456", "retryCount": 3, "reason": "max_retries_exceeded" } ] }

skippedPlatforms lists any failed platforms that have already hit the 3-retry limit. Use the Fix Platform endpoint to reset these manually.


Fix Platform

POST /v1/posts/:id/platforms/:platformId/fix

Fixes and re-queues a single platform on a partial or failed post. Eligible platform statuses are failed, publishing, and pending. Unlike /retry, this endpoint resets the platform’s retry counter to zero and optionally lets you supply a replacement image URL or content — useful when the original failure was caused by an invalid or rejected image.

Required scopes: posts:write

Use this endpoint when a platform has exhausted its automatic retries (retryCount = 3) or when you need to correct the media or caption before resubmitting.

Path Parameters

ParameterDescription
idPost ID
platformIdThe id of the specific PostPlatform entry to fix. Found in the post’s platforms array.

Request Body

All fields are optional. Omitting the body re-queues the platform with its original content.

FieldTypeRequiredDescription
mediaUrlstringNoReplacement image URL (https:// only). Use when the original image was rejected by the platform (e.g., wrong aspect ratio, size exceeded).
contentstringNoReplacement caption or post text for this platform only.
refreshMediabooleanNoIf true, re-fetches and re-uploads the existing media from storage before resubmitting. Useful when the original upload expired or was rejected.

Example — Re-queue with corrected image

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/platforms/pp_xyz789/fix \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "mediaUrl": "https://cdn.example.com/corrected-image-1080x1080.jpg" }'

Example — Re-queue with no changes

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/platforms/pp_xyz789/fix \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "success": true, "platformId": "pp_xyz789", "status": "publishing" }

After a successful response, the parent post status returns to publishing. The platform is re-queued immediately without backoff delay (retryCount is reset to 0).


Handling Partial Failures

A post reaches PARTIAL status when at least one platform publishes successfully and at least one fails. Platforms that succeeded are not affected by retry or fix operations — only FAILED platforms are re-queued.

Typical partial failure workflow

  1. Your webhook receives a post.published event with status: "PARTIAL".
  2. Inspect the platforms array on the post — each entry has its own status, error, and retryCount.
  3. If the failure was transient (network error, rate limit), call POST /v1/posts/:id/retry to automatically re-queue all failed platforms with backoff.
  4. If the failure was caused by invalid content (e.g., Instagram rejected the image), call POST /v1/posts/:id/platforms/:platformId/fix with a corrected mediaUrl or content.
  5. If a platform has retryCount: 3, automatic retries are exhausted — /retry will skip it. You must use /fix to reset the counter and resubmit.

Platform status values

StatusDescription
pendingWaiting to be sent
publishingCurrently being submitted to the platform
publishedSuccessfully posted
failedPublish attempt failed — eligible for retry or fix
skippedExcluded from this run (e.g. platform not applicable, or media validation failed for this platform)

If all platforms fail, the post status is FAILED (not PARTIAL). Both statuses are eligible for /retry and /fix.


Post Status

All status values are returned as lowercase strings.

StatusDescription
draftCreated but not scheduled or published
approvedApproved through the approval workflow; queued for publishing
scheduledQueued for future publishing
publishingCurrently being sent to platforms
publishedSuccessfully published to all platforms
failedPublishing failed on all platforms
partialPublished to some platforms; at least one failed — use /retry or /fix to remediate
cancelledPost was cancelled before publishing
unpublishedPost was unpublished after a successful publish
archivedPost has been archived and is hidden from default views

State transition table

The table below shows which statuses permit each operation. “Yes” means the operation is available; ”—” means it is blocked.

StatusEdit (PATCH)Publish nowCancelRetryFixCloneDelete
draftYesYesYesYesYes
approvedYesYesYesYesYes
scheduledYesYesYesYesYes
publishingYes
publishedYes
partialYesYesYesYesYes
failedYesYesYesYesYes
cancelledYesYesYesYes
unpublishedYes
archivedYes

Terminal statuses (no further automatic transitions): published, cancelled, unpublished, archived.

User-triggered transitions:

  • draftscheduled: Set scheduledFor in a PATCH or at creation time
  • draft / scheduledpublishing: Call POST /v1/posts/:id/publish
  • draft / scheduledcancelled: Call POST /v1/posts/:id/cancel
  • published / partialunpublished: Call POST /v1/posts/:id/unpublish
  • failed / partialpublishing: Call POST /v1/posts/:id/retry or POST /v1/posts/:id/platforms/:id/fix

System-triggered transitions:

  • scheduledpublishing: Scheduler cron fires when scheduledFor is reached
  • approvedpublishing: Approval workflow completes
  • publishingpublished: All platform workers complete successfully
  • publishingpartial: At least one platform fails, at least one succeeds
  • publishingfailed: All platforms fail

Scheduling Semantics

All scheduled times are specified using the scheduledFor field on POST /v1/posts or PATCH /v1/posts/:id.

Timestamp format

scheduledFor must be a valid ISO 8601 datetime string. Always use UTC to avoid ambiguity around daylight saving transitions:

2026-06-15T14:00:00Z ✅ UTC (recommended) 2026-06-15T10:00:00-04:00 ✅ Offset included 2026-06-15T14:00:00 ❌ No timezone — rejected

Minimum lead time

The scheduled time must be in the future. The API allows a 60-second grace window to account for clock skew between your system and VoxBurst’s servers. Requests where scheduledFor is more than 60 seconds in the past return 400 VALIDATION_ERROR.

Maximum scheduling horizon

There is no maximum scheduling horizon. You can schedule posts years in advance.

Rescheduling

To reschedule a post, PATCH /v1/posts/:id with a new scheduledFor. The post must be in draft, scheduled, approved, failed, or partial status. Set clearSchedule: true to remove the scheduled time and revert the post to draft.

Queue mode

Set queue: true (instead of scheduledFor) to automatically assign the next available slot in your workspace’s posting queue. These are mutually exclusive — you cannot set both.

DST guidance

Use UTC timestamps for reliability. If you need to schedule at a user-local time, convert to UTC in your application before calling the API. VoxBurst stores and compares all times in UTC.


Validate Post

POST /v1/posts/validate

Validate content against platform rules without creating a post. Returns per-platform errors and warnings.

This endpoint takes platforms (not accountIds). Pass an array of platform constants such as ["TWITTER", "INSTAGRAM"]. It does not accept account IDs — it validates content rules for the named platforms directly, with no account lookup.

Request Body

FieldTypeRequiredDescription
contentstringYesPost content to validate
platformsstring[]Yes1–15 platform constants to validate against (e.g. ["TWITTER", "INSTAGRAM"]). Must be valid Platform enum values. See platform constants.
mediaIdsstring[]NoMedia IDs to include in validation (checks dimensions, size, format)
mediaobject[]NoInline media metadata for pre-upload checks. Each item: { type: "image"|"video"|"gif", url?: string, sizeBytes?: number, durationMs?: number }
firstCommentstringNoOptional first-comment content to validate alongside the post (max 10,000 characters)
curl -X POST https://api.voxburst.io/v1/posts/validate \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "content": "Test post content", "platforms": ["TWITTER", "LINKEDIN"] }'

Response

{ "valid": true, "platforms": { "TWITTER": { "valid": true, "errors": [], "warnings": [ { "code": "NO_HASHTAGS", "message": "Posts with hashtags typically get more reach on Twitter" } ] }, "LINKEDIN": { "valid": true, "errors": [], "warnings": [] } } }

Bulk Generate Check

GET /v1/posts/bulk-generate-check

Check for scheduling conflicts before bulk-generating posts. Returns a warning and a count of already-scheduled posts in the requested date range.

Query Parameters

ParameterTypeRequiredDescription
accountIdstringYesAccount ID to check for conflicts
startDatestringYesStart of the range (ISO 8601)
endDatestringYesEnd of the range (ISO 8601)
curl "https://api.voxburst.io/v1/posts/bulk-generate-check?accountId=acc_123&startDate=2026-04-01T00:00:00Z&endDate=2026-04-30T23:59:59Z" \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "warning": "duplicate_detected", "warningMessage": "A similar bulk generation was run recently for this account and platform. You may be creating duplicate posts.", "overlappingScheduledCount": 12 }

When no conflict is found, warning is null, warningMessage is omitted, and overlappingScheduledCount is 0:

{ "warning": null, "overlappingScheduledCount": 0 }

Pre-flight Check

GET /v1/posts/preflight

Run distribution intelligence pre-flight checks for a set of accounts before creating a post. Returns risk tier and scheduling guidance per platform.

Query Parameters

ParameterTypeDescription
platformsstringComma-separated platform constants to check (e.g. TWITTER,INSTAGRAM)
textstringOptional post text to include in the check
curl "https://api.voxburst.io/v1/posts/preflight?platforms=TWITTER,INSTAGRAM" \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "platformResults": [ { "platform": "INSTAGRAM", "tier": "GREEN", "tierLabel": "Optimal", "guidance": [], "isVerified": true } ] }

Bulk Create Posts

POST /v1/posts/bulk

Create up to 50 posts in a single request. Each post in the posts array is processed independently. Add "dryRun": true to validate without creating any posts.

Required scopes: posts:write

Request Body

FieldTypeRequiredDescription
postsarrayYes1–50 post objects. Each item accepts the same fields as POST /v1/posts (see Create Post for the full field list).
posts[].contentstringYesPost text (max 10,000 chars)
posts[].accountIdsstring[]YesTarget account IDs (max 10)
posts[].scheduledForstringNoISO 8601 publish time
posts[].saveAsDraftbooleanNoSave as draft regardless of scheduledFor
posts[].contentTypestringNoTEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, or REEL
posts[].mediastring[]NoMedia IDs (max 10)
posts[].platformOverridesobjectNoPer-platform { content } overrides
posts[].platformMetadataobjectNoPer-platform metadata overrides (e.g. TIKTOK.tiktokPrivacyLevel)
posts[].firstCommentstringNoFirst comment text (max 2,200 chars). Silently skipped on unsupported platforms.
posts[].firstCommentDelayintegerNoSeconds to wait before the first comment (0–60)
posts[].threadPostsobject[]NoThread items (X only). Max 25.
posts[].tagsstring[]NoInternal tags (max 20)
posts[].metadataobjectNoArbitrary key-value metadata
dryRunbooleanNoIf true, validates all posts but creates nothing. Returns status: "valid" or status: "failed" per item (HTTP 200).

Idempotency: POST /v1/posts/bulk accepts the Idempotency-Key header. Provide a unique key per bulk request to prevent duplicate batch submissions on retries. On a replay, the entire original 207 response is returned.

curl -X POST https://api.voxburst.io/v1/posts/bulk \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "posts": [ { "content": "Post one", "accountIds": ["acc_123"] }, { "content": "Post two", "accountIds": ["acc_456"], "scheduledFor": "2026-04-20T10:00:00Z" } ] }'

Response (207)

{ "dry_run": false, "total": 2, "created": 2, "failed": 0, "results": [ { "index": 0, "status": "created", "postId": "post_abc1" }, { "index": 1, "status": "created", "postId": "post_abc2" } ] }

Dry runs return HTTP 200 instead of 207. Each results item has status: "valid" or status: "failed" with an errors array when invalid.

Bulk semantics

PropertyValue
AtomicityNon-atomic — each post is created independently. A failure on item 3 does not roll back items 0–2.
Success responseHTTP 207 Multi-Status
Per-item result{ "index": N, "status": "created" | "failed", "postId"?: string, "errors"?: object[] }
Partial successPossible — check every results[].status individually
Max posts per request50
Dry runSet "dryRun": true — validates all items, creates nothing, returns HTTP 200
QuotaEach successfully created post counts against your plan’s post limit

When a post in the batch fails, its results entry contains "status": "failed" and an errors array describing the validation or creation error. Successfully created posts are not affected.


Bulk Video Posts

POST /v1/posts/bulk-video

Create up to 20 posts in a single request, each tied to a pre-uploaded validated video. Use this after bulk video upload (see Bulk Upload in the Media docs) and validation.

Required scopes: posts:write

Request Body

FieldTypeRequiredDescription
postsarrayYes1–20 video post descriptors
posts[].mediaIdstringYesID of a READY media record for the video
posts[].accountIdsstring[]YesAccounts to post this video from
posts[].scheduledForstringYesISO 8601 scheduled time for this post
posts[].contentstringNoCaption / post text
posts[].tagsstring[]NoInternal tags
curl -X POST https://api.voxburst.io/v1/posts/bulk-video \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "posts": [ { "mediaId": "media_abc123", "accountIds": ["acc_123"], "scheduledFor": "2026-04-20T10:00:00Z", "content": "Watch our latest reel!" }, { "mediaId": "media_def456", "accountIds": ["acc_123", "acc_456"], "scheduledFor": "2026-04-20T12:00:00Z", "content": "Behind the scenes!" } ] }'

Response (207)

{ "created": [ { "id": "post_xyz1", "content": "Watch our latest reel!", "status": "scheduled", "scheduledFor": "2026-04-20T10:00:00Z", "platforms": [ "..." ] } ], "warnings": [] }

The response uses HTTP 207 (Multi-Status). Each item in created is a full post object (same shape as POST /v1/posts). Check the warnings array for any videos that could not be posted (e.g. media not in READY state) — these are excluded from created.


Recurring Posts

Recurring rules allow a published or scheduled post to be automatically re-published on a repeating schedule defined by an iCal RRULE  string.

Plan limits

PlanActive recurring rules
Free0
Starter3
Pro / AgencyUnlimited

Create Recurrence Rule

POST /v1/posts/:id/recurrence

Attach a recurrence rule to a PUBLISHED or SCHEDULED post. Each re-run clones the post and publishes it fresh.

Required scopes: posts:write

Request Body

FieldTypeRequiredDescription
recurrenceRulestringYesiCal RRULE string (e.g. RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR)
timezonestringYesIANA timezone identifier (e.g. America/New_York)
maxOccurrencesintegerNoStop after this many runs. Omit for indefinite.
startAtstringNoISO 8601 datetime — first run will not occur before this time. Defaults to now.
curl -X POST https://api.voxburst.io/v1/posts/post_abc123/recurrence \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "recurrenceRule": "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR", "timezone": "America/New_York", "maxOccurrences": 24 }'

Response (201)

Returns the full recurrence rule record including the computed nextRunAt.


Get Recurrence Rule

GET /v1/posts/:id/recurrence

curl https://api.voxburst.io/v1/posts/post_abc123/recurrence \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Update Recurrence Rule

PATCH /v1/posts/:id/recurrence

Update or pause/resume an existing recurrence rule.

Required scopes: posts:write

FieldTypeDescription
recurrenceRulestringNew RRULE string
timezonestringNew IANA timezone
maxOccurrencesinteger | nullNew limit, or null to remove
enabledbooleanfalse to pause; true to resume

Delete Recurrence Rule

DELETE /v1/posts/:id/recurrence

Soft-disables the recurrence rule. The source post and all previously created clone posts are preserved.

Required scopes: posts:write

Response: 204 No Content


Cancel Post

POST /v1/posts/:id/cancel

Cancel a scheduled post. Only posts in draft or scheduled status can be cancelled. Cancelled posts are not deleted and remain visible in the post list with status: "cancelled".

Required scopes: posts:write

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/cancel \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Unpublish Post

POST /v1/posts/:id/unpublish

Attempts to delete the published post from each connected social platform, then marks the post as unpublished in VoxBurst. Only published or partial posts can be unpublished.

Required scopes: posts:write

Platforms that support post deletion will have the post removed. Platforms that do not support deletion are returned in unsupported_platforms — you must remove posts on those platforms manually.

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/unpublish \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "results": { "INSTAGRAM": { "success": true }, "TWITTER": { "success": false, "reason": "platform_not_supported" } }, "unsupported_platforms": ["TWITTER"], "note": "Posts on unsupported platforms must be deleted manually." }
FieldTypeDescription
resultsobjectPer-platform outcome map. Keys are platform constants (UPPERCASE).
results[platform].successbooleanWhether the post was successfully deleted from this platform
results[platform].reasonstringReason for failure, when success is false. platform_not_supported means deletion is not available for this platform.
unsupported_platformsstring[]Platforms where deletion is not supported. Posts on these platforms must be removed manually.
notestring | undefinedHuman-readable explanation when unsupported_platforms is non-empty.

The VoxBurst post is marked unpublished regardless of per-platform deletion outcomes. Check results and unsupported_platforms to determine whether manual cleanup is needed on any platform.


Get Post Replies

GET /v1/posts/:id/replies

Fetches replies for a published Threads post.

Required scopes: posts:read

Threads only. Returns { "data": [] } for posts not published on Threads. For posts published on Threads, replies are fetched using the connected Threads account’s access token.

If the connected Threads account does not have the threads_read_replies permission (granted at OAuth connection time), the response is always { "data": [] } — no error is returned. To grant this permission, reconnect the Threads account via OAuth.

Returns at most 25 replies per call.

curl https://api.voxburst.io/v1/posts/post_abc123/replies \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "data": [ { "id": "string", "text": "string", "username": "string", "timestamp": "2026-06-15T14:05:00Z", "permalink": "https://www.threads.net/@username/post/ABC123" } ] }
FieldTypeDescription
dataarrayList of reply objects. Empty when no replies exist, the post was not published on Threads, or threads_read_replies permission is not granted.
data[].idstringPlatform-native reply ID
data[].textstringReply text content
data[].usernamestringUsername of the reply author
data[].timestampstringISO 8601 timestamp of the reply
data[].permalinkstringPublic URL of the reply on Threads

Clone Post

POST /v1/posts/:id/clone

Create a copy of an existing post as a new draft. The clone inherits the original’s content, media, platform targets, and overrides, but is saved as draft with no scheduled time and no platform publish records.

Required scopes: posts:write

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/clone \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response (201)

Returns the full post object of the newly created draft.


Duplicate Post

POST /v1/posts/:id/duplicate

Alias for Clone — creates a copy of a post as a new draft. Behaves identically to /clone.

Required scopes: posts:write

curl -X POST https://api.voxburst.io/v1/posts/post_abc123/duplicate \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

CSV Import

Import posts in bulk from a CSV file. The import is asynchronous — you upload the file, receive a jobId, then poll for completion.

Download Template

GET /v1/posts/import/template

Returns a CSV file with the correct column headers and example rows.

curl https://api.voxburst.io/v1/posts/import/template \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -o voxburst-import-template.csv

Upload CSV

POST /v1/posts/import

Required scopes: posts:write

Upload a CSV file to create posts in bulk. Send the file as multipart/form-data with the file in the file field. Add ?dry_run=true to validate without creating any posts.

# Dry run (validate only) curl -X POST "https://api.voxburst.io/v1/posts/import?dry_run=true" \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -F "file=@posts.csv;type=text/csv" # Real import curl -X POST https://api.voxburst.io/v1/posts/import \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \ -F "file=@posts.csv;type=text/csv"

Response (202)

{ "jobId": "job_import_abc123", "status": "queued" }

Check Import Status

GET /v1/posts/import/:jobId/status

Poll this endpoint every 2–5 seconds until status is complete or failed.

curl https://api.voxburst.io/v1/posts/import/job_import_abc123/status \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"
{ "jobId": "job_import_abc123", "status": "done", "totalRows": 44, "processedRows": 44, "successCount": 42, "failureCount": 2, "errors": [ { "row": 12, "message": "Invalid scheduledFor date format" } ], "createdPostIds": ["post_abc1", "post_abc2"] }
statusMeaning
queuedQueued, not yet started
processingImport in progress
doneFinished — check successCount and errors
failedImport job itself failed (e.g. invalid CSV structure)

Use dry_run=true to validate your CSV structure and catch row-level errors before committing the import.


Post Events

List Post Events

GET /v1/posts/:id/events

List all lifecycle events for a post in chronological order.

curl https://api.voxburst.io/v1/posts/post_abc123/events \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response

{ "events": [ { "id": "evt_001", "type": "post.created", "createdAt": "2026-04-01T10:00:00Z", "data": {} }, { "id": "evt_002", "type": "post.published", "createdAt": "2026-04-01T14:00:00Z", "data": { "status": "published" } } ] }

Get Latest Post Event

GET /v1/posts/:id/events/latest

Returns the most recent lifecycle event for a post.

curl https://api.voxburst.io/v1/posts/post_abc123/events/latest \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"
Last updated on