Accounts
Connect and manage social media accounts. VoxBurst supports OAuth-based account connection for all major platforms.
Base URL
https://api.voxburst.io/v1/accountsList Accounts
GET /v1/accounts
List all connected social media accounts.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status. Accepted values: ACTIVE, INACTIVE, ERROR, DISCONNECTED, or all. Uppercase required for query params; response values are lowercase. DISCONNECTED accounts are excluded by default unless includeArchived=true is passed. |
platform | string | — | Filter by platform slug (e.g. twitter) |
includeArchived | boolean | false | When false (default), DISCONNECTED accounts are excluded from results. Pass true to include disconnected (archived) accounts in the response. |
limit | integer | 20 | Results per page |
cursor | string | — | Pagination cursor from previous response |
Default behavior changed 2026-06-15: Previously, DISCONNECTED accounts were implicitly included in list results. They are now excluded by default. Integrations that iterate all accounts and act on disconnected ones must add includeArchived=true to their requests.
curl "https://api.voxburst.io/v1/accounts?status=ACTIVE&platform=twitter" \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response
{
"data": [
{
"id": "acc_123",
"platform": "twitter",
"username": "@voxburst",
"displayName": "VoxBurst",
"avatarUrl": "https://cdn.example.com/avatar.jpg",
"accountType": "personal",
"status": "active",
"connectedAt": "2026-01-15T10:00:00Z"
}
],
"pagination": {
"has_more": false,
"next_cursor": null,
"limit": 20
}
}The list is paginated using cursor-based pagination. Pass cursor=<nextCursor> as a query parameter to fetch the next page.
Connect Account
POST /v1/accounts/connect/:platform
Initiate OAuth connection for a social media platform. Returns an authorization URL to redirect the user to.
Required scopes: accounts:write
Supported platforms: twitter, linkedin, instagram, facebook, bluesky, threads, youtube, mastodon, tiktok, pinterest, snapchat, reddit, telegram, googlebusiness, whatsapp
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
callbackUrl | string | Yes | URI to redirect to after OAuth authorization |
forcePrompt | boolean | No | If true, forces the OAuth consent screen to re-display even if the user has previously authorized |
pageMode | boolean | No | For LinkedIn: true to connect as a LinkedIn Page (requires Pages app credentials), false or omit to connect as a personal profile |
curl -X POST https://api.voxburst.io/v1/accounts/connect/linkedin \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://yourapp.com/oauth/callback",
"pageMode": true
}'Response
{
"authorizationUrl": "https://x.com/i/oauth2/authorize?...",
"state": "oauth_state_token"
}Redirect your user to the authorizationUrl. After authorization, the platform will redirect back to your callbackUrl with a code and state parameter.
The callbackUrl you pass here is validated server-side against an allowlist of permitted domains. URIs that do not match an approved domain are rejected. Use your production app domain (HTTPS required) or localhost for local development.
OAuth Callback
POST /v1/accounts/callback/:platform
Complete the OAuth flow by exchanging the authorization code for access tokens.
Required scopes: accounts:write
curl -X POST https://api.voxburst.io/v1/accounts/callback/twitter \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"code": "oauth_authorization_code",
"state": "oauth_state_token"
}'Response
{
"account": {
"id": "acc_789",
"platform": "twitter",
"username": "@newuser",
"displayName": "New User",
"status": "active",
"connectedAt": "2026-02-20T10:00:00Z"
}
}Disconnect Account
DELETE /v1/accounts/:id
Disconnect and remove a social media account.
Required scopes: accounts:write
curl -X DELETE https://api.voxburst.io/v1/accounts/acc_123 \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response (200):
{ "success": true }Refresh Account Token
POST /v1/accounts/:id/refresh
Manually refresh the OAuth token for an account. VoxBurst refreshes tokens automatically, but this endpoint is available for troubleshooting.
Required scopes: accounts:write
curl -X POST https://api.voxburst.io/v1/accounts/acc_123/refresh \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response (200):
{ "success": true }Test Connection
POST /v1/accounts/:id/test
Test the connection for a social account by making a lightweight API call to the platform.
curl -X POST https://api.voxburst.io/v1/accounts/acc_123/test \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response
{
"success": true,
"message": "Connection verified"
}On success, some platforms return additional details:
{
"success": true,
"message": "Connection verified",
"details": {
"platformUserId": "1234567890",
"username": "@voxburst",
"displayName": "VoxBurst"
}
}Get Account
GET /v1/accounts/:id
Retrieve a single connected account by ID.
curl https://api.voxburst.io/v1/accounts/acc_123 \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"List Pages
GET /v1/accounts/:id/pages
List the pages or business accounts associated with a connected account. Currently supported for LinkedIn and Instagram (Facebook Login) accounts.
- LinkedIn: Returns the organizations the connected user administers. Use these to populate a page selector before creating a LinkedIn post targeting a company page.
- Instagram (Facebook Login only): Returns the Instagram Business accounts linked to the connected Facebook user token. Legacy Instagram Direct Login accounts return an empty
pagesarray.
For all other platforms, returns an empty pages array.
curl https://api.voxburst.io/v1/accounts/acc_123/pages \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response — LinkedIn
{
"pages": [
{
"id": "page_001",
"name": "VoxBurst Official",
"urn": "urn:li:organization:12345678",
"type": "organization",
"logoUrl": "https://cdn.example.com/logo.png"
}
]
}Response — Instagram
{
"pages": [
{
"id": "<ig_user_id>",
"name": "@username",
"pageId": "<fb_page_id>",
"pageName": "Page Display Name",
"type": "instagram",
"avatarUrl": "https://..."
}
]
}The response shape differs by platform. LinkedIn entries include urn and logoUrl; Instagram entries include pageId, pageName, type, and avatarUrl. Both use the top-level id and name fields.
Select Page
POST /v1/accounts/:id/select-page
Persist a page selection for an account. Supported for LinkedIn and Instagram accounts.
- LinkedIn: Saves the selected LinkedIn Page. Subsequent posts to this account are published to the selected page.
- Instagram (Facebook Login only): Re-fetches Instagram Business accounts from the Graph API, matches on
pageId, and updates the account withusername,instagramAccountId,pageAccessToken,pageId,pageName, andoauthVersion: 'fb_login'. Returns400 INVALID_PAGE_IDif the providedpageIdis not found among the accounts accessible via the connected Facebook token.
Required scopes: accounts:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
pageId | string | Yes | ID of the page to select. For LinkedIn, this is the page’s internal ID. For Instagram, this is the Facebook Page ID (pageId from the List Pages response). |
pageName | string | No | Display name of the selected page |
# LinkedIn example
curl -X POST https://api.voxburst.io/v1/accounts/acc_123/select-page \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "pageId": "page_001", "pageName": "VoxBurst Official" }'
# Instagram example
curl -X POST https://api.voxburst.io/v1/accounts/acc_456/select-page \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "pageId": "<fb_page_id>", "pageName": "Page Display Name" }'Error Codes
| Code | HTTP | Description |
|---|---|---|
INVALID_PAGE_ID | 400 | Instagram only — the provided pageId was not found among the Instagram Business accounts accessible via the connected Facebook token. Re-fetch pages with GET /v1/accounts/:id/pages and use a valid pageId from that response. |
YouTube Playlists
GET /v1/accounts/:id/youtube/playlists
Returns the YouTube playlists accessible by a connected YouTube account. Use this to populate a playlist selector before creating a YouTube post.
curl https://api.voxburst.io/v1/accounts/acc_123/youtube/playlists \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response (200)
{
"playlists": [
{ "id": "PLxxxxxxxxxxxxxxxxxx", "title": "My Tutorial Series" },
{ "id": "PLyyyyyyyyyyyyyyyyyy", "title": "Product Demos" }
]
}If the connected account does not have the YouTube playlist read scope, the response returns an empty list with a requiresReauth flag:
{
"playlists": [],
"requiresReauth": true
}When requiresReauth is true, prompt the user to reconnect their YouTube account to grant the required scope.
Account Backfill
Get Backfill Status
GET /v1/accounts/:id/backfill
Get the backfill status for a connected account. Backfill pulls historical analytics from the platform.
curl https://api.voxburst.io/v1/accounts/acc_123/backfill \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Trigger Backfill
POST /v1/accounts/:id/backfill
Trigger a historical analytics backfill for a connected account.
Required scopes: accounts:write
curl -X POST https://api.voxburst.io/v1/accounts/acc_123/backfill \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Optimal Posting Times
GET /v1/accounts/:id/optimal-times
Returns the optimal posting time slots for a connected account based on historical engagement data.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
timezone | string | IANA timezone identifier (default: America/Chicago) |
limit | integer | Number of slots to return (1–10, default: 5) |
curl "https://api.voxburst.io/v1/accounts/acc_123/optimal-times?timezone=America/New_York&limit=5" \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Instagram Audio Search
GET /v1/instagram/audio/search
Search for audio tracks available for use in Instagram Reels. Returns trending tracks when q is omitted, or filtered results when a search term is provided.
Auth: Bearer token required (read:posts scope)
Feature flag: This endpoint returns 503 FEATURE_DISABLED until the INSTAGRAM_AUDIO_ENABLED environment variable is set. The feature is currently pending Meta App Review and is not yet available in production.
Facebook Login required: The account identified by accountId must have been connected via Facebook Login. Legacy Instagram Direct Login accounts return 400 — prompt the user to reconnect via Facebook Login to use this endpoint.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
accountId | string | Yes | ID of a connected Instagram account (must be Facebook Login-connected) |
q | string | No | Search term. Omit to retrieve trending tracks. |
limit | integer | No | Number of tracks to return (1–50, default: 20) |
# Trending tracks
curl "https://api.voxburst.io/v1/instagram/audio/search?accountId=acc_456&limit=10" \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"
# Search by term
curl "https://api.voxburst.io/v1/instagram/audio/search?accountId=acc_456&q=summer&limit=20" \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"Response (200)
{
"tracks": [
{
"id": "1234567890",
"title": "Track Title",
"artistName": "Artist Name",
"audioUrl": "https://...",
"coverImageUri": "https://...",
"durationMs": 30000
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
tracks | object[] | Array of audio track objects |
tracks[].id | string | Track identifier (use this when attaching audio to a Reel) |
tracks[].title | string | Track title |
tracks[].artistName | string | undefined | Artist name (may be absent for original audio) |
tracks[].audioUrl | string | undefined | Direct audio URL (may be absent depending on Meta’s response) |
tracks[].coverImageUri | string | undefined | Cover art URL |
tracks[].durationMs | number | undefined | Track duration in milliseconds |
Error Codes
| Code | HTTP | Description |
|---|---|---|
FEATURE_DISABLED | 503 | Audio search is not yet enabled in this environment (pending Meta App Review) |
GRAPH_API_ERROR | 502 | Meta Graph API returned an error. The message field contains the Meta error detail. |
| — | 404 | The accountId was not found or does not belong to this workspace |
| — | 400 | The account does not have a pageAccessToken — it was connected via Direct Login rather than Facebook Login. Prompt the user to reconnect via Facebook Login. |
Shared Accounts
When the same social media account (identified by platform + platform user ID) is connected in more than one VoxBurst workspace, VoxBurst marks it as a shared account. Posts published from any workspace that share the account count toward a combined platform publishing limit.
When you connect an account that is already connected in another workspace, the connect response includes a warning:
{
"account": { "id": "acc_789", "platform": "instagram", "username": "brandname", ... },
"warning": "This account is already connected in another workspace. Posts to this account from all connected workspaces share a combined publishing limit."
}The isSharedAccount field on the account record is true when this condition applies.
Account Fields
All string enum fields (platform, status, accountType) are returned as lowercase strings in REST API responses.
| Field | Type | Description |
|---|---|---|
id | string | Account ID (cuid2 format, starts with c) |
platform | string | Platform slug — always lowercase (e.g. "twitter", "instagram") |
username | string | null | Platform username or handle |
displayName | string | null | Display name on the platform |
avatarUrl | string | null | Profile avatar URL |
accountType | string | null | personal, business, or creator — platform-dependent |
status | string | See Account Status below |
connectedAt | string | ISO 8601 timestamp of when the account was connected |
needsPlaylistReauth | boolean | YouTube only — true when the account lacks the playlist read scope |
Account Status
All status and platform values in REST API responses are lowercase strings (e.g. "twitter", "active"). This applies to all account endpoints: list, get, connect callback, refresh, and test.
Query parameters that filter by status or platform accept UPPERCASE values (e.g. status=ACTIVE, platform=TWITTER). The response always returns lowercase regardless of how the query parameter was cased.
Webhook payloads (account.connected, account.error) use UPPERCASE for platform and status — this is intentionally different from REST responses. See Webhooks for details.
| Status (response) | Query parameter value | Description |
|---|---|---|
active | ACTIVE | Account is connected and tokens are valid |
expired | (use ERROR) | Token has expired — call /refresh or reconnect |
disconnected | DISCONNECTED | Account was disconnected by the user. Excluded from list results by default — pass includeArchived=true to include. |
suspended | (not filterable) | Account has been suspended by the platform |
error | ERROR | Publishing error — check the account’s error details |
OAuth Edge Cases
Reconnecting an already-connected account
If a user completes the OAuth flow for an account that is already connected in the same workspace (same platform + platform user ID), VoxBurst upserts the account record: tokens are refreshed, status is reset to ACTIVE, and any previous errors are cleared. No duplicate account is created.
Connecting an account that exists in another workspace
When the same social account is connected across multiple workspaces, VoxBurst flags it as a shared account. The callback response includes a warning field:
{
"account": { "id": "acc_789", "platform": "instagram", ... },
"warning": "This account is already connected in another workspace. Posts to this account from all connected workspaces share a combined publishing limit."
}OAuth state expiry
The OAuth state token stored during the connect step expires after 10 minutes. If the user does not complete authorization within that window, the callback endpoint will return:
{ "error": { "code": "INVALID_PARAM", "message": "Invalid or expired OAuth state" } }Restart the connect flow to get a fresh state token.
OAuth error codes
| Code | HTTP | When it occurs |
|---|---|---|
INVALID_PARAM | 400 | Invalid or expired state, or invalid auth code |
PLATFORM_RESTRICTED | 403 | Platform is not permitted in this workspace |
PLAN_LIMIT_EXCEEDED | 402 | Workspace has reached its connected account limit for the current plan |
Token expiry and refresh
VoxBurst refreshes tokens automatically when they are within 5 minutes of expiry. If automatic refresh fails (e.g. the user revoked access), the account status is set to EXPIRED or ERROR. At that point:
- Call
POST /v1/accounts/:id/refreshto attempt a manual refresh - If refresh fails, the user must reconnect via the full OAuth flow
Bluesky and Mastodon accounts do not use expiring tokens and do not require refresh.
User Endpoints
Store GA4 Client ID
POST /v1/users/me/ga4-client-id
Stores the browser-side GA4 client ID for server-side Measurement Protocol event stitching. Call this once per session after the GA4 client ID becomes available in the browser (typically via gtag('get', ...) or ga.getAll()[0].get('clientId')).
Auth: Bearer token required
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
clientId | string | Yes | GA4 client ID in the format XXXXXXXXX.XXXXXXXXX (two numeric segments separated by a dot). Must match /^\d+\.\d+$/. |
curl -X POST https://api.voxburst.io/v1/users/me/ga4-client-id \
-H "Authorization: Bearer vb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "clientId": "1234567890.9876543210" }'Response (200)
{ "ok": true }Error Codes
| HTTP | Description |
|---|---|
| 400 | clientId is missing or does not match the required format |
| 401 | Bearer token is missing or invalid |