Posts
Create, schedule, update, and publish social media posts across multiple platforms simultaneously.
Base URL
https://api.voxburst.io/v1/postsCreate 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
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Post content. Used as the default for all platforms unless overridden (max 10,000 characters). |
accountIds | string[] | Yes | IDs of connected accounts to post from. The platform is inferred from each account. |
scheduledFor | string | No | ISO 8601 datetime for scheduled publishing |
contentType | string | No | Content type hint: TEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, REEL |
saveAsDraft | boolean | No | If true, saves as draft regardless of scheduledFor |
platformOverrides | object | No | Per-platform content overrides — keyed by platform constant (e.g. "TWITTER"). See Content Overrides |
platformMetadata | object | No | Per-platform metadata overrides — keyed by platform constant (e.g. "TIKTOK"). See Platform Metadata |
media | string[] | No | IDs of previously uploaded media files |
tags | string[] | No | Internal tags for organization (not posted to platforms) |
personaId | string | No | ID of a persona profile to associate with this post |
queue | boolean | No | If true, picks the next available queue slot instead of requiring scheduledFor. Mutually exclusive with scheduledFor. |
threadPosts | object[] | No | Thread items for X/Threads posts (max 25 items). Each item: { sequence: number, content: string, mediaIds?: string[] }. Sequences must be 1-based and contiguous. |
firstComment | string | No | Text 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. |
firstCommentDelay | integer | No | Seconds 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. |
isManualSave | boolean | No | When true, bypasses the post.created webhook debounce window so the webhook fires immediately |
metadata | object | No | Arbitrary 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.
| Rule | Limit |
|---|---|
| Key length | Max 100 characters |
| String value length | Max 1,000 characters |
| Array value item count | Max 100 items |
| Array value item length | Max 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:
| Value | Description |
|---|---|
PUBLIC_TO_EVERYONE | Visible to all TikTok users |
MUTUAL_FOLLOW_FRIENDS | Visible to mutual followers only |
SELF_ONLY | Visible only to the account owner |
FOLLOWER_OF_CREATOR | Visible 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.
| Platform | First comment supported |
|---|---|
| ✅ | |
| ✅ | |
| ✗ † | |
| Threads | ✅ |
| TikTok | ✅ |
| YouTube | ✅ |
| ✅ | |
| Twitter / X | ✗ |
| Bluesky | ✗ |
| ✗ | |
| Snapchat | ✗ |
| Telegram | ✗ |
| Google Business | ✗ |
| Mastodon | ✗ |
† Facebook first comment — pending Meta API approval. The
pages_manage_engagementpermission required for Facebook first comments is currently under review by Meta. Posts that includefirstCommentwill 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:
- Targeting accounts (request) — use
accountIdsto specify which connected accounts to post from. The platform is inferred from each account. There is noplatformsfield on post creation or update. - Publish state (response) — the
platformsarray in every post response tracks per-destination publish status, errors, and URLs. This is read-only. - Content overrides (request) — the
platformOverridesobject 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
| Field | Type | Description |
|---|---|---|
id | string | Post ID |
content | string | Post content |
contentType | string | null | TEXT, IMAGE, VIDEO, CAROUSEL, STORY, REEL, or THREAD |
status | string | See Post Status |
scheduledFor | string | null | ISO 8601 publish time |
publishedAt | string | null | ISO 8601 timestamp when publishing completed |
platformRemovedAt | string | null | ISO 8601 timestamp when the post was deleted from the platform |
firstComment | string | null | Content of a comment auto-posted after publishing |
firstCommentDelay | number | null | Seconds to wait before posting the first comment |
queuePosition | number | null | Position in the publishing queue, if queued |
tags | string[] | Internal tags (not published to platforms) |
metadata | object | null | Arbitrary key-value metadata — see Metadata Constraints |
media | object[] | Attached media files with full metadata |
platforms | object[] | Per-platform publish state — see PostPlatform fields |
threadPosts | object[] | Thread items for X/Threads posts |
recurringPostId | string | null | ID of the recurring rule that generated this post |
isRecurring | boolean | null | true if this post is the source of a recurring rule |
personaId | string | null | Persona associated with this post |
createdAt | string | ISO 8601 creation timestamp |
updatedAt | string | ISO 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.
| Field | Type | Description |
|---|---|---|
postPlatformId | string | ID of this PostPlatform record — used with Fix Platform |
platform | string | Platform constant (lowercase, e.g. "twitter") |
accountId | string | Connected account ID |
accountName | string | Display name of the connected account |
status | string | pending, publishing, published, failed, or skipped |
platformPostId | string | null | Platform’s native post ID (success only) |
platformPostUrl | string | null | Public URL of the post (success only) |
publishedAt | string | null | ISO 8601 timestamp when this platform succeeded (success only) |
publishWarning | string | null | Non-fatal warning set during publishing. null when no warning occurred. See values below. |
error | string | null | Error message when status is failed |
attempts | object[] | History of publish attempts |
publishWarning values
| Value | Description |
|---|---|
TIKTOK_SELF_ONLY_FALLBACK | The 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:
| Field | Type | Required | Description |
|---|---|---|---|
content | string | No | Override 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.
| Field | Type | Required | Description |
|---|---|---|---|
sequence | integer | Yes | 1-based position in the thread. Must be contiguous starting from 1. |
content | string | Yes | Text of this thread item (max 280 characters for X) |
mediaIds | string[] | No | Media 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
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: draft, scheduled, publishing, published, failed, partial, cancelled, unpublished, archived, all |
platform | string | Filter by platform |
from | string | Start date (ISO 8601) |
to | string | End date (ISO 8601) |
limit | number | Results per page (default: 20, max: 100) |
cursor | string | Pagination 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.
| Field | Type | Description |
|---|---|---|
content | string | New post content (max 63,206 characters) |
contentType | string | Content type: TEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, REEL |
scheduledFor | string | New scheduled time (ISO 8601) |
clearSchedule | boolean | If true, removes the scheduled time and reverts the post to draft |
saveAsDraft | boolean | If true, saves as draft regardless of scheduledFor |
accountIds | string[] | Replace the set of target accounts |
media | string[] | Replace attached media |
platformOverrides | object | Per-platform content overrides |
platformMetadata | object | Per-platform metadata overrides — keyed by platform constant (e.g. "TIKTOK"). See Platform Metadata |
tags | string[] | Replace post tags |
personaId | string | Replace the associated persona |
threadPosts | object[] | Replace thread items (X/Threads only). Same schema as Create Post. |
firstComment | string | Replace the first comment text (max 2,200 characters) |
firstCommentDelay | integer | Seconds before the first comment is posted (0–60). Capped at 60s by the Lambda timeout. Only applies on platforms that support firstComment. |
platformRemovedAt | string | null | ISO 8601 timestamp when the post was removed from the platform, or null to clear |
metadata | object | Replace post metadata key-value pairs |
version | string | Optimistic 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
| Parameter | Type | Description |
|---|---|---|
sync | boolean | If true, waits for publishing to complete before responding (up to timeout seconds) |
timeout | integer | Max seconds to wait when sync=true (default: 30, max: 120) |
Request Headers
| Header | Description |
|---|---|
X-Callback-Url | Optional 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 attempt | Delay |
|---|---|
| 1st retry | 2 minutes |
| 2nd retry | 4 minutes |
| 3rd retry | 8 minutes |
| After 3rd | Platform 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
| Parameter | Description |
|---|---|
id | Post ID |
platformId | The 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.
| Field | Type | Required | Description |
|---|---|---|---|
mediaUrl | string | No | Replacement image URL (https:// only). Use when the original image was rejected by the platform (e.g., wrong aspect ratio, size exceeded). |
content | string | No | Replacement caption or post text for this platform only. |
refreshMedia | boolean | No | If 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
- Your webhook receives a
post.publishedevent withstatus: "PARTIAL". - Inspect the
platformsarray on the post — each entry has its ownstatus,error, andretryCount. - If the failure was transient (network error, rate limit), call
POST /v1/posts/:id/retryto automatically re-queue all failed platforms with backoff. - If the failure was caused by invalid content (e.g., Instagram rejected the image), call
POST /v1/posts/:id/platforms/:platformId/fixwith a correctedmediaUrlorcontent. - If a platform has
retryCount: 3, automatic retries are exhausted —/retrywill skip it. You must use/fixto reset the counter and resubmit.
Platform status values
| Status | Description |
|---|---|
pending | Waiting to be sent |
publishing | Currently being submitted to the platform |
published | Successfully posted |
failed | Publish attempt failed — eligible for retry or fix |
skipped | Excluded 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.
| Status | Description |
|---|---|
draft | Created but not scheduled or published |
approved | Approved through the approval workflow; queued for publishing |
scheduled | Queued for future publishing |
publishing | Currently being sent to platforms |
published | Successfully published to all platforms |
failed | Publishing failed on all platforms |
partial | Published to some platforms; at least one failed — use /retry or /fix to remediate |
cancelled | Post was cancelled before publishing |
unpublished | Post was unpublished after a successful publish |
archived | Post 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.
| Status | Edit (PATCH) | Publish now | Cancel | Retry | Fix | Clone | Delete |
|---|---|---|---|---|---|---|---|
draft | Yes | Yes | Yes | — | — | Yes | Yes |
approved | Yes | Yes | Yes | — | — | Yes | Yes |
scheduled | Yes | Yes | Yes | — | — | Yes | Yes |
publishing | — | — | — | — | — | Yes | — |
published | — | — | — | — | — | Yes | — |
partial | Yes | — | — | Yes | Yes | Yes | Yes |
failed | Yes | — | — | Yes | Yes | Yes | Yes |
cancelled | Yes | Yes | — | — | — | Yes | Yes |
unpublished | — | — | — | — | — | Yes | — |
archived | — | — | — | — | — | Yes | — |
Terminal statuses (no further automatic transitions): published, cancelled, unpublished, archived.
User-triggered transitions:
draft→scheduled: SetscheduledForin a PATCH or at creation timedraft/scheduled→publishing: CallPOST /v1/posts/:id/publishdraft/scheduled→cancelled: CallPOST /v1/posts/:id/cancelpublished/partial→unpublished: CallPOST /v1/posts/:id/unpublishfailed/partial→publishing: CallPOST /v1/posts/:id/retryorPOST /v1/posts/:id/platforms/:id/fix
System-triggered transitions:
scheduled→publishing: Scheduler cron fires whenscheduledForis reachedapproved→publishing: Approval workflow completespublishing→published: All platform workers complete successfullypublishing→partial: At least one platform fails, at least one succeedspublishing→failed: 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 — rejectedMinimum 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
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Post content to validate |
platforms | string[] | Yes | 1–15 platform constants to validate against (e.g. ["TWITTER", "INSTAGRAM"]). Must be valid Platform enum values. See platform constants. |
mediaIds | string[] | No | Media IDs to include in validation (checks dimensions, size, format) |
media | object[] | No | Inline media metadata for pre-upload checks. Each item: { type: "image"|"video"|"gif", url?: string, sizeBytes?: number, durationMs?: number } |
firstComment | string | No | Optional 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
| Parameter | Type | Required | Description |
|---|---|---|---|
accountId | string | Yes | Account ID to check for conflicts |
startDate | string | Yes | Start of the range (ISO 8601) |
endDate | string | Yes | End 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
| Parameter | Type | Description |
|---|---|---|
platforms | string | Comma-separated platform constants to check (e.g. TWITTER,INSTAGRAM) |
text | string | Optional 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
| Field | Type | Required | Description |
|---|---|---|---|
posts | array | Yes | 1–50 post objects. Each item accepts the same fields as POST /v1/posts (see Create Post for the full field list). |
posts[].content | string | Yes | Post text (max 10,000 chars) |
posts[].accountIds | string[] | Yes | Target account IDs (max 10) |
posts[].scheduledFor | string | No | ISO 8601 publish time |
posts[].saveAsDraft | boolean | No | Save as draft regardless of scheduledFor |
posts[].contentType | string | No | TEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, or REEL |
posts[].media | string[] | No | Media IDs (max 10) |
posts[].platformOverrides | object | No | Per-platform { content } overrides |
posts[].platformMetadata | object | No | Per-platform metadata overrides (e.g. TIKTOK.tiktokPrivacyLevel) |
posts[].firstComment | string | No | First comment text (max 2,200 chars). Silently skipped on unsupported platforms. |
posts[].firstCommentDelay | integer | No | Seconds to wait before the first comment (0–60) |
posts[].threadPosts | object[] | No | Thread items (X only). Max 25. |
posts[].tags | string[] | No | Internal tags (max 20) |
posts[].metadata | object | No | Arbitrary key-value metadata |
dryRun | boolean | No | If 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
| Property | Value |
|---|---|
| Atomicity | Non-atomic — each post is created independently. A failure on item 3 does not roll back items 0–2. |
| Success response | HTTP 207 Multi-Status |
| Per-item result | { "index": N, "status": "created" | "failed", "postId"?: string, "errors"?: object[] } |
| Partial success | Possible — check every results[].status individually |
| Max posts per request | 50 |
| Dry run | Set "dryRun": true — validates all items, creates nothing, returns HTTP 200 |
| Quota | Each 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
| Field | Type | Required | Description |
|---|---|---|---|
posts | array | Yes | 1–20 video post descriptors |
posts[].mediaId | string | Yes | ID of a READY media record for the video |
posts[].accountIds | string[] | Yes | Accounts to post this video from |
posts[].scheduledFor | string | Yes | ISO 8601 scheduled time for this post |
posts[].content | string | No | Caption / post text |
posts[].tags | string[] | No | Internal 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
| Plan | Active recurring rules |
|---|---|
| Free | 0 |
| Starter | 3 |
| Pro / Agency | Unlimited |
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
| Field | Type | Required | Description |
|---|---|---|---|
recurrenceRule | string | Yes | iCal RRULE string (e.g. RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR) |
timezone | string | Yes | IANA timezone identifier (e.g. America/New_York) |
maxOccurrences | integer | No | Stop after this many runs. Omit for indefinite. |
startAt | string | No | ISO 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
| Field | Type | Description |
|---|---|---|
recurrenceRule | string | New RRULE string |
timezone | string | New IANA timezone |
maxOccurrences | integer | null | New limit, or null to remove |
enabled | boolean | false 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."
}| Field | Type | Description |
|---|---|---|
results | object | Per-platform outcome map. Keys are platform constants (UPPERCASE). |
results[platform].success | boolean | Whether the post was successfully deleted from this platform |
results[platform].reason | string | Reason for failure, when success is false. platform_not_supported means deletion is not available for this platform. |
unsupported_platforms | string[] | Platforms where deletion is not supported. Posts on these platforms must be removed manually. |
note | string | undefined | Human-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"
}
]
}| Field | Type | Description |
|---|---|---|
data | array | List of reply objects. Empty when no replies exist, the post was not published on Threads, or threads_read_replies permission is not granted. |
data[].id | string | Platform-native reply ID |
data[].text | string | Reply text content |
data[].username | string | Username of the reply author |
data[].timestamp | string | ISO 8601 timestamp of the reply |
data[].permalink | string | Public 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.csvUpload 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"]
}status | Meaning |
|---|---|
queued | Queued, not yet started |
processing | Import in progress |
done | Finished — check successCount and errors |
failed | Import 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"