Skip to Content
API ReferenceAccounts

Accounts

Connect and manage social media accounts. VoxBurst supports OAuth-based account connection for all major platforms.

Base URL

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

List Accounts

GET /v1/accounts

List all connected social media accounts.

Query Parameters

ParameterTypeDefaultDescription
statusstringFilter 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.
platformstringFilter by platform slug (e.g. twitter)
includeArchivedbooleanfalseWhen false (default), DISCONNECTED accounts are excluded from results. Pass true to include disconnected (archived) accounts in the response.
limitinteger20Results per page
cursorstringPagination 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

FieldTypeRequiredDescription
callbackUrlstringYesURI to redirect to after OAuth authorization
forcePromptbooleanNoIf true, forces the OAuth consent screen to re-display even if the user has previously authorized
pageModebooleanNoFor 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. Accounts connected via Instagram Direct Login (oauthVersion: "instagram_login") return an empty pages array — only Facebook Login-connected accounts (oauthVersion: "fb_login") have access to page data.

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 with username, instagramAccountId, pageAccessToken, pageId, pageName, and oauthVersion: 'fb_login'. Returns 400 INVALID_PAGE_ID if the provided pageId is not found among the accounts accessible via the connected Facebook token.

Required scopes: accounts:write

Request Body

FieldTypeRequiredDescription
pageIdstringYesID 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).
pageNamestringNoDisplay 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

CodeHTTPDescription
INVALID_PAGE_ID400Instagram 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.
PAGE_SELECTION_NOT_SUPPORTED400Instagram only — the account is connected via Instagram Direct Login (oauthVersion: "instagram_login"), which has no Facebook Pages to select. Page selection is a Facebook Login concept. To choose a Page, reconnect the account using Facebook Login for Business.
ACCOUNT_PAGE_MISMATCH400Instagram and Facebook — the selected Page does not belong to the account identified by :id. See Page ownership below.
DESTINATIONS_NOT_LOADED400The account’s destination list has not been populated yet, so the supplied pageId cannot be validated. Wait a moment and retry.

Page ownership

Each Page or Instagram Business account is stored as its own connected account. A Page may only be selected onto the account it actually belongs to — POST /v1/accounts/:id/select-page verifies ownership before writing and returns 400 ACCOUNT_PAGE_MISMATCH when the selected Page belongs to a different account. Nothing is written when this error is returned; the account is left exactly as it was.

This applies in three situations:

  • Instagram — the pageId resolves to a different Instagram Business account than the one identified by :id.
  • Instagram — the platform does not report the selected Page as the owner of this account’s Instagram Business account. This can happen if the Page↔account link changed on the platform after the account was connected.
  • Facebook — the account is already keyed to a specific Page, or the selected Page is already stored as its own connected account in the workspace.

What a client should do: re-fetch GET /v1/accounts/:id/pages and retry with a Page belonging to that account. Retrying with the same pageId will fail identically — this is not a transient condition. If the Page you want is administered by the same user but belongs to a different account, manage it through that account’s own id rather than reassigning this one. If neither applies, reconnect the account to refresh the Page↔account relationships.

GET /v1/accounts/:id/pages may list every Page reachable by the connected token, which is a superset of the Pages selectable onto any one account. Do not assume that every entry in that response is a valid pageId for the account you fetched it from. If you render a picker, scope it to the account being edited and be prepared to surface ACCOUNT_PAGE_MISMATCH.

Check oauthVersion on the account before offering page selection in your UI. An Instagram account with oauthVersion: "instagram_login" will always return PAGE_SELECTION_NOT_SUPPORTED here, and GET /v1/accounts/:id/pages returns an empty pages array for it — neither is a transient condition, and retrying will not change the outcome.


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.


TikTok Creator Info

GET /v1/accounts/:id/tiktok/creator-info

Returns TikTok’s creator info for a connected TikTok account — the connected creator’s nickname, the privacy levels they’re allowed to post with, and their duet/stitch/comment interaction settings. TikTok’s Content Sharing Guidelines require this to be fetched and rendered before a “Post to TikTok” screen is shown: the privacy options offered must exactly match privacyLevelOptions, and the duet/stitch/comment toggles must be disabled wherever the creator has turned those interactions off on their own account.

curl https://api.voxburst.io/v1/accounts/acc_tiktok_123/tiktok/creator-info \ -H "Authorization: Bearer vb_live_xxxxxxxxxxxxx"

Response (200) — available

{ "available": true, "creatorInfo": { "nickname": "jane.creates", "username": "jane.creates", "avatarUrl": "https://p16-sign.tiktokcdn.com/...", "privacyLevelOptions": ["PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "SELF_ONLY"], "commentDisabled": false, "duetDisabled": false, "stitchDisabled": true, "maxVideoPostDurationSec": 600, "canPost": true } }

Response (200) — unavailable

{ "available": false, "reason": "NO_TOKEN" }

Response Fields

FieldTypeDescription
availablebooleanfalse if creator info could not be retrieved — see reason
reasonstringPresent when available is false. One of NOT_TIKTOK, NO_TOKEN, QUERY_FAILED
messagestring | undefinedPresent when reason is QUERY_FAILED — human-readable detail
creatorInfo.nicknamestringCreator’s display name on TikTok
creatorInfo.usernamestringCreator’s TikTok handle
creatorInfo.avatarUrlstringCreator’s profile picture URL
creatorInfo.privacyLevelOptionsstring[]Privacy levels this creator is allowed to publish with. Pass one of these values as platformMetadata.TIKTOK.tiktokPrivacyLevel on Create Post — see Platform Metadata
creatorInfo.commentDisabledbooleantrue if the creator has disabled comments on their own account — comments cannot be enabled for this post regardless of tiktokAllowComment
creatorInfo.duetDisabledbooleantrue if the creator has disabled duets on their own account
creatorInfo.stitchDisabledbooleantrue if the creator has disabled stitches on their own account
creatorInfo.maxVideoPostDurationSecnumber | nullMaximum video duration this creator can post, in seconds
creatorInfo.canPostbooleanfalse when TikTok returned no privacy level options for this creator — the account cannot currently publish. POST /v1/posts will fail with TIKTOK_CREATOR_CANNOT_POST for this account until it resolves.

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

ParameterTypeDescription
timezonestringIANA timezone identifier (default: America/Chicago)
limitintegerNumber 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"

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 (oauthVersion: "fb_login"). Accounts connected via Instagram Direct Login return 400 — check the account’s oauthVersion before calling this endpoint. If the account’s flobProbe.flobAvailable is true, the user can upgrade to Facebook Login to enable this feature.

Query Parameters

ParameterTypeRequiredDescription
accountIdstringYesID of a connected Instagram account (must be Facebook Login-connected)
qstringNoSearch term. Omit to retrieve trending tracks.
limitintegerNoNumber 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

FieldTypeDescription
tracksobject[]Array of audio track objects
tracks[].idstringTrack identifier (use this when attaching audio to a Reel)
tracks[].titlestringTrack title
tracks[].artistNamestring | undefinedArtist name (may be absent for original audio)
tracks[].audioUrlstring | undefinedDirect audio URL (may be absent depending on Meta’s response)
tracks[].coverImageUristring | undefinedCover art URL
tracks[].durationMsnumber | undefinedTrack duration in milliseconds

Error Codes

CodeHTTPDescription
FEATURE_DISABLED503Audio search is not yet enabled in this environment (pending Meta App Review)
GRAPH_API_ERROR502Meta Graph API returned an error. The message field contains the Meta error detail.
404The accountId was not found or does not belong to this workspace
400The account does not have a pageAccessToken — it was connected via Instagram Direct Login (oauthVersion: "instagram_login") rather than Facebook Login. Check the account’s flobProbe.flobAvailable field; if true, the user can upgrade to Facebook Login to enable audio search.

Google Business Profile Locations

Connected Google Business Profile accounts can have multiple business locations. Use these endpoints to list available locations and configure which ones are selected for posting.

List GMB Locations

GET /v1/accounts/:id/gmb/locations

Returns all GMB locations available under a connected Google Business account, grouped by account, with each location’s current selection state.

Auth: Bearer token, workspace-scoped. The account identified by :id must be a GOOGLE_BUSINESS platform account.

curl https://api.voxburst.io/v1/accounts/acc_example123/gmb/locations \ -H "Authorization: Bearer eyJexample..."

Response (200)

{ "groups": [ { "accountName": "Acme Corp", "accountPath": "accounts/123456789", "locations": [ { "path": "accounts/123456789/locations/987654321", "title": "Acme Corp - Main St", "address": "123 Main St, Springfield, IL 62701", "selected": true }, { "path": "accounts/123456789/locations/111222333", "title": "Acme Corp - Oak Ave", "address": "456 Oak Ave, Springfield, IL 62702", "selected": false } ] } ], "selectedCount": 1 }

Response Fields

FieldTypeDescription
groupsobject[]Location groups keyed by GMB account
groups[].accountNamestringDisplay name of the GMB account
groups[].accountPathstringGMB account resource path
groups[].locationsobject[]Locations under this account
groups[].locations[].pathstringGMB location resource path — use this as the locationPath identifier
groups[].locations[].titlestringBusiness name for this location
groups[].locations[].addressstringFormatted street address
groups[].locations[].selectedbooleanWhether this location is currently selected for posting
selectedCountintegerTotal number of currently selected locations across all groups

Error Codes

HTTPDescription
404Account not found or does not belong to this workspace
400Account is not a GOOGLE_BUSINESS platform account
400Google Business token is missing or expired — prompt the user to reconnect

Save GMB Location Selection

PUT /v1/accounts/:id/gmb/locations

Saves the set of selected location paths for a connected GMB account. Only the locations listed in locationPaths will be marked as selected; all others will be deselected.

Auth: Bearer token, workspace-scoped. Requires accounts:write scope.

Request Body

FieldTypeRequiredDescription
locationPathsstring[]YesGMB location resource paths to select (max 100). Use the path values from GET /v1/accounts/:id/gmb/locations. Pass an empty array to deselect all locations.
curl -X PUT https://api.voxburst.io/v1/accounts/acc_example123/gmb/locations \ -H "Authorization: Bearer eyJexample..." \ -H "Content-Type: application/json" \ -d '{ "locationPaths": [ "accounts/123456789/locations/987654321" ] }'

Response (200)

{ "success": true, "selectedCount": 1 }

Error Codes

HTTPDescription
404Account not found or does not belong to this workspace
400Account is not a GOOGLE_BUSINESS platform account
400locationPaths contains more than 100 entries

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.

FieldTypeDescription
idstringAccount ID (cuid2 format, starts with c)
platformstringPlatform slug — always lowercase (e.g. "twitter", "instagram")
usernamestring | nullPlatform username or handle
displayNamestring | nullDisplay name on the platform
avatarUrlstring | nullProfile avatar URL
accountTypestring | nullpersonal, business, or creator — platform-dependent
statusstringSee Account Status below
connectedAtstringISO 8601 timestamp of when the account was connected
needsPlaylistReauthbooleanYouTube only — true when the account lacks the playlist read scope
oauthVersionstring | undefinedInstagram only. The OAuth path used to connect this account: "instagram_login" (Direct Login) or "fb_login" (Facebook Login for Business). Present on all connected Instagram accounts. Absent on all other platforms.
flobProbeobject | undefinedInstagram Direct Login accounts only. Eligibility information for upgrading to Facebook Login for Business. See Instagram OAuth paths below. Absent if the eligibility check has not yet completed, or if the account was connected via Facebook Login.
scopeHealthobject | undefinedResult of the permission check run when the account was connected. See Scope health below. Absent on accounts connected before this check shipped — absent is not the same as healthy.

Scope Health

When an account is connected or reconnected, VoxBurst compares the permissions the platform actually granted against the permissions each VoxBurst feature requires for that platform, and records the result on the account as scopeHealth.

This exists because a connection can succeed, and publishing can work, while a narrower operation on the same account cannot — the shortfall is otherwise invisible until a user attempts that operation. scopeHealth lets an integration surface the shortfall at connect time instead.

{ "id": "acc_123", "platform": "instagram", "status": "active", "scopeHealth": { "status": "DEGRADED", "degradedCapabilities": [ { "capability": "deletePost", "label": "deleting posts" } ], "message": "This connection is missing permissions for deleting posts. Reconnect the account and accept all requested permissions to restore it.", "verifiedAt": "2026-08-21T14:02:11.000Z" } }

Fields

FieldTypeDescription
scopeHealth.statusstringOK, DEGRADED, UNKNOWN, or NOT_APPLICABLE. See below.
scopeHealth.degradedCapabilitiesarrayCapabilities that cannot work with the permissions actually granted. Always [] unless status is DEGRADED.
scopeHealth.degradedCapabilities[].capabilitystringStable machine-readable capability key — see the table below. Safe to branch on.
scopeHealth.degradedCapabilities[].labelstringHuman-readable description of the capability, e.g. "deleting posts". Suitable for display. Wording may change; do not branch on it.
scopeHealth.degradedCapabilities[].grantedForOtherTargetsOnlyboolean | undefinedPresent and true when the permission was granted, but for a different Page or account rather than this one. The key is omitted otherwise.
scopeHealth.messagestring | undefinedOne user-facing sentence summarising the shortfall. Present only when status is DEGRADED.
scopeHealth.reasonstring | undefinedShort diagnostic explaining why the result is UNKNOWN or NOT_APPLICABLE. Opaque and unstable — log it, do not parse or display it.
scopeHealth.verifiedAtstring | undefinedISO 8601 timestamp of the check.

Note that scopeHealth never contains raw platform permission names. Branch on capability; display label or message.

Status values

StatusMeaningSuggested handling
OKEvery feature VoxBurst ships for this platform has the permissions it needs.No action.
DEGRADEDAt least one feature cannot work. degradedCapabilities is non-empty.Prompt the user to reconnect and accept all requested permissions. Disable the affected features in your UI.
UNKNOWNThe platform did not report which permissions it granted and no introspection was available.Neither healthy nor degraded — do not treat as either. Proceed, and handle permission errors at call time.
NOT_APPLICABLEThe platform has no permission-scope concept, or no scope-gated features.No action.

Capability keys

capabilitylabel
publishpublishing posts
deletePostdeleting posts
readCommentsreading comments
replyCommentsreplying to comments
readMessagesreading direct messages
sendMessagessending direct messages
insightsaudience insights and analytics
listDestinationslisting the Pages, channels or locations you can post to
readFollowersaudience discovery
readOwnPostsreading your published posts

New capability keys may be added over time. Treat an unrecognised capability as a generic degradation and fall back to displaying label.

scopeHealth absent is not status: "OK". The field is only populated for accounts connected or reconnected after this check shipped. On an older account it is omitted entirely, which means never verified — not no problems found. Do not infer health from its absence.

Verification never blocks a connect. An account with scopeHealth.status: "DEGRADED" is still status: "active" and will still publish, provided publish is not among its degradedCapabilities.


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 valueDescription
activeACTIVEAccount is connected and tokens are valid
expired(use ERROR)Token has expired — call /refresh or reconnect
disconnectedDISCONNECTEDAccount 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
errorERRORPublishing 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

CodeHTTPWhen it occurs
INVALID_PARAM400Invalid or expired state, or invalid auth code
PLATFORM_RESTRICTED403Platform is not permitted in this workspace
PLAN_LIMIT_EXCEEDED402Workspace has reached its connected account limit for the current plan

Instagram OAuth result error codes

When an Instagram OAuth attempt fails, the callback redirect includes an errorCode query parameter on the result page. These codes are distinct from the API error codes above — they are propagated via the redirect URL rather than a JSON error response. Integrations that present or process the OAuth result URL should handle these values:

CodeMeaning
NO_FACEBOOK_PAGESThe user has no Facebook Pages associated with their account
NO_IG_ON_PAGESFacebook Pages were found, but none are linked to an Instagram Business or Creator account
ACCOUNTS_CENTER_MISMATCHThe Instagram account is not associated with the user’s personal Accounts Center — usually the result of a Business Manager-only account
CREDENTIALS_NOT_CONFIGUREDInstagram OAuth credentials are not configured on this VoxBurst instance

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/refresh to 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.



Instagram OAuth Paths

VoxBurst supports two active OAuth paths for connecting Instagram accounts. The path used is recorded in the oauthVersion field on the account and governs which API scopes and endpoints are available.

PathoauthVersion valueOAuth hostDescription
Instagram Direct Login"instagram_login"www.instagram.comThe default path used in the VoxBurst app UI. Grants scopes for business content publishing, comments, insights, and messages directly via Instagram’s OAuth.
Facebook Login for Business"fb_login"www.facebook.comThe path always initiated when connecting via the API. Available for accounts where an Instagram Business account is linked to a Facebook Page through Accounts Center. Provides access to the Facebook Graph API in addition to Instagram publishing.

API behavior: POST /v1/accounts/connect/instagram always initiates the Facebook Login for Business (FLOB) path. There is no loginMethod parameter — the login path is not selectable via the API. Direct Login is the default in the VoxBurst app UI but is not available to API callers.

Checking FLOB eligibility

For Instagram accounts connected via Direct Login, VoxBurst runs an eligibility check after connection and writes the result to the flobProbe field. You can read this field from GET /v1/accounts/:id:

{ "id": "acc_example123", "platform": "instagram", "oauthVersion": "instagram_login", "flobProbe": { "flobAvailable": true, "facebookAccountId": "...", "checkedAt": "2026-07-06T01:29:37Z" } }
FieldTypeDescription
flobProbe.flobAvailablebooleantrue if this account is eligible for Facebook Login upgrade
flobProbe.facebookAccountIdstring | nullThe associated Facebook account identifier when eligible; null otherwise
flobProbe.checkedAtstringISO 8601 timestamp of the most recent eligibility check

flobProbe may be absent if the check has not yet completed. If you need to wait for it, poll GET /v1/accounts/:id — the field will be populated within a few seconds of account connection.

Feature availability by OAuth path

Some Instagram-specific endpoints require a Facebook Login-connected account (i.e. oauthVersion: "fb_login"):

Featureinstagram_loginfb_login
Feed posts (IMAGE, VIDEO, CAROUSEL, REEL)
First comment
Analytics / Insights
GET /v1/accounts/:id/pages (returns pages)✗ (empty)
GET /v1/instagram/audio/search

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

FieldTypeRequiredDescription
clientIdstringYesGA4 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

HTTPDescription
400clientId is missing or does not match the required format
401Bearer token is missing or invalid
Last updated on