Skip to Content
Changelog

Changelog

This is the API & developer changelog — endpoint additions, schema changes, SDK releases, breaking changes, and deprecations. For product-level updates (new UI features, platform support, app improvements), see the Product changelog .

Track API changes, new features, and breaking updates for VoxBurst.

How to read this changelog

Each entry is labeled with one of:

LabelMeaning
AddedNew endpoints, fields, or features. Backwards-compatible.
ChangedModifications to existing behavior. Non-breaking unless labeled Breaking.
FixedBug fixes and correctness improvements.
BreakingChanges that require client updates. Announced 30 days in advance.
DeprecatedFields or endpoints that will be removed. 90-day removal window.
SecuritySecurity improvements.

Backwards compatibility: additive changes (new optional fields, new endpoints, new enum values) are deployed without prior notice. Clients must ignore unknown fields. Non-additive changes are always labeled Breaking and announced in advance.

Contract precedence: API source code → this documentation → OpenAPI spec → SDK docs → examples. See Contract Precedence.


2026-09-02 — TikTok posting requirements

Breaking:

  • platformMetadata.TIKTOK.tiktokPrivacyLevel is now required for TikTok posts. TikTok’s Content Sharing Guidelines require an explicit, user-made privacy selection before a post is published, so this field no longer has an implicit default. Omitting it fails the TikTok publish with TIKTOK_PRIVACY_LEVEL_REQUIRED; requesting a level the account doesn’t support fails with TIKTOK_PRIVACY_LEVEL_UNAVAILABLE. Both are returned as platforms[].error.code on the post response — other platforms in the same multi-platform post are unaffected. Fetch GET /v1/accounts/:id/tiktok/creator-info to get the account’s allowed privacy levels before submitting. See TikTok publish errors.

Added:

  • GET /v1/accounts/:id/tiktok/creator-info — returns the connected TikTok account’s allowed privacy levels, duet/stitch/comment interaction settings, and posting eligibility. See TikTok Creator Info.
  • New TikTok post metadata keys: TIKTOK.tiktokAllowComment, TIKTOK.tiktokAllowDuet, TIKTOK.tiktokAllowStitch, TIKTOK.tiktokCommercialContent, TIKTOK.tiktokBrandOrganic, TIKTOK.tiktokBrandedContent, TIKTOK.tiktokAutoAddMusic. See Platform Metadata.
  • New TikTok publish error codes: TIKTOK_CREATOR_CANNOT_POST, TIKTOK_DISCLOSURE_INCOMPLETE, TIKTOK_BRANDED_CONTENT_PRIVACY. See TikTok publish errors.

2026-08-25 — SDK v1.3.0, MCP Server v0.3.0, CLI v0.3.0

All three published packages have been updated. Install or upgrade with:

npm install @voxburst/sdk@latest npm install -g @voxburst/mcp-server@latest npm install -g @voxburst/cli@latest

@voxburst/sdk v1.3.0

Added:

  • client.accounts.selectPage(id, pageId, options?) — chooses which Page or organization a connected account publishes as, wrapping POST /v1/accounts/:id/select-page. Pass an empty pageId to clear the selection and publish as the personal profile (LinkedIn). Requires the accounts:write scope.

    await client.accounts.selectPage('acc_123', 'page_456', { pageName: 'Acme Co' })

    Throws ValidationError (HTTP 400) with one of three codes you should handle distinctly:

    CodeMeaning
    PAGE_SELECTION_NOT_SUPPORTEDAccount was connected via Instagram Direct Login, which has no Facebook Pages. Reconnect via Facebook Login for Business first.
    ACCOUNT_PAGE_MISMATCHThat Page belongs to a different Instagram account. Select the Page from the matching account’s own record.
    DESTINATIONS_NOT_LOADEDDestinations are still being fetched — retry.
  • client.contacts.get() now returns ContactDetail, which extends Contact with recent engagement history: comments (comments the contact left on your posts) and inboundMessages (DMs they sent to a connected account). client.contacts.list() continues to return the flat Contact — engagement history is detail-only.

    Both arrays are truncated by the API and carry no cursor, so treat them as a recent-activity preview rather than a complete record. An empty array is not proof the contact has never commented or messaged you. The response also carries deliveries and sequenceEnrollments, which are not yet typed in the SDK.

  • New exported types: Contact, ContactDetail, ContactComment, ContactInboundMessage.

Changed:

  • The client constructor now rejects a non-HTTPS baseUrl. Passing a baseUrl that does not begin with https:// throws immediately, so your API key cannot be sent over plaintext. There is no exemption for localhost.

    This affects you only if you set baseUrl explicitly — the default (https://api.voxburst.io/v1) is unchanged. If you point the SDK at a local or proxied API over http://, that call will now throw at construction time. Terminate TLS in front of the local endpoint, or keep an unpatched client pinned for that environment.

@voxburst/mcp-server v0.3.0

Added:

  • create_post and update_post accept contentType — one of TEXT, IMAGE, VIDEO, CAROUSEL, THREAD, STORY, REEL. Required for Instagram Reels (REEL) and carousels (CAROUSEL); use IMAGE for single-image Instagram posts.
  • First-comment support on both toolsfirstComment (text auto-posted as a comment immediately after publishing, up to 2,200 characters) and firstCommentDelay (seconds to wait first, 0–3600, default 0). Supported on Instagram, LinkedIn, Facebook, and YouTube.
  • update_post accepts media and platformOverridesmedia replaces the attached media with an array of VoxBurst media IDs; platformOverrides supplies per-platform content keyed by platform constant, e.g. { "INSTAGRAM": { "content": "..." } }.
  • create_post accepts mediaIds as an explicit alternative to mediaUrls, for agents that have already uploaded media and hold the IDs.

Fixed:

  • create_post silently dropped all media passed as mediaUrls. The tool forwarded mediaUrls straight to POST /v1/posts, which accepts only media IDs; the unknown field was ignored and every MCP-created post published without its media. Each URL is now registered via POST /v1/media/register to obtain an ID before the post is created, with content type inferred from the file extension. If you built an agent flow against mediaUrls and worked around the missing media, remove that workaround — media now attaches as intended.
  • list_posts accepts the full status set. Valid statuses were under-specified, so filtering by certain values was rejected.
  • Account schema corrected and batch tool scope tightened.
  • retry_post description corrected, and the server’s advertised version now tracks package.json — MCP clients previously saw a stale version string during capability negotiation.

@voxburst/cli v0.3.0

Added:

  • voxburst completion — generates a shell completion script for bash and zsh, covering top-level commands and the accounts and posts subcommands:

    eval "$(voxburst completion)" # add to .zshrc / .bashrc voxburst completion --shell bash
  • New posts subcommands: create, update <id> [content], cancel <id>, and validate, joining the existing list, get, and delete. All accept --json; create and update accept --platforms and --schedule (ISO 8601 or "YYYY-MM-DD HH:mm"), and cancel accepts --force to skip confirmation.

  • --draft / -d on voxburst post — saves as a draft instead of publishing immediately.

Fixed:

  • voxburst --version reported a stale version string that did not match the installed package.
  • accounts list --json output shape now matches the API response.

Breaking: No. Every change above is additive, except the SDK’s HTTPS requirement on an explicitly-supplied baseUrl — see the note under Changed.


2026-08-25 — API Key Scopes for AI, Billing, Invitations & Comment Automations

Added:

  • Seven new API key scopes. These endpoint groups were previously reachable only with a wildcard (*) key; scoped keys received 403 AUTHORIZATION_ERROR. They can now be requested by name when creating a key:

    ScopeGrants
    ai:readGET /v1/ai/status, /v1/ai/usage, /v1/ai/usage/history, /v1/ai/image-jobs, /v1/ai/image-jobs/:jobId
    ai:writeAll POST routes under /v1/ai, and GET /v1/ai/suggest-time/:accountId
    billing:readGET /v1/billing/current, /subscription, /usage, /scheduled-post-count, /credits
    billing:writeAll POST routes under /v1/billing, and GET /v1/billing/portal
    invitations:readGET /v1/workspaces/:id/invitations
    comment-automations:readGET /v1/comment-automations, /:id, /:id/logs, /:id/resolve-post
    comment-automations:writePOST, PATCH, DELETE on /v1/comment-automations

    As elsewhere, :write does not imply :read — request both if your integration needs to list and modify. See Authentication → Scopes for the full table.

    No change to existing keys. Wildcard keys reached these groups before and still do. A key holding other named scopes could not reach them before and still cannot — scopes are matched exactly, so these are available only to keys issued with them going forward.

    The hashtag-sets:read and hashtag-sets:write scopes were announced separately earlier the same day and are not repeated here — see the API Key Scopes for Hashtag Sets entry below.

    Three behaviours here are deliberate and will surprise you if you assume the usual method rule:

    1. GET /v1/ai/suggest-time/:accountId requires ai:write, not ai:read. It reserves and spends workspace AI credits before calling the provider, exactly as the POST routes do. The same applies to GET /v1/billing/portal, which requires billing:write — it mints a Stripe billing portal URL whose bearer can change the payment method, switch plan, or cancel. Both are carve-outs above their parent group. Every other GET in those two groups needs only the :read scope.

    2. There is no invitations:write. invitations:read grants GET /v1/workspaces/:id/invitations and nothing else. Invitation mutations are bound to a person, not a workspace, and are unavailable to API keys entirely — no scope, wildcard included, enables them.

    3. comment-automations:* is an API-access gate only and does not carry the plan entitlement. Comment automations require the PRO or AGENCY plan. A key holding comment-automations:write on a workspace without the feature still receives 403 PLAN_UPGRADE_REQUIRED. Both the scope check and the plan check must pass.

    Two existing route groups are worth restating because neither has a scope of its own: /v1/batch uses posts:read / posts:write (there is no batch:* scope), and /v1/uploads shares media:read / media:write with /v1/media.

Changed:

  • GET /v1/platforms now reports the limits the publish path actually enforces. The capability values in this response are now derived directly from the platform capability registry that validation and publishing read, so the advertised limit and the enforced limit are the same number. Three values changed:

    PlatformFieldPreviously advertisedNow reported
    WhatsApp BusinessmaxTextLength655364096
    LinkedInmaxImages920
    RedditmaxImages201

    Reddit additionally now reports no video support (maxVideoLength: 0, and video/mp4 absent from mediaTypes), matching the adapter — Reddit gallery and video upload are not implemented.

    What to check in your integration. If you read GET /v1/platforms to build client-side validation, re-read it: WhatsApp content between 4,097 and 65,536 characters and Reddit posts with more than one image were being accepted by your validation but rejected at publish time. LinkedIn moves the other way — up to 20 images are accepted where your client may still cap at 9.

    The published platform reference already listed the enforced values, so no documentation page changes with this release; it is the API response that now agrees with it.

Fixed:

  • Invitation endpoints return 403 AUTHORIZATION_ERROR to API-key callers instead of 401. POST /v1/invitations, POST /v1/workspaces/:id/invitations, POST /v1/invitations/:id/resend, GET /v1/invitations, POST /v1/invitations/:token/accept and DELETE /v1/invitations/:id previously returned 401 Authentication required when called with an API key. That was both the wrong status — the caller is authenticated; it is the operation that is unavailable to that credential — and a message that read like a broken or expired token. These now return 403 with a message naming the actual reason.

    What to check in your integration. If you have retry or re-authentication logic keyed on 401 from these paths, it was retrying a call that can never succeed. Treat 403 here as terminal and route these operations through a signed-in workspace admin instead.

Breaking: No


2026-08-25 — API Key Scopes for Hashtag Sets

Changed:

  • /v1/hashtag-sets is now reachable with named API key scopes. Previously only a wildcard (*) key could call this endpoint group; scoped keys received 403 AUTHORIZATION_ERROR. Two scopes are now available and can be requested when creating a key:

    ScopeGrants
    hashtag-sets:readGET /v1/hashtag-sets, GET /v1/hashtag-sets/:id
    hashtag-sets:writePOST, PATCH, DELETE on /v1/hashtag-sets

    As elsewhere, :write does not imply :read — request both if your integration needs to list and modify. See Authentication → Scopes for the full table.

    No change to existing keys. Wildcard keys reached this group before and still do. A key holding other named scopes could not reach it before and still cannot — scopes are matched exactly, so this is available only to keys issued with it going forward.

Breaking: No


2026-08-24 — Account Scope Health & Page Selection Ownership

Added:

  • scopeHealth on the account object — returned by GET /v1/accounts and GET /v1/accounts/:id. Records what the platform actually granted at connect time against what each VoxBurst feature requires, so a shortfall is visible immediately rather than at the moment an affected call fails.

    "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" }

    status is one of OK, DEGRADED, UNKNOWN, or NOT_APPLICABLE. Branch on degradedCapabilities[].capability (stable); display label or message (wording may change). grantedForOtherTargetsOnly: true means the permission exists but was granted for a different Page or account — the key is omitted otherwise. reason is an opaque diagnostic for UNKNOWN / NOT_APPLICABLE; log it, do not parse it.

    scopeHealth absent is not status: "OK". The field is omitted entirely on accounts connected before this check shipped, which means never verified — not no problems found. Treating null as healthy is the exact failure mode this field exists to prevent. Verification never blocks a connect: a DEGRADED account is still status: "active" and still publishes unless publish is among its degraded capabilities.

    See Scope Health for the full field and capability reference.

Changed:

  • POST /v1/accounts/:id/select-page returns 400 ACCOUNT_PAGE_MISMATCH when the selected Page does not belong to the account identified by :id. Applies to Instagram and Facebook. Nothing is written when this is returned — the account is left exactly as it was.

    Honest framing: this is not a breaking change — no status code, envelope, or field changed on any successful call, and a selection that was already valid still succeeds. But a caller that selected a Page belonging to a different connected account previously received { "success": true } and will now receive a 400. If your integration treats any non-2xx from this endpoint as fatal, add a handler.

    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 transient. Note that GET /v1/accounts/:id/pages can return a superset of the Pages selectable onto any one account, so do not assume every entry is a valid pageId for the account you fetched it from. See Page ownership.

  • Account matching on reconnect now resolves an account to its platform-stable identity. A reconnect may upgrade an existing disconnected record in place where it previously created a second one, so GET /v1/accounts can show one account where two appeared before. This is a data-shape outcome, not a contract change, and requires no integration work.

Docs:

  • POST /v1/accounts/:id/select-page and GET /v1/accounts/:id/pages in the OpenAPI spec no longer state that page selection does not apply to Instagram. It does, for Facebook Login-connected accounts.

2026-08-20 — Post, Media, Contact & Workspace Field Reference

No API behaviour changed in this entry. These fields and behaviours were already live and are now documented.

Documented — post and media fields:

  • GET /v1/posts/:idplatforms[] entries include accountAvatarUrl (optional; the key is omitted when absent, not null) and accountStatus (active, expired, disconnected, suspended, error — lowercase). Check accountStatus before unpublishing: any non-active value means the platform-side delete cannot succeed.
  • POST /v1/posts/:id/unpublish — the failure reason vocabulary is now documented and stable: platform_not_supported, account_disconnected, account_not_authenticated, token_expired, token_lookup_failed. token_expired requires the user to reconnect; token_lookup_failed is transient and reconnecting will not help.
  • Media objects include thumbnailUrl — a poster frame for video, generated asynchronously. Nullable: null for non-video media, and until extraction completes.

Documented — contacts and workspaces:

  • GET /v1/contacts/:id includes inboundMessages[] (30 most recent) and comments[] (20 most recent). Both are capped and unpaginated — treat them as recent context, not a complete record.
  • PATCH /v1/workspaces/:id — the approve_content and approve_schedule settings are documented. Unrecognised settings keys are silently discarded and still return 200; re-read the workspace to confirm a save.

Documented — accounts:

  • POST /v1/accounts/:id/select-page returns 400 PAGE_SELECTION_NOT_SUPPORTED for Instagram accounts connected via Instagram Direct Login (oauthVersion: "instagram_login"). Not transient — page selection is a Facebook Login concept.

Documented — reserved metadata keys (pre-existing behaviour, previously undocumented):

  • metadata.title is read as the post title by the Pinterest and Reddit adapters, and as a YouTube fallback. On a multi-platform post all three share the one value — use platformMetadata for per-platform titles.
  • A top-level tags body field silently overwrites metadata.tags. Set one or the other. Prefer the namespaced youtubeTitle / youtubeTags keys for YouTube.

Docs:

  • New Inbox Comments reference page — the comments endpoint group previously had no page.
  • WhatsApp Business is documented as pending approval, matching the platform gate in the product. The 2026-06-07 “now live” entry carries a dated correction.

2026-08-17 — Inbox Comments & Mentions Enhancements

Breaking Changes:

  • Inbox list endpoints now exclude hidden items by default. GET /v1/inbox/comments and GET /v1/inbox/mentions no longer return items that have been soft-hidden. There is no tombstone or placeholder for an excluded item, and the response totals (pagination for comments, meta.total for mentions) count only the visible set.

    The hiddenOnly filter is binary — omitted or false returns non-hidden items only, true returns hidden items only. No value returns both.

    Migration: any integration performing a full sync must now issue two requests per resource — one with hiddenOnly=true and one without — and merge the results. A single unqualified request will silently under-report. Reconciliation logic that treats “absent from the list” as “deleted upstream” must be updated, or it will incorrectly delete hidden items from local stores.

  • Threads posts containing more than 5 links are now rejected before publishing. The link count is validated locally, so the post fails with VALIDATION_ERROR (Threads posts support a maximum of 5 links) instead of reaching the platform. The resulting failure is non-retryablePOST /v1/posts/:id/retry will not clear it, where the previous platform-side failure was retried. Reduce the link count and resubmit via POST /v1/posts/:id/platforms/:platformId/fix.

New Endpoints:

  • POST /v1/inbox/comments/:commentId/hide — soft-hide a comment

  • DELETE /v1/inbox/comments/:commentId/hide — unhide a comment

  • POST /v1/inbox/mentions/:id/hide — soft-hide a mention

  • DELETE /v1/inbox/mentions/:id/hide — unhide a mention

    Hiding is a VoxBurst-side visibility control. It does not hide, delete, or otherwise modify the item on the source platform. All four return the stored record, not the formatted shape returned by the corresponding list and get endpoints — re-fetch the item if you need the formatted form. See Inbox Comments and Inbox Mentions.

Changed:

  • GET /v1/inbox/comments and GET /v1/inbox/mentions accept a hiddenOnly boolean query parameter. Note that hiddenOnly is not applied on the account-scoped (GET /v1/accounts/:accountId/comments) or post-scoped (GET /v1/posts/:postId/comments) comment lists — it is accepted and silently ignored there.
  • Comment and mention objects include hidden (boolean) and hiddenAt (ISO 8601 timestamp or null).
  • GET /v1/inbox/mentions/stats counts all unread mentions including hidden ones, so unreadCount can exceed the number of rows returned by the default list.
  • Comment sync now conditionally updates author information on resync, allowing previously stored placeholder author names to be replaced with real author data when the platform later supplies it. Consumers caching author.username should not assume it is immutable.
  • Facebook mention payloads use real sender identity when the webhook provides it. authorUsername remains null for a meaningful fraction of Facebook mentions — see Facebook Mention Author Identity.

Fixed:

  • Internal cleanup of the Facebook analytics collection path to stop requesting a metric Meta no longer serves. There is no change to reported impressions values — the figures returned by the analytics endpoints are sourced as before and the series is continuous. No action required.

Security:

  • Improved access control on invitation endpoints


2026-08-12 — Inbox: permalinkUrl on Comments and Mentions, Facebook Mentions Now Delivered

Added:

  • permalinkUrl: string | null on GET /v1/inbox/comments, GET /v1/inbox/comments/:id, GET /v1/inbox/mentions, and GET /v1/inbox/mentions/:id — each item now includes a direct public web permalink to the original content on the source platform. For Facebook, this is the fully-formed post or comment URL (e.g. https://www.facebook.com/{post_id}?comment_id={comment_id}), usable as a click-through link in integrations without additional Graph API calls. For Instagram and all other platforms, permalinkUrl is currently null — Instagram permalinks require the media shortcode, which is not fetched inline. This is a documented limitation, not a bug; shortcode-based permalink support is tracked as a future enhancement.

Fixed:

  • Facebook Page mention events (GET /v1/inbox/mentions?platform=FACEBOOK) now appear in the API. Due to a payload shape mismatch in the webhook ingestion layer, Facebook mention webhook events were previously stored in a format that prevented them from being returned by the mentions endpoints. They were silently dropped from API responses even though they were received from Meta. This is now corrected — Facebook mentions are stored with the correct shape and returned normally. If your integration was not seeing Facebook mentions before this release, no backfill was performed; existing stored events are unaffected.

Platform behavior note — Facebook mention author identity: For Facebook mentions where the tagged content is a comment on a post the Page does not own, authorId and authorUsername may be a non-resolvable placeholder or null. This is a permanent Meta Graph API constraint: the from field on the mention webhook event is documented by Meta as “Only available for Workplace community” for the item: "comment" case, and is not available to consumer apps via pages_read_user_content. VoxBurst performs a best-effort Graph API enrichment and uses real sender data when Meta provides it (which occurs for the item: "post" case when Meta includes sender identity inline). Integrations should handle authorUsername: null gracefully — for example, by displaying “Unknown” or using the new permalinkUrl field for navigation instead. See Inbox — Mentions: Facebook Mention Author Identity.

Breaking: No — permalinkUrl is an additive field. Clients that ignore unknown fields are unaffected.


2026-08-08 — Contacts: Instagram Messaging Window

Changed:

  • The 24-hour messaging window on POST /v1/contacts/:id/messages is now enforced consistently for both Facebook and Instagram contacts. Previously this was Facebook-only; Instagram sends outside the window were not refused server-side. Integrators sending to Instagram contacts outside the window now receive the same VALIDATION_ERROR refusal Facebook contacts already do. messengerEligibleUntil on the contact object is populated for Instagram contacts as well (see updated field table below).

Breaking: No — this closes a gap in enforcement rather than changing a documented contract. Integrations relying on unrestricted Instagram sends should add window handling identical to their existing Facebook logic.


2026-08-08 — Broadcasts: Delivery Status Corrections, New Endpoints, and Missing Fields

Fixed:

  • delivery.status value corrected: "SENT" is not a valid delivery statusDeliveryStatus is an independent enum from BroadcastStatus. The valid delivery statuses are PENDING, DELIVERING, DELIVERED, FAILED, and READ. "SENT" is a broadcast-level status only; individual delivery records never carry it. Any integration checking delivery.status === "SENT" should change that check to "DELIVERED".

  • Delete eligibility correctedDELETE /v1/broadcasts/:id accepts DRAFT, FAILED, and SENT broadcasts, not DRAFT only. Broadcasts in SENDING, SCHEDULED, or PAUSED status remain non-deletable and return 422 BROADCAST_NOT_DELETABLE.

Added:

  • POST /v1/broadcasts/preview-count — returns { count } with the number of contacts that would receive a broadcast for a given platform, account, and tag selection. Uses the same eligibility query as /:id/send. Agency plan required.

  • POST /v1/broadcasts/:id/retry — retries all FAILED delivery records on a FAILED or SENT broadcast. Resets each failed delivery to PENDING, sets the broadcast status back to SENDING, and re-enqueues for processing. Returns { broadcast, retriedContacts, batches }. Errors: BROADCAST_NOT_RETRYABLE (422) if the broadcast is not in a retryable state; NO_FAILED_DELIVERIES (422) if there are no failed deliveries to retry.

  • socialAccountId on POST /v1/broadcasts and PATCH /v1/broadcasts/:id — optional field that pins the broadcast to a specific connected account. On create, accepts a string; on update, accepts string | null (pass null to clear). The account must belong to the workspace and its platform must be in the broadcast’s platforms list.

  • deliveredCount, failedCount, readCount on the Broadcast object — integer counters maintained as deliveries progress. Now documented in the Broadcast schema.

  • sentAt, readAt, errorCode, createdAt, updatedAt on BroadcastDelivery records — fields present in the API response but previously undocumented. errorCode is a platform-specific string populated when status is FAILED.

  • avatarUrl on the nested contact in delivery list responses — included in the contact selection returned by GET /v1/broadcasts/:id/deliveries.


2026-08-08 — Contacts: Direct Messaging, Messaging Status Fields & Enriched Mutation Responses

Added:

  • POST /v1/contacts/:id/messages — send a 1:1 direct message to a contact via their linked Facebook or Instagram account. Body: { "text": "..." } (1–2,000 characters). Returns { delivery: { deliveryId, status, errorCode? }, contact } where contact is the full detail shape. See Contacts — Send Direct Message.

    Eligibility is enforced server-side on every call. A refused send returns 400 before Meta is contacted. A transport failure returns 200 with delivery.status: "FAILED" and an errorCode. These are distinct outcomes — integrations must handle both:

    OutcomeStatusMeaning
    Refused (suppressed, window closed, no account, unsupported platform)400 VALIDATION_ERRORMeta was never contacted; the send was blocked
    Transport failure (Meta rejected the message)200, delivery.status: "FAILED"Delivery record created; check delivery.errorCode
    Success200, delivery.status: "DELIVERED"Message delivered
  • Messaging-status fields on all contact responsesGET /v1/contacts and all mutation endpoints now include the following fields on every contact:

    FieldTypeNotes
    socialAccountobject | nullThe connected account this contact was last seen through: { id, displayName, username, platform }
    lastInboundAtstring | nullISO 8601 timestamp of the most recent inbound message from this contact
    messengerEligibleUntilstring | nullFacebook and Instagram. When the 24-hour messaging window closes. Always null once the window lapses — never a past timestamp. null for all other platforms.
    isSuppressedbooleantrue if this contact has opted out
    suppressedAtstring | nullISO 8601 timestamp when suppression was applied
    suppressionReasonstring | null"STOP_KEYWORD" or "MANUAL"
    messagesSentCountintegerTotal messages sent to this contact
    sequenceEnrollmentCountintegerTotal sequence enrollments
  • Detail shape on GET /v1/contacts/:id — now returns deliveries[] (last 20 delivery records) and sequenceEnrollments[] (last 10 enrollment records) in addition to all list-shape fields.

Changed:

  • All contacts mutation endpoints now return the detail shapePOST /v1/contacts, PATCH /v1/contacts/:id, POST /v1/contacts/:id/tags, and DELETE /v1/contacts/:id/tags/:tag previously returned a bare contact. They now return the same enriched detail shape as GET /v1/contacts/:id (including deliveries, sequenceEnrollments, and all messaging-status fields). This change is additive — new fields are present, no existing fields were renamed or removed. Strict deserializers that reject unknown fields will need to be updated.

Breaking: No — the response shape change is additive only. Clients that ignore unknown fields (the standard recommended behavior per the backwards compatibility policy) are unaffected. Strict clients must allow the new fields listed above.


2026-07-27 — WhatsApp Business Templates & Security Hardening

Added:

  • GET /v1/whatsapp/accounts/:accountId/templates — fetch approved WhatsApp Business message templates from connected accounts. Returns gracefully with an empty array if the Meta API is unavailable, allowing template composition to degrade cleanly.
  • WhatsApp Business OAuth provider registration — users can now connect WhatsApp Business accounts via Meta Facebook Login (same OAuth flow as Facebook).

Changed:

  • Instagram Direct Login post deletion now explicitly blocked at the API level with a clear error message, since graph.instagram.com does not support the DELETE endpoint for this OAuth variant. The UI warns users that Instagram posts from Direct Login accounts must be deleted manually on Instagram.
  • Instagram Direct Login analytics now requests reach,profile_views,views instead of the deprecated impressions metric, fixing 400 errors on insights calls.

Fixed:

  • CloudFront redirect functions on both voxburst.com and app.voxburst.io were silently dropping query strings on trailing-slash redirects, breaking links with UTM parameters, referral codes, and consent tokens. Query strings are now preserved on all redirect paths.
  • Improved input validation and request authorization across the API (API key route scoping, workspace path binding, webhook replay safety, and billing operation atomicity).
  • Improved frontend dependency security across PostCSS and third-party packages.

Breaking: No


Added:

  • WhatsApp message templates — Connected WhatsApp Business accounts can now retrieve approved message templates for use in publishing workflows.
  • On-demand analytics refresh — Account metrics can be refreshed when needed through POST /v1/analytics/accounts/:accountId/refresh.

Changed:

  • Instagram Direct Login accounts now show a clear in-product notice when post deletion must be completed in Instagram.
  • Account analytics are refreshed on demand when stale, improving dashboard freshness.

Fixed:

  • Query parameters, including referral and consent values, are preserved through site redirects.
  • Consent preferences are retained when navigating between voxburst.com and voxburst.io.
  • Instagram analytics use the correct metrics and API host for each connection type.
  • API keys cannot access global-administration routes.
  • The “Remember me” control has improved keyboard and screen-reader support.

2026-07-04 — Instagram OAuth Redesign: Direct Login Default, New Account Fields & Structured Error Codes

Added:

  • oauthVersion field on Instagram account responses — all connected Instagram accounts now include an oauthVersion field indicating which OAuth path was used to connect. Two values are possible:

    • "instagram_login" — connected via Instagram Direct Login (the new default)
    • "fb_login" — connected via Facebook Login for Business

    This field is present on all Instagram accounts returned by GET /v1/accounts and GET /v1/accounts/:id. Accounts connected before this release that went through the Facebook Login path will show "fb_login"; accounts connected via Direct Login will show "instagram_login".

  • flobProbe field on Instagram account responses — Instagram accounts connected via Direct Login may include an optional flobProbe object. When present, it indicates whether the account is eligible for upgrade to Facebook Login for Business:

    { "flobProbe": { "flobAvailable": true, "facebookAccountId": "...", "checkedAt": "2026-07-06T01:29:37Z" } }
    FieldTypeDescription
    flobAvailablebooleantrue if this Instagram account is linked to a Facebook Page in Accounts Center and is eligible for Facebook Login upgrade
    facebookAccountIdstring | nullThe associated Facebook account identifier when flobAvailable is true; null otherwise
    checkedAtstringISO 8601 timestamp of when eligibility was last determined

    The flobProbe field may be absent if the eligibility check has not yet completed. Poll GET /v1/accounts/:id if you need to wait for it.

  • Structured error codes on Instagram OAuth failures — when an Instagram OAuth attempt fails during the callback redirect, the result page now includes an errorCode query parameter. Integrations that handle the OAuth result URL should parse and present these codes:

    CodeMeaning
    NO_FACEBOOK_PAGESUser has no Facebook Pages in their account
    NO_IG_ON_PAGESFacebook Pages found but none are linked to an Instagram account
    ACCOUNTS_CENTER_MISMATCHThe Instagram account is not associated with the user’s personal Accounts Center
    CREDENTIALS_NOT_CONFIGUREDInstagram OAuth credentials are not configured on this VoxBurst instance

Changed:

  • Instagram Direct Login is now the default connection path — the login method picker previously presented during Instagram account connection is no longer shown. Accounts now connect via Instagram Direct Login by default. Facebook Login for Business remains available as an optional upgrade path for eligible accounts (see flobProbe above). Existing connected accounts and all post publishing behavior are unaffected. Integrators calling POST /v1/accounts/connect/instagram with an explicit loginMethod parameter are unaffected — the parameter is still accepted when provided.

Removed:

  • message_reads removed from Meta webhook subscription field lists — VoxBurst’s Instagram and Facebook Page webhook subscriptions no longer include message_reads, which Meta’s API no longer accepts as a valid subscribed field. Current subscribed fields:

    • Instagram: comments, mentions, messages, messaging_postbacks
    • Facebook Page: feed, mention, messages, messaging_postbacks

    This change only affects what events VoxBurst receives from Meta’s platform. It does not affect VoxBurst webhook deliveries to your endpoints.

Breaking: No — oauthVersion and flobProbe are additive fields. Clients must ignore unknown fields. The removal of message_reads from Meta subscriptions is transparent to API consumers.


2026-07-03 — Facebook Post IDs, Twitter/X Engagement Rate, and Mention Filters

Fixed:

  • platformPostId for Facebook photo and video posts — for posts published to Facebook as photos or videos, platformPostId now returns the feed-level post ID. This is the same ID that appears in post.published webhook payloads. The field was already present in the response; only the value has changed. No schema migration is needed.

  • GET /v1/inbox/mentionsplatform and accountId filters now reliably scope results — the platform and accountId query parameters now correctly filter returned mention records. Both parameters can be used independently or in combination.

Changed:

  • Twitter/X engagementRate is now follower-based — for Twitter/X, the engagementRate field returned by all analytics endpoints now uses a follower-based denominator rather than impressions. For accounts below a minimum follower threshold, engagementRate is null rather than 0. All other platforms continue to use impression-based engagement rate. Existing stored data is not retroactively recalculated; the change applies to new metric fetches.

2026-06-27 — scheduledFor with saveAsDraft: true accepts past timestamps

Changed:

  • POST /v1/posts and PATCH /v1/posts/:idscheduledFor past-date validation bypassed when saveAsDraft: true — the 60-second future-date minimum is no longer enforced when saveAsDraft: true is set. This allows integrations that import historical content to preserve the original publication date for calendar positioning without requiring a future timestamp. The post is saved as a draft regardless of the scheduledFor value. The future-date constraint still applies when creating or updating scheduled posts without saveAsDraft: true.

Breaking: No — saveAsDraft: true behavior is unchanged. Integrations that do not use saveAsDraft are unaffected.


2026-06-22 — Google Business Profile Live, GMB Review Management, platformMetadata Validation, and API Key Scope Enforcement

Added:

  • Google Business Profile (GOOGLE_BUSINESS) is now live — publishing is fully enabled. Connect a Google account with Owner or Manager access via Settings > Accounts. Supports Business Updates, Offers, and Events post types. See Google Business Profile.

  • GET /v1/inbox/reviews — list Google Business Profile reviews for the workspace. Supports filtering by accountId, locationPath, rating (1–5), unreadOnly, and unrepliedOnly. Paginated (page, limit 1–100, default 20). Triggers a lazy GMB sync on each call, debounced per 5 minutes per account; sync failures do not block the response. See Inbox — Reviews.

  • GET /v1/inbox/reviews/stats — returns aggregate unread and unreplied review counts for the workspace. Response: { "unreadCount": N, "unrepliedCount": N, "totalCount": N }.

  • GET /v1/inbox/reviews/locations — returns distinct synced GMB location paths for use in filter dropdowns. Response: { "locations": [{ "locationPath": "string", "locationName": "string" }] }.

  • POST /v1/inbox/reviews/:id/reply — post a reply to a GMB review via the Google Business API. Body: { "comment": "string" } (1–4,096 characters). On success, sets replied: true, repliedAt, replyContent, and read: true on the review record.

  • PATCH /v1/inbox/reviews/:id/read — mark a review as read. No request body required.

  • GET /v1/accounts/:id/gmb/locations — list all GMB locations available under a connected Google Business account, grouped by account, with each location’s current selection state. Account must be GOOGLE_BUSINESS platform. Returns 400 if the account is a different platform or the token is missing or expired.

  • PUT /v1/accounts/:id/gmb/locations — save the set of selected location paths for a connected GMB account. Body: { "locationPaths": ["string"] } (max 100 paths). Returns { "success": true, "selectedCount": N }.

Breaking:

  • platformMetadata validation tightened on POST /v1/posts and PATCH /v1/posts/:id — nested values in platformMetadata are now strictly validated. Each value must be string, number, boolean, null, or an array of those primitive types (max 50 elements per array). Each platform’s metadata object is limited to 20 keys. Requests containing arbitrary nested objects or non-primitive values now return 400 VALIDATION_ERROR. Previously these were silently ignored or passed through.

    Migration: Audit any platformMetadata payloads your integration sends. Nested objects (e.g. { "TIKTOK": { "nested": { "key": "val" } } }) are no longer accepted. Flatten any nested structures to top-level primitive key-value pairs.

  • API key scope validation now enforced on POST /v1/workspaces/:id/api-keys and PATCH /v1/workspaces/:id/api-keys/:keyId — requests containing unrecognized scope strings are rejected with 400 VALIDATION_ERROR. The wildcard scope (*) must be the sole scope in the array; combining it with named scopes is rejected.

    Valid scopes: see the full table at Authentication — Scopes.

    Error response when an invalid scope is submitted:

    { "error": { "code": "VALIDATION_ERROR", "message": "Invalid scope(s): unknown_scope" } }

    Migration: Remove any custom or fabricated scope strings from API key creation/update calls. Replace ["*", "posts:read"] with ["*"] alone or list only named scopes.


2026-06-20 — Facebook Analytics Metrics Deprecated by Meta

Changed:

  • Facebook analytics — reach, engagements, and clicks now return 0 — Meta deprecated the per-post metrics post_impressions_unique, post_engaged_users, and post_clicks across all Graph API versions effective June 15, 2026. For posts published on Facebook, the reach, engagements, and clicks fields in all analytics responses (GET /v1/analytics/posts/:postId, GET /v1/analytics/aggregate, GET /v1/analytics/overview, etc.) now return 0. The impressions field for Facebook posts now reflects post_media_view (view and play count) rather than the former impression metric.

    Instagram analytics are not affected by this change.

Breaking: No — fields are still present in the response and remain 0 rather than being removed. Downstream calculations that divide by or compare these fields against non-zero expectations should be updated.


2026-06-15 — TikTok Privacy Level, Publish Warnings, Unpublish Behavior Change & Threads Replies

Added:

  • platformMetadata on POST /v1/posts and PATCH /v1/posts/:id — New optional request field for per-platform metadata overrides. Keys are platform constants (e.g. "TIKTOK"); values are objects merged into the post’s platform-specific metadata. First supported key: TIKTOK.tiktokPrivacyLevel — set the TikTok privacy level (PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, SELF_ONLY, FOLLOWER_OF_CREATOR) per post. Available values depend on the connected TikTok account’s creator permissions. See Platform Metadata.

  • publishWarning on PostPlatform response objects — Non-fatal publishing warnings are now returned on per-platform publish state entries (the platforms[] array in all post responses). The field is string | nullnull when no warning occurred. First value: TIKTOK_SELF_ONLY_FALLBACK — the requested TikTok privacy level was rejected by the API and the post was automatically retried with SELF_ONLY visibility and published successfully. See PostPlatform fields.

  • GET /v1/posts/:id/replies — New endpoint to fetch replies for a published Threads post. Returns up to 25 replies. Requires the posts:read scope. If the connected Threads account does not have threads_read_replies permission (granted at OAuth time), the response is always { "data": [] } — no error is returned. Returns { "data": [] } for posts not published on Threads. See Get Post Replies.

  • /v1/settings/notifications — now documentedGET and PUT endpoints for managing per-user notification preferences are now part of the public API reference. All existing fields (emailEnabled, pushEnabled, postPublished, postFailed, weeklyDigest, analyticsReport, timezone, dormant account reminder fields, and marketingEmails) are documented. Two fields added in this release: webhookFailed (default true) — alerts when a webhook endpoint fails or is auto-disabled; and approvalUpdates (default true) — alerts when an approval workflow state changes (submitted, approved, or rejected). All fields accept partial updates — send only the fields you want to change. See Notification Settings.

Changed:

  • POST /v1/posts/:id/unpublish now deletes posts from platforms — Previously this endpoint only marked the VoxBurst post as unpublished without touching the content on social platforms. It now attempts to delete the post from each connected platform before marking it unpublished. The response shape has changed:

    Previous response: { "success": true } (or similar — behavior was undocumented)

    New response:

    { "results": { "INSTAGRAM": { "success": true }, "TWITTER": { "success": false, "reason": "platform_not_supported" } }, "unsupported_platforms": ["TWITTER"], "note": "Posts on unsupported platforms must be deleted manually." }

    The results object contains per-platform outcomes (success: boolean, optional reason: string on failure). unsupported_platforms lists platforms where deletion is not supported — you must remove posts on those platforms manually. The VoxBurst post is marked unpublished regardless of per-platform deletion outcomes.

    Migration: If your integration calls POST /v1/posts/:id/unpublish and reads the response body, update your handler to parse the new shape. If you were manually deleting posts from platforms after calling unpublish, review whether that logic is still needed for platforms listed in unsupported_platforms. See Unpublish Post.

Breaking: No — platformMetadata and publishWarning are additive. The unpublish response shape change affects integrations that parse the response body; the previous response was undocumented, so no stable contract existed. Update handlers that read the unpublish response.


2026-06-13 — Instagram Page Selection, Audio Search & AI Add-On Clarifications

Added:

  • GET /v1/accounts/:id/pages now supports Instagram — for accounts connected via Facebook Login, returns a list of Instagram Business accounts linked to the connected Facebook user token. The response shape differs from LinkedIn: Instagram entries use pageId, pageName, type, and avatarUrl fields (LinkedIn uses urn and logoUrl). See List Pages.
  • POST /v1/accounts/:id/select-page now supports Instagram — re-fetches IG 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 pageId is not found. See Select Page.
  • GET /v1/instagram/audio/search — search for audio tracks for use in Instagram Reels. Requires a Facebook Login-connected Instagram account. Returns trending tracks (omit q) or search results (q=<term>). Feature-flagged behind INSTAGRAM_AUDIO_ENABLED=true — currently returns 503 FEATURE_DISABLED while pending Meta App Review. See Instagram Audio Search.
  • POST /v1/users/me/ga4-client-id — stores the browser GA4 client ID for server-side Measurement Protocol event stitching. Body: { "clientId": "XXXXXXXXX.XXXXXXXXX" } (must match /^\d+\.\d+$/). Returns { ok: true }. See User Endpoints.

Changed:

  • GET /v1/billing/currentaiAddon field clarifications: subscribed: true, tier: null is now a valid combination for complimentary or admin-granted AI access. creditsLimit: null and creditsRemaining: null mean unlimited credits (not missing data). Previously these null combinations were undocumented. See Get Current Billing Status.

Breaking: No


2026-06-07 — WhatsApp Business Live, Analytics Expansion & AI Insights

Added:

  • WhatsApp Business is now a live platform — connect your WhatsApp Business Account (WABA) via OAuth and start publishing TEXT, IMAGE (up to 30 images), and VIDEO posts to your business number. Requires Meta Business verification and a registered phone number ID. See WhatsApp platform guide.

    Correction (2026-08-19): WhatsApp Business was returned to pending approval status on 2026-06-24 and is not currently available for publishing. This 2026-06-07 entry is retained for history but no longer reflects the platform’s status. See Supported Platforms for current availability.

  • AI Insights — a new collapsible panel on the analytics dashboard surfaces AI-generated recommendations (priority-ranked, categorized as posting_time, content_quality, platform_mix, growth, engagement) with actionable next steps and direct navigation links to the relevant dashboard section. Available on paid plans with a system-level AI provider configured.

  • 10 new analytics endpoints:

    • GET /v1/analytics/hashtag-performance — top hashtags by post count and average engagement rate
    • GET /v1/analytics/content-types — engagement breakdown by content type (TEXT, IMAGE, VIDEO, etc.)
    • GET /v1/analytics/insights — AI-generated insights (paid plan, 2-hour cache, configurable per workspace)
    • GET /v1/analytics/publishing-calendar — post frequency calendar heatmap by date
    • GET /v1/analytics/content-themes — top words/themes by engagement rate across posts
    • GET /v1/analytics/goals — retrieve current follower, impression, engagement, and post goals
    • POST /v1/analytics/goals — set or update analytics goals (all fields optional, null to clear)
    • GET /v1/analytics/report-schedule — retrieve the email report schedule (frequency, recipients)
    • POST /v1/analytics/report-schedule — configure weekly or monthly email reports with up to 10 recipients
    • POST /v1/analytics/send-report — immediately dispatch an analytics email report to configured or override recipients
  • Engagement Per Post KPI card added to the analytics dashboard overview

  • Email report redesign — new visual layout with platform breakdown, top posts, and insights summary; accessible via a toolbar button in the analytics panel

Changed:

  • Facebook first commentpages_manage_engagement permission required for Facebook first comments is currently under review by Meta. Posts that include firstComment will publish normally; the comment is silently skipped on Facebook until the permission is approved and re-enabled. All other platforms are unaffected.
  • GET /v1/analytics/posting-heatmap now accepts platforms and accountIds filter parameters; response now includes hasEngagementData boolean

Fixed:

  • AI Insights panel no longer shows fabricated data for workspaces with no post history — a structured onboarding state is returned instead
  • Funnel bars now use the true maximum value in the dataset instead of clamping all bars relative to 100%, so a single high-performing post no longer makes all other posts appear flat
  • “View breakdown” insight action link now correctly scrolls to the platform breakdown section

Breaking: No


2026-06-01 — AI Credit Costs Updated & Analytics Filtering Fixed

Added:

  • Meta deauthorize and data deletion compliance webhooks (POST /webhooks/meta/deauthorize, POST /webhooks/meta/data-deletion, GET /webhooks/meta/data-deletion?id=<code>) for OAuth app review and live-mode Instagram/Facebook support
  • Credit usage tracking in ledger for unlimited/admin-granted accounts (audit visibility without blocking generation)
  • Follower count display in /v1/accounts response and account UI cards
  • Engagement per Post KPI card to analytics dashboard

Changed:

  • AI credit costs realigned: image generation, best time analysis, and hashtag suggestions now each consume credits
  • Default credit cost per post updated — varies by content type (text-only vs. text+image)
  • Workspace AI key (/v1/ai/hashtags) now consumes credits
  • /v1/overview analytics endpoint now respects platforms filter parameter (previously ignored)
  • OAuth scopes: removed instagram_business_messaging from required scopes to unblock account reconnection during Meta app review

Fixed:

  • Analytics dashboard platform filter now correctly returns posts for selected platforms only (was showing all platforms regardless of filter)
  • Account follower counts now display correctly after reconnection (separate followers_count fetch to gracefully handle restricted personal accounts)
  • Instagram account sync no longer fails on personal accounts or accounts with restricted follower_count field
  • OAuth result page no longer flashes “Connection failed” during redirect hydration
  • Instagram Direct Login OAuth callback now correctly broadcasts result to parent window (was treating Instagram’s #_=_ hash artifact as pure page load)

Breaking: No



2026-05-26 — Instagram Discovery Improvements

Added:

  • Setup guide — new “How to connect Instagram for Discovery” section in the Getting Started help page walks through the 5-step Facebook Business Manager + Instagram Professional Account setup
  • Requirements banner — Discovery search now shows an inline banner listing which requirements are not yet met, with a direct link to the setup guide
  • Per-account readiness check — each connected Instagram account is now checked for Discovery eligibility before you search; accounts that are missing the Facebook connection or are personal accounts show a specific, actionable warning
  • Hashtag chips — hashtags are stripped from post captions and rendered as clickable chips; clicking any chip searches that hashtag immediately
  • Copy-all hashtags — each result card has a “Copy all N” button that copies the post’s hashtags to the clipboard
  • FAQ — “How Discovery works” FAQ is shown in the empty state, covering data source, top-post ranking, missing engagement fields, and the 30-hashtag platform limit
  • Clickable media-type badge — the Photo / Video / Carousel badge on each result card links directly to the post on Instagram

Changed:

  • “Album” renamed to “Carousel” across Discovery results (correct Instagram term)
  • Post result cards are now compact text-only cards; image placeholders removed (Meta’s hashtag search API does not return media URLs for public posts)
  • Relative timestamps (“3d ago”, “2w ago”) replace formatted dates on result cards
  • Search button is disabled when the selected account is not Discovery-ready, so issues are surfaced before an API call is made

Fixed:

  • Error messages are now specific and actionable — each error type returns its own message and suggested next steps
  • Invalid account ID errors (code 100) are now distinguished from permission denied errors (code 10) and handled separately

Breaking: No


2026-05-25 — Workspace Ownership Transfer & Plan Enforcement

Added:

  • POST /v1/workspaces/:id/transfer-requests — Owner initiates a transfer request to any workspace member
  • POST /v1/workspaces/:id/transfer-requests/:requestId/accept — Target accepts the ownership transfer (re-validates plan capacity at accept time)
  • POST /v1/workspaces/:id/transfer-requests/:requestId/decline — Target declines the transfer request
  • DELETE /v1/workspaces/:id/transfer-requests/:requestId — Owner cancels a pending transfer request
  • GET /v1/billing/scheduled-post-count — Returns count of SCHEDULED and PAYMENT_PAUSED posts across owned workspaces
  • POST /v1/billing/cancel now accepts { cancelScheduledPosts: boolean } to bulk-move scheduled posts to DRAFT before cancellation
  • Workspace transfer requests expire after 7 days and auto-reject
  • Email notifications sent to both owner and target on successful transfer (non-blocking; failures logged but do not roll back)
  • Old owner becomes admin (retains data access); ownership transfer does not affect Stripe subscription

Changed:

  • Workspace ownership transfer now uses request/acceptance flow instead of immediate transfer
  • Plan capacity validation now occurs at acceptance time (not just request initiation) to prevent race conditions
  • Scheduled posts targeting disconnected social accounts are now skipped at dispatch time (marked SKIPPED with skipReason: 'account_disconnected') instead of failing — posts auto-recover if the account reconnects before the scheduled time
  • WhatsApp moved back to Early Access (Channels API not yet available from Meta); renamed to “WhatsApp Business” in platform listings
  • /enhance, /hashtags, /suggest-time now require plan-level AI access (Starter or higher, or active AI add-on) — documented in API reference
  • POST /v1/accounts/store-oauth-tokens now enforces plan account limits at OAuth callback time; returns 402 if limit reached
  • AI credit enforcement for plan-only users (non-addon) is now atomic — credits reserved before provider call on all three plan AI endpoints

Fixed:

  • Scheduled post month calculation now uses scheduledFor (publish month) instead of createdAt for accurate plan limit enforcement
  • Scheduler now correctly handles posts that exceed plan limits, preventing indefinite reprocessing
  • Posts paused due to plan limits automatically resume when the plan is upgraded, within a recovery window
  • Dispatch-time plan limit re-check added — posts scheduled before downgrade now correctly blocked
  • WhatsApp OAuth account reconnection now preserves stable account identity
  • AI usage reports now read from an updated data source
  • Publish Now button no longer schedules posts that were auto-populated with a scheduled time
  • Plan-only AI routes (/enhance, /hashtags, /suggest-time) now log usage correctly for billing reports
  • Automatic image quality correction passes no longer charge credits
  • Post deletion now correctly releases the associated scheduling capacity reservation
  • Persona overage now visible in downgrade confirmation email
  • Bulk post creation (/posts, /bulk-video) now correctly enforce post limits based on the number of posts being created
  • Post creation enforcement is now consistent across all validation layers
  • Facebook OAuth flow no longer overridden by secondary #= artifact redirect

Breaking: No



2026-05-20 — @voxburst/sdk v1.2.0

Added:

  • client.media — new MediaResource with get(id), delete(id), and getUploadUrl({ filename, contentType, sizeBytes }) methods. New Media type exported from the package root.
  • client.webhooks — new WebhooksResource with create(input), list(), get(id), update(id, input), and delete(id) methods. New Webhook type exported from the package root.
  • client.batch.createPosts(posts, options?) — now hits POST /posts/bulk directly (up to 50 posts per call). Accepts { dryRun?: boolean }. Returns BulkPostResponse with per-post status (created | failed | valid). BulkPostResponse and BulkPostResult types exported from BatchResource.
  • client.posts.cancel(id) — cancel a draft or scheduled post without deleting it.
  • client.posts.unpublish(id) — attempt to delete a published post from each connected platform. Returns { results: Record<string, { success, reason? }>, unsupported_platforms, note }.
  • client.accounts.listAll(options?) — async generator that auto-paginates all accounts, mirroring posts.listAll().
  • Per-request timeout overrideclient.request() now accepts { timeout?: number } as a third argument. Pass a per-call timeout in milliseconds to override the global client timeout.
  • X-Request-ID on errors — when the API returns an x-request-id response header, it is now attached to the thrown VoxBurstError as error.requestId for easier support debugging.
  • 429 Retry-After respected — on rate-limit responses the SDK now waits exactly the number of seconds specified in the Retry-After header (capped at 60 s) before retrying, rather than applying an exponential multiplier.

Upgrade: npm install @voxburst/sdk@latest


2026-05-20 — @voxburst/sdk v1.1.0

Added:

  • client.posts.validate(input) — new method on PostsResource. Checks content against platform character limits and rules without creating a post. Accepts { content: string; platforms: string[] }, returns { valid: boolean; platforms: Record<string, ValidatePlatformResult> }. Types ValidateContentResult and ValidatePlatformResult are exported from the package root.

Upgrade: npm install @voxburst/sdk@latest


2026-05-20 — @voxburst/sdk v1.0.1

Fixed:

  • Error class names preserved through minificationinstanceof ValidationError, instanceof NotFoundError, instanceof AuthenticationError etc. now work correctly in bundled/minified consumer code. Previously all error instances appeared as a generic minified class.
  • fromResponse dispatches to the correct error subclass — API errors are now thrown as the appropriate subclass based on HTTP status (400ValidationError, 401AuthenticationError, 404NotFoundError, 409ConflictError, 429RateLimitError) instead of always throwing a base VoxBurstError.
  • ValidationError preserves the API’s error code — the code field now reflects the actual code returned by the API (e.g. INVALID_INPUT) rather than always being hardcoded to VALIDATION_ERROR.

Upgrade: npm install @voxburst/sdk@latest


2026-05-20 — TypeScript SDK, CLI & MCP Server Published

Added:

  • @voxburst/sdk v1.0.0 is now publicly available on npm. Install with npm install @voxburst/sdk. The SDK provides full TypeScript support, cursor-based auto-pagination via listAll(), automatic retries with exponential backoff, and typed error classes. See the TypeScript SDK docs for installation and usage examples.
  • @voxburst/cli v0.2.2 — updated release with API response shape fixes. Install with npm install -g @voxburst/cli.
  • @voxburst/mcp-server v0.2.2 — updated release with API path fixes (see API Correctness Fixes below). Install with npm install -g @voxburst/mcp-server.

Breaking: No — SDK is a new package; CLI and MCP Server updates are backwards-compatible.


2026-05-20 — API Correctness Fixes

Fixed:

  • POST /v1/posts/validate schema clarified — this endpoint requires a platforms array (e.g. ["TWITTER", "INSTAGRAM"]), not accountIds. It validates content rules for the named platforms without performing any account lookup. Previous documentation incorrectly showed accountIds in the example. See Validate Post for the corrected schema.
  • Post status values are lowercase — the API has always returned lowercase status strings (draft, scheduled, published, failed, partial, etc.). Documentation examples incorrectly showed uppercase values (DRAFT, SCHEDULED, etc.). Updated throughout the API reference, SDK docs, and examples.
  • Health check endpoint pathGET /v1/health and GET /v1/ready are the correct paths. Requests to /health (without the /v1 prefix) return 403 from CloudFront and do not reach the API. See Health Check.
  • Malformed resource IDs return 400, not 404 — Passing a malformed ID returns a 400 BAD_REQUEST. Handle both 400 and 404 when accepting user-supplied IDs. See Resource ID Format.
  • @voxburst/mcp-server v0.2.2 — API path fix — all internal HTTP client paths were incorrectly prefixed with /api/v1/ instead of /v1/. This caused every MCP tool call to return 403. Update to 0.2.2 or later. If you are running the MCP server from source, pull the latest main.

2026-05-11 — Trial Countdown, Plan Features Control Plane, and Security Hardening

Added:

  • GET /billing/current now returns trialEnd, trialTotalDays, and recentlyExpiredTrial fields for accurate trial status tracking
  • Plan feature changes now take effect immediately without requiring a redeploy
  • PDF analytics report generation (GET /v1/reports/pdf, PRO/AGENCY plan-gated, white-label option for AGENCY)
  • New plan feature gates: brand_voice, persona_evolution, ai_image_generation, pdf_reports, mcp_cli

Changed:

  • Trial polling reduced for Twitter: account-level snapshots cached 24h (was 4x/day), post metrics use 7-day lookback with 24h staleness (was 90-day, 6h)
  • “White-label Reports” renamed to “White-label Client Portal” (feature is CNAME-verified agency portal, not PDF branding)
  • Qwen models updated to Qwen3.6 series with US-endpoint variants
  • AI add-on credit system now enforces per-token rate limits on generation endpoints

Fixed:

  • User avatar uploads now accept Cognito sub or DB ID (fixed 403 for all avatar uploads)
  • Qwen API key validation fixed: uses correct US endpoint (dashscope-us.aliyuncs.com) and chat completion probe
  • Unlimited team members now correctly enforced on Pro/Agency plans
  • ai-sitemap and ai-context endpoints no longer blocked by CloudFront index.html rewrite
  • Plan limit sync now occurs on cold start without overwriting admin edits
  • Trial end dates now properly cleared from DB when subscription canceled to FREE
  • Fixed a race condition on concurrent review submissions

Security:

  • Improved prompt injection defense on AI generation endpoints
  • Improved cost abuse prevention: tightened input field validation on AI generation and image prompt fields
  • Improved rate limiting on review submissions and contact form attachments
  • Improved webhook signature verification
  • Improved Qwen key validation: added timeout to prevent hangs on network failures

Breaking: No


2026-05-06 — Webhook Auto-Disable, Downgrade Endpoints & Security Hardening

Added: New API fields and endpoints for webhook health management and plan downgrade workflows.

  • Webhook objects now include autoDisabledAt (ISO timestamp, null if active) and consecutiveFailures (integer) — use these to detect and alert on unhealthy webhook endpoints in your integration.
  • Webhook auto-disable behavior: after repeated consecutive delivery failures VoxBurst sends a warning email; after further failures it disables the webhook. Re-enabling via PATCH /v1/webhooks/:id resets the failure state and resumes delivery immediately.
  • GET /v1/billing/downgrade-readiness — returns a checklist of items to resolve before a plan downgrade (over-quota posts, connected accounts above the lower limit, active AI addons, etc.). Use this before initiating a plan change to surface blockers proactively.
  • GET /v1/billing/downgrade-designations — lists the connected accounts that will remain active after a downgrade (workspace owner chooses which to keep up to the lower plan’s limit).

Changed:

  • Plan enforcement is now consistent between API responses, feature gates, and display — upgrading mid-period activates new limits immediately; downgrading activates at the next renewal.

Fixed:

  • Improved input validation on file upload endpoints (MIME type enforcement) and AI generation fields (length and content checks).

Breaking: No — autoDisabledAt and consecutiveFailures are additive webhook fields. Existing handlers that ignore unknown fields are unaffected.


2026-04-02 — Partial Failure Retry & Fix Endpoints

Added: Two new endpoints for recovering from partial or failed posts without re-publishing to platforms that already succeeded.

  • POST /v1/posts/:id/retry — Re-queues all failed platform targets automatically. Platforms with status: published are never touched. Returns retriedPlatforms and skippedPlatforms arrays indicating which platforms were retried and which had exhausted retries.
  • POST /v1/posts/:id/platforms/:platformId/fix — Fixes a single failed platform and optionally replaces the image (mediaUrl) or caption (content) before resubmitting. This is the correct path for platforms that have exhausted automatic retries.
  • post.published webhook payload now includes status and a full per-platform platforms array (with postPlatformId, status, error, platformPostUrl per entry) so handlers can detect partial failures and identify which platform needs fixing.

Breaking: No — existing post.published webhook handlers that only read postId are unaffected. New fields are additive.


2026-03-24 — Bulk Generation v2.1

Added: Queue-based bulk generation with credit reservation and decision engine foundation.

  • New POST /v1/bulk-generation/jobs endpoint for creating bulk post generation jobs
  • Credit reservation system ensures credits are held before generation begins
  • Decision engine scores and categorizes generated content

Breaking: No


2026-03-16 — Persona Support for Posts

Added: Posts can now be linked to personas for AI-generated content with consistent voice.

  • Added personaId field to posts
  • Persona memory updates after publishing enable learning from successful posts
  • Persona profiles define tone, style, and platform-specific adaptations

Breaking: No — personaId is optional; existing posts unaffected.


2026-03-15 — AI Generation Improvements

Added: Enhanced tracking for AI content generation.

  • workspace_id added to AI generation records for billing queries by workspace
  • resolved_model field stores the actual model used when aliases redirect requests
  • New AI providers: Qwen added to supported models

Breaking: No


2026-03-14 — Account Permission Modes

Added: Granular permission controls for connected social accounts.

  • Accounts now support permission modes: read, publish, full
  • Fine-grained access control for team workspaces
  • Permission checks enforced at the API level

Breaking: No — existing accounts have full access unless a permission mode is specified.


2026-02-22 — Webhook Delivery Logs

Added: Comprehensive webhook delivery tracking and improved delivery diagnostics.

  • Delivery status tracking: pending, delivering, delivered, failed
  • Automatic retry with exponential backoff for failed deliveries

Breaking: No


2026-02-19 — Webhook Delivery Fields

Added: Enhanced webhook endpoint reliability features.

  • failureCount tracking on webhook endpoints
  • lastDeliveryAt timestamp for monitoring
  • Improved delivery diagnostics

Breaking: No


Versioning Policy

VoxBurst follows semantic versioning principles for API changes:

  • Non-breaking changes (new fields, new endpoints) are deployed continuously
  • Breaking changes are announced 30 days in advance via email and changelog
  • Deprecated fields are marked in documentation 90 days before removal

Follow @voxburst  on X for announcements.


Docs & contract changes

This feed tracks changes to the documentation and OpenAPI spec — new pages, schema corrections, field additions to reference tables, and spec updates. It is distinct from the API changelog above, which tracks live API behavior changes.

2026-07-29 — Updated Webhooks with Meta compliance callback configuration: the versioned data-deletion and deauthorize URLs, signed-request authentication, and the public deletion-status flow. Updated Versioning & Compatibility to clarify that VoxBurst API /v1 is independent of Meta Graph API versions; Meta Graph API v25.0 remains the current integration version while v26.0 is validated in a Meta test app.

2026-06-15 — Updated Posts API reference: added platformMetadata request field to POST /v1/posts and PATCH /v1/posts/:id; added publishWarning to PostPlatform fields; corrected POST /v1/posts/:id/unpublish behavior description and response schema; added GET /v1/posts/:id/replies endpoint

2026-06-13 — Updated Accounts API reference: expanded GET /v1/accounts/:id/pages and POST /v1/accounts/:id/select-page to document Instagram support alongside LinkedIn; added GET /v1/instagram/audio/search endpoint; added POST /v1/users/me/ga4-client-id endpoint

2026-06-13 — Updated Subscription API reference: clarified aiAddon field in GET /v1/billing/current — documented subscribed: true, tier: null (complimentary access) and creditsLimit: null / creditsRemaining: null (unlimited) as valid states

2026-05-31 — Added OpenAPI Spec page: codegen examples (openapi-typescript, openapi-generator, openapi-zod-client), Postman/Insomnia import, and generated type excerpts for Post and CreatePostInput

2026-05-31 — Added Post with media and Scheduled campaign end-to-end production workflow guides

2026-05-31 — Added Versioning & Compatibility page: explicit breaking-change tables, deprecation timeline, SDK semver rules, stability tiers, contract precedence

2026-05-31 — Added SDK↔REST mapping tables for 8 previously undocumented resources: Batch, Personas, Hashtag Sets, Contacts, Broadcasts, Sequences, Approval Workflows, Image Generation Jobs

2026-05-31 — Surfaced OpenAPI spec URLs on homepage, Getting Started, and SDK mapping page; added idempotency and versioning to Getting Started Next Steps

2026-05-31 — Added Python (REST) page to SDK navigation — clarifies no official Python package exists, REST-only with comparison table

2026-05-31 — Added client.media and client.webhooks reference sections to TypeScript SDK docs; completed client.posts with publish, cancel, clone, fixPlatform

2026-05-31 — Added API conventions section to Canonical Schemas: camelCase naming, ISO 8601 UTC dates, cuid2 ID prefix table, enum casing rules, null-vs-absent, pagination envelope 2026-05-31 — Corrected TypeScript SDK method coverage to match published @voxburst/sdk@1.2.1: removed publish(), clone(), fixPlatform() from client.posts (REST-only); removed completeConnect(), test(), listPages(), listPlaylists(), selectPage() from client.accounts (REST-only); removed client.personas, client.hashtagSets, client.approvalWorkflows (REST-only); corrected recurrence field to REST-only for creation. All affected examples replaced with raw-fetch equivalents.

2026-05-31 — Improved docs lint script: stale-pip check uses denial-text detection instead of path exemption; response-shape check extended to let/var and multiline destructuring, scoped to code blocks only; platforms-on-create check replaced line-window heuristic with fenced-code-block parser

2026-05-31 — Added scripts/sdk-methods.json (snapshot of @voxburst/sdk@1.2.1 resource/method surface derived from published package types) and scripts/generate-sdk-methods.js (regenerates the snapshot from the npm registry tarball). Added check 5 to the docs lint script: validates every client.X.Y() call in TypeScript code blocks against the snapshot — CI now fails if a documented SDK method does not exist in the published package. Immediately caught 22 violations across four files, all corrected.

2026-05-31 — Corrected Personas and Hashtag Sets tables in the SDK ↔ REST mapping: TypeScript SDK column incorrectly listed client.personas.* and client.hashtagSets.* as SDK methods. Both resources are REST-only and do not exist in @voxburst/sdk@1.2.1. All entries updated to REST only, resolving the contradiction between the mapping page and the TypeScript SDK page.

2026-06-07 — Added WhatsApp Business MCP guide: content types, failure cookbook, account requirements, cross-platform override example, benchmark checklist

2026-06-07 — Added 10 new analytics endpoints to Analytics API reference: hashtag-performance, content-types, insights, publishing-calendar, content-themes, goals (GET/POST), report-schedule (GET/POST), send-report

2026-06-07 — Expanded SDK ↔ REST mapping Analytics section from 2 to 21 endpoints; corrected prior client.analytics.* entries which incorrectly implied TypeScript/Go SDK coverage (all analytics endpoints are REST-only)

2026-06-07 — Updated platform count from 8 → 9 across platforms-at-a-glance, same-post-all-platforms, 7 MCP platform guide breadcrumbs, and facebook-mcp guide; WhatsApp row added to all comparison tables

2026-06-07 — Documented Facebook first-comment pending-approval state in api-reference/posts, sdks/mcp, platforms/index, and guides/facebook-mcp with consistent ✗ † notation and footnote explaining the pages_manage_engagement Meta review status

2026-06-20 — Updated Analytics API reference: added platform-specific caveat for Facebook metrics — reach, engagements, and clicks now return 0 for Facebook posts; impressions reflects post_media_view; Instagram unaffected

2026-06-22 — Updated Platforms reference: Google Business Profile status updated from Pending approval to Live (go-live date 2026-06-22)

2026-06-22 — Updated Posts API reference: added platformMetadata validation constraints (primitive-only values, max 50 array elements, max 20 keys per platform object); updated scheduledFor scheduling semantics to document saveAsDraft exception to past-date validation

2026-06-22 — Added Inbox — Reviews: new page documenting all GMB review management endpoints (GET /v1/inbox/reviews, GET /v1/inbox/reviews/stats, GET /v1/inbox/reviews/locations, POST /v1/inbox/reviews/:id/reply, PATCH /v1/inbox/reviews/:id/read)

2026-06-22 — Updated Accounts API reference: added Google Business Profile subsection documenting GET /v1/accounts/:id/gmb/locations and PUT /v1/accounts/:id/gmb/locations; updated API key scope table in Authentication with complete scope list and wildcard constraint

2026-06-22 — Updated Errors reference: added requestId field to OAuth error response shape; noted X-Request-Id header on all OAuth handler responses

2026-07-04 — Updated Accounts API reference: added oauthVersion and flobProbe to the Account Fields table; added Instagram-specific OAuth error codes (NO_FACEBOOK_PAGES, NO_IG_ON_PAGES, ACCOUNTS_CENTER_MISMATCH, CREDENTIALS_NOT_CONFIGURED) to the OAuth Edge Cases section; updated Instagram authentication description in platforms reference and Instagram MCP guide to reflect both connect paths

2026-08-08 — Rewrote Contacts API reference: added POST /v1/contacts/:id/messages; documented all new messaging-status fields (socialAccount, lastInboundAt, messengerEligibleUntil, isSuppressed, suppressedAt, suppressionReason, messagesSentCount, sequenceEnrollmentCount); documented list vs. detail response shapes; documented deliveries[] and sequenceEnrollments[] on the detail shape; updated all mutation endpoint response descriptions to reflect the enriched detail shape

2026-08-12 — Updated Inbox — Mentions: added permalinkUrl field to all response field tables and JSON example; added Facebook Mention Author Identity callout documenting the Meta platform constraint on authorId/authorUsername for item: "comment" events; updated Platform Coverage table with Permalink URL column. Note: GET /v1/inbox/comments endpoints also return permalinkUrl in production but no API reference page for Comments exists yet — reference-page creation is a separate task.

Last updated on