Zernio
Zernio
QuickstartBuild a PlatformSDKsCLIMCPWebhooksWorkflowsGuidesSecurityGlossaryPricingBillingChangelogRefer & earn
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources

Changelog

Every change to the Zernio API, newest first.


Significant changes are announced here, on the Telegram channel and on X.

Nothing below breaks a working integration. Every endpoint is versioned in the URL path, currently /v1, and a breaking change ships only as a new path version: /v1 keeps working. New endpoints, new response fields and new error codes arrive inside /v1 at any time, which is why error handling asks you to branch on code. An operation on its way out is marked deprecated: true in the OpenAPI spec and announced here before it is removed.

September 13, 2026New Feature
You can now create YouTube playlists via POST /v1/accounts/{accountId}/youtube-playlists.

This creates an empty playlist on the connected YouTube channel and returns the playlist object; use the returned playlist.id as platformSpecificData.playlistId when publishing a video.

Request body:
• title (required)
• description (optional)
• privacy: private | public | unlisted (default private)

Notes: requires YouTube OAuth scope youtube or youtube.force-ssl; costs 50 YouTube quota units; not idempotent (retrying can create duplicate playlists).
September 11, 2026New Feature
New endpoint GET /v1/ads/campaigns/{campaignId} returns a single Meta campaign live, so you no longer need to page GET /v1/ads/campaigns just to find one campaign.

Call it with campaignId (Meta platformCampaignId) and accountId. Optionally override the Graph projection with fields (passed through to Meta; unknown fields return Meta’s 400).

Key params:
• campaignId (path)
• accountId (query, required)
• fields (query, optional; e.g. id,name,status,daily_budget)

Note: if the resolved connection can’t see the campaign, Meta returns 400 (not 404).

Also: PUT /v1/ads/{adId} may now return 429 when Meta rate-limits writes to metered objects (notably ad creatives). Retry after ~30 seconds.
September 11, 2026Breaking Change
GET /v1/ads/lead-forms/{formId} now returns the full Meta form (including thankyoupage) and can be customized with a Meta Graph field selection.

Use fields (Meta only) to override the default projection so newly-added Meta fields are reachable without an API change (e.g. name,thankyoupage{title,body,buttontype,websiteurl}). Invalid selections are rejected with 400.

Meta lead forms are now strictly scoped to the Page managed by accountId: reading or archiving a form on any other Page returns 404.

Also applies to DELETE /v1/ads/lead-forms/{formId} (archive): you may now receive 404 when the form isn’t on the managed Page.
September 11, 2026Breaking Change
TikTok connect behavior changed on GET /v1/connect/{platform}: TikTok connections now go through the TikTok for Business app, and each profile supports one TikTok account.

Connecting TikTok on a profile that already has one will replace it; reconnecting the same account keeps history, but authorizing a different account can delete the previous account’s analytics/inbox/DM history.

Handle the new redirect error missingtiktokpermissions (user-fixable): the authorization omitted required permissions, so nothing changes and the existing connection keeps working. Re-run the flow and accept all permissions.

Key fields:
• Path: platform=tiktok
• Query: profileId, redirect_url, optional headless
• Redirect error enum now includes: missingtiktokpermissions
September 11, 2026Breaking Change
Google Ads behavior changed for campaign status + targeting.

Campaign status on Google now cascades on resume via PUT /v1/ads/campaigns/{campaignId}/status.
Setting status to active turns on the campaign and the ad groups/ads Zernio tracks under it in one mutate (Google’s most-restrictive switch wins). Setting paused still writes the campaign only.

Campaign targeting reads now return real device bid adjustments via GET /v1/ads/campaigns/{campaignId}/targeting.
devices[].bidModifier is now Google’s bid adjustment (null = none, 0 = device switched off), and the returned device list depends on channel (Search: MOBILE/DESKTOP/TABLET; Display also includes CONNECTED_TV).

Device targeting edits are stricter on Google via PUT /v1/ads/campaigns/{campaignId}/targeting.
When sending targeting.devices, any supported device you omit is switched off (written with bidModifier: 0). Sending a device the campaign channel doesn’t support, or switching every device off, now returns 422.
September 11, 2026New Feature
New endpoints let you inspect and re-subscribe a connected Facebook Page’s webhook field subscription (read live from Meta), so you can detect and fix cases where leadgen is missing and lead ads stop arriving.

Read current subscription:
• GET /v1/accounts/{accountId}/webhook-subscription
Returns appSubscribed, leadgen, subscribedFields, and warning (only when leadgen is missing).

Re-subscribe (re-sends full field set to Meta and reads back what was actually granted):
• POST /v1/accounts/{accountId}/webhook-subscription
Returns resubscribed, plus appSubscribed, leadgen, subscribedFields, and optional warning.

Both endpoints can return 409 when the connection has no selected Page; the POST can return 502 if Meta rejects the subscription.
September 11, 2026Breaking Change
Meta Promotion is no longer supported in the Ads API.

Meta validates promotion metadata on create, but discards it, so Zernio now rejects it to avoid false positives. Any request that sends a Promotion object will now fail.

Changes:
• POST /v1/ads/create: promotion is rejected (400) on all shapes
• POST /v1/ads/creatives: promotion is rejected (400)
• PUT /v1/ads/{adId}: creative.promotion is not supported; any object is rejected (400)
• GET /v1/ads/{adId}: removed query param refreshPromotion

Use creativeFeatures (values OPTIN | OPTOUT) for Advantage+ enhancements; set Promotions directly in Meta Ads Manager instead.
September 11, 2026Improvement
Analytics post items now include ad detection via isAd in AnalyticsListResponse.posts[].

Use this flag to exclude paid delivery from organic reporting when a post’s metrics include ads (e.g., LinkedIn dark posts, TikTok Spark/boosted promotions).

New field:
• isAd (boolean) — true when metrics include paid delivery; false otherwise (note: some TikTok uploaded-asset ads can’t be detected and may still report false).
September 11, 2026Improvement
TikTok posting behavior is now clarified for TikTok for Business accounts when using videoCoverImageUrl and videoMadeWithAi.

For TikTok for Business connections, videoCoverImageUrl is passed to TikTok as the cover directly (no stitching), and the URL must resolve on a TikTok-verified domain.

Also, videoMadeWithAi disclosure is supported on video posts only for TikTok for Business: setting videoMadeWithAi=true on a direct photo post will be rejected. Use draft=true to send the photo to Creator Inbox and set the disclosure in the TikTok app.

Key fields:
• videoCoverImageUrl
• videoMadeWithAi
• draft
September 11, 2026New Feature
PUT /v1/ads/{adId} now supports updating Google Ads creatives beyond Search: you can update Display responsive ads and Performance Max asset groups.

Google creative updates are now dispatched by the ad’s advertisingChannelType, and unsupported creative fields for a channel return 422.

Key request fields:
• Google Search + Display: headlines, descriptions, finalUrls
• Google Display (additional under creative): creative.longHeadline, creative.businessName, creative.imageUrl, creative.squareImageUrl
• Google Performance Max: assetGroup (schema GooglePmaxAssetGroupUpdate) with fields like finalUrl, headlines, descriptions, businessName, images.landscape/images.square/images.logo, youtubeVideoIds

Channel validation highlights:
• Display rejects pinned positions (no pinnedField)
• assetGroup returns 422 on non-Performance-Max ads; other Google-only creative fields return 422 when the channel can’t carry them
September 11, 2026New Feature
Google Ads ad updates now support editing campaign-level geo + language targeting via PUT /v1/ads/{adId}.

You can now replace the campaign’s targeting.locations (or top-level geo fields) and targeting.languages from the ad update call; each provided list replaces the full set, and omitted fields are left unchanged.

Key fields:
• targeting.locations: country-code array or { countries, regions, cities, zips, metros• targeting.languages: ["en", "de", "zh_CN", ...] (full replacement)

Behavior/validation:
• Empty
locations now returns 400 (omit locations to leave targeting unchanged)
•
customLocations (radius targeting) returns 422
• Don’t mix
targeting.locations with top-level geo fields (countries/regions/cities/zips/metros): mixing returns 400`
• Location/language updates are applied atomically in one Google mutate (no half-applied sets); negative (excluded) locations are left untouched
September 11, 2026New Feature
LinkedIn B2B targeting facets can now be searched via GET /v1/ads/targeting/search using new dimension values.

This lets you resolve LinkedIn facet values to the exact URNs to pass into TargetingSpec, so you don’t need to assemble URNs manually.

Use dimension:
• industry → feeds TargetingSpec.industries
• jobFunction → feeds TargetingSpec.jobFunctions
• seniority → feeds TargetingSpec.seniorities
• companySize → feeds TargetingSpec.companySizes

Returned results[].id is the full URN (e.g. urn:li:industry:4, urn:li:function:8, urn:li:seniority:6, urn:li:staffCountRange:(51,200)).
September 11, 2026Breaking Change
comment.received webhook payloads now include Threads and no longer include X/Twitter.

You can now receive comment events for Threads posts. If you validate or route by platform, update your handling to match the new enum and note that X/Twitter does not fire this event.

Update comment.platform enum to:
• instagram, facebook, threads, youtube, linkedin, bluesky, reddit, tiktok
September 10, 2026Improvement
Meta campaign budget updates via PUT /v1/ads/campaigns/{campaignId} now check the live campaign budget mode before applying changes.

This prevents stale local ABO/CBO state from incorrectly blocking updates, and a successful update will also repair local mirrored budget fields.

Key behavior:
• If the live campaign is CBO, sending budget (with type = daily | lifetime) can succeed even if local data previously indicated ABO.
• A live ABO campaign still returns 409 for campaign-level budget edits (use the ad-set budget endpoint instead).
September 10, 2026New Feature
Meta placement asset customization on POST /v1/ads/create now supports per-placement copy overrides.

You can pin different headline, body, and description per placement rule while keeping linkUrl and callToAction shared.

Use:
• placementAssets.defaultImageUrl
• placementAssets.rules[].placements
• placementAssets.rules[].imageUrl
• placementAssets.rules[].headline
• placementAssets.rules[].body
• placementAssets.rules[].description

Also updated: validateOnly: true now supports validating placementAssets for all-image setups (video placement validation still requires uploads).
September 10, 2026New Feature
You can now purchase an exact phone number by passing phoneNumber to POST /v1/phone-numbers/purchase (and the deprecated alias POST /v1/whatsapp/phone-numbers/purchase).

Pick a number from GET /v1/phone-numbers/available, then buy that specific E.164 number instead of getting an auto-assigned one.

New request field:
• phoneNumber (E.164, e.g. +14155552671)

New purchase failure case:
• 409 with code = PHONENUMBERUNAVAILABLE (number no longer available / rejected at buy-time)

GET /v1/phone-numbers/available results now include:
• numbers[].locality
• numbers[].bestEffort (true if carrier returned a number outside your requested filters)

Note: regulated countries that return 202 status = kyc_required do not support buying an exact phoneNumber.
September 10, 2026Improvement
GET /v1/analytics now supports sorting by two new metrics: completionrate and profileviews.

This lets you rank posts by TikTok (TikTok for Business app) completion and profile traffic directly in analytics queries.

Use sortBy:
• completion_rate
• profile_views

The delta feed GET /v1/analytics/delta also includes these fields in metrics: completionRate, profileViews.
September 10, 2026New Feature
POST /v1/inbox/conversations now supports WhatsApp CAROUSEL template sends via templateCards.

This lets you override per-card variables, header media, and buttons when sending a carousel template (card body variables restart at {{1}} per card, so they can’t be represented in the flat templateParams list).

Use templateCards (max 10), each item:
• cardIndex (0–9)
• params (card body variable values)
• headerMedia: type = image | video | document + (link or id)
• buttons (max 2): subType = quick_reply | url

Invalid carousel card overrides now return 400 with code = INVALIDTEMPLATECARD_PARAM.
September 10, 2026Breaking Change
Meta Ads business login connection flow now supports an automatic Page picker when multiple Facebook Pages are granted.

If a business-login connect results in multiple Pages, GET /v1/connect/meta-ads/callback may redirect to the Page picker instead of completing immediately.

Facebook Page selection endpoints now support a new business-login grant token:
• GET /v1/connect/facebook/select-page can be called with selectionToken (instead of profileId + tempToken)
• POST /v1/connect/facebook/select-page now accepts { selectionToken, pageId } to complete the business-login Meta Ads connection

Notes:
• selectionToken is encrypted and expires in 10 minutes
• Business selection requires session/bearer auth for the initiating user; X-Connect-Token is not accepted for the business selectionToken flow
September 10, 2026Improvement
POST /v1/ads/campaigns and POST /v1/ads/create now default Meta buyingType to AUCTION.

If you omit buyingType, Zernio will treat it as AUCTION and explicitly send it on new Meta campaigns (including validateOnly requests).

Set buyingType to:
• AUCTION
• RESERVED (Reach & Frequency; requires rfPredictionId on /v1/ads/create)
September 10, 2026New Feature
You can now pin/unpin TikTok comments (TikTok for Business–connected accounts only).

Pinning lets you keep a top-level comment at the top of the post’s comment section.

New endpoints:
• POST /v1/inbox/comments/{postId}/{commentId}/pin with body accountId
• DELETE /v1/inbox/comments/{postId}/{commentId}/pin with query accountId

TikTok Inbox support was also expanded:
• GET /v1/inbox/comments/{postId} now supports TikTok (TikTok for Business). Use commentId (query) to page a comment’s full replies; each top-level comment may include up to 3 inline replies.
• POST /v1/inbox/comments/{postId}/{commentId}/hide and DELETE /v1/inbox/comments/{postId}/{commentId}/hide now support TikTok (TikTok for Business).

Behavior change to handle:
• POST /v1/posts/sync-external may now return 409 with code adsconnectionrequired when the account is inactive/needs reconnection (stop scheduled retries until reconnected).

Webhook schema update:
• WebhookPayloadComment.comment.platform now includes tiktok (TikTok events may include only author id, without username/picture/owner flag).
September 10, 2026New Feature
New Tools endpoint: GET /v1/tools/tiktok/download.

It lets you fetch a TikTok video download URL or list available formats (uses Tools API access + Tools rate limit).

Key params:
• url (required)
• action: download | formats (default download)
• formatId (optional; from formats response)

Notes:
• Provider-side blocks/gateway failures can return 503; unavailable video returns 404.

Also: several Analytics/Accounts/Inbox endpoints now explicitly return 502/503 for upstream/temporary failures (handle retry/backoff). LinkedIn like endpoints now return alreadyReacted/reactionType on 200, and may return 409 when a different reaction already exists.
September 10, 2026Minor
Added 4 new endpoints:

GET /v1/ads/pixels - List TikTok ad pixels
GET /v1/ads/partnership-content - List partnership ad content
GET /v1/ads/partnership-permissions - List partnership permissions
POST /v1/ads/partnership-permissions - Set partnership permission
September 10, 2026New Feature
Meta Ads now supports Business Login (system-user tokens) as an opt-in flow.

Use it to connect/reconnect metaads without creating/requiring a posting parent account, and to select a granted Facebook Page for creatives/lead forms.

Start the flow via GET /v1/connect/{platform}/ads with:
• platform: facebook | instagram
• loginMode: business (default: classic)
• optional pageId: ^\d+$

Complete the OAuth redirect via the new callback endpoint:
• GET /v1/connect/meta-ads/callback with state (required) and code (or error)
• On success it redirects (307) with connected=metaads, profileId, accountId

Notable behavior/errors:
• Business login always returns an authUrl; returns 503 if not configured
• Reconnect grant mismatches can return 409
• GET /v1/ads/accounts adds Meta fields businessId and businessName
• Lead forms/leads endpoints now accept a metaads business-login accountId (uses system-user token)
September 10, 2026New Feature
Google Ads now supports Performance Max creation via POST /v1/ads/create, and you can list created asset groups via GET /v1/ads/campaigns/{campaignId}/asset-groups.

This lets you create a PAUSED PMax campaign + asset group atomically (with optional validateOnly), then read back the linked text/image/YouTube assets for a campaign.

Create PMax by setting:
• campaignType = pmax
• assetGroup (required)
• budgetType = daily | lifetime
• status = PAUSED (PMax rejects ACTIVE)

Key assetGroup fields:
• finalUrl
• headlines (3-15)
• longHeadline
• descriptions (2-5)
• businessName
• images.landscape, images.square, images.logo (logo required)
• youtubeVideoId (optional)

List asset groups:
• GET /v1/ads/campaigns/{campaignId}/asset-groups returns assetGroups, cachedAt, stale (may serve cached data when Google quota is exhausted).
September 10, 2026New Feature
New endpoint: POST /v1/ads/accounts creates a durable Meta ad account in the user’s business portfolio using their connected Meta Ads token.

Use it to self-serve ad account creation (note: the account starts without a payment method; the user must add payment/invoicing in Ads Manager before ads can deliver).

Required body fields: accountId, businessId, name, currency, timezoneId (Meta numeric timezone ID).

Optional fields include endAdvertiser, mediaAgency, partner (default NONE), plus invoicing options like invoice, invoiceGroupId, invoicingEmails, io, poNumber, fundingId.

Response 201 returns adAccountId (e.g. act_...), connectionUpdated, paymentMethodRequired, adsManagerUrl, nextSteps, warnings.

Important: this operation is not idempotent. After a timeout or 502 with details.creationStatus=unknown, check Ads Manager before retrying.
September 10, 2026New Feature
Phone number availability now indicates when an out-of-stock country/type can be pre-ordered.

If available is false but preOrderable is true, you can still proceed with KYC and the carrier will source the number after review (typically ~3 weeks; not guaranteed). Only document tiers (3/4) qualify.

New fields:
• GET /v1/phone-numbers/availability (+ deprecated alias /v1/whatsapp/phone-numbers/availability): preOrderable
• GET /v1/phone-numbers/countries: preOrderable and per-type types[].preOrderable, plus types[].fulfilment = instant | request (and types[].tier may be null for fulfilment: request)
• POST /v1/phone-numbers/kyc (+ deprecated alias /v1/whatsapp/phone-numbers/kyc): response adds preOrder (true when the submission placed a pre-order; pre-orders are 1 number and aren’t billed until active)

Note: POST /v1/phone-numbers/stock-watches is now mainly for true stock returns; if preOrderable is true, you can submit KYC instead of creating a watch.
September 10, 2026Improvement
WhatsApp messages can now include location pins in the message.received webhook via metadata.location.

This lets you read the shared coordinates directly from the webhook (the message text is only the preview like 📍 <name>).

New field:
• metadata.location with latitude, longitude, name, address

Also, message.sent webhook metadata now includes WhatsApp context so you can detect non-text sends without a GET:
• metadata.location
• metadata.contacts
(Existing fields like metadata.quotedMessageId and metadata.threadTs are unchanged.)
September 9, 2026New Feature
Google Ads now supports managing extension assets (sitelinks, callouts, structured snippets) at account, campaign, and ad-group levels, plus richer Google RSA reads/updates.

New asset endpoints:
• Campaign assets: GET /v1/ads/campaigns/{campaignId}/assets, PUT, DELETE
• Ad-group assets: GET /v1/ads/ad-sets/{adSetId}/assets, POST, PUT, DELETE
• Account assets:
- Callouts: GET /v1/ads/accounts/callouts, POST, PUT, DELETE
- Sitelinks: GET /v1/ads/accounts/sitelinks, POST, PUT, DELETE
- Structured snippets: GET /v1/ads/accounts/structured-snippets, POST, PUT, DELETE

Key request params:
• accountId (required) and optional customerId (required when the connection has multiple customers)

Notes:
• List endpoints may return cache metadata: cachedAt and stale
• Asset updates are in-place via updates[] (max 20) with assetResourceName; assets are shared, so edits affect all attachments
• Removes detach only (assets themselves can’t be deleted)

Google RSA changes:
• GET /v1/ads/{adId} may now include top-level cachedAt and stale for Google RSA enrichment
• PUT /v1/ads/{adId} now supports Google RSA text updates via top-level headlines, descriptions, finalUrls (each array replaces the full list; omit a field to keep it). Pinning uses pinnedField: HEADLINE1 | HEADLINE2 | HEADLINE3 and DESCRIPTION1 | DESCRIPTION_2.
September 9, 2026New Feature
TikTok Ads now supports ad comment moderation via new endpoints for replying, hiding/unhiding, and deleting comments.

This lets you moderate first-level TikTok ad comments directly from the Ads API (replies-to-replies are rejected; reply is not idempotent).

New endpoints:
• POST /v1/ads/{adId}/comments/{commentId}/reply with text
• POST /v1/ads/{adId}/comments/{commentId}/hide with hidden (true/false)
• DELETE /v1/ads/{adId}/comments/{commentId}

All three support TikTok comment lookup window params since/until (max 30 days; defaults to last 30 days).

Also updated: GET /v1/ads/{adId}/comments now supports TikTok (adds since/until; placement is Meta-only and returns 400 for TikTok). TikTok replies are returned as separate comments with parentId.

Meta Ads additions for app-promotion setup:
• GET /v1/ads/instagram-accounts (discover IG ad identities)
• GET /v1/ads/advertisable-applications
• GET /v1/ads/ios-fourteen-campaign-limits

Campaign creation update (Meta): POST /v1/ads/campaigns adds validateOnly, plus SKAdNetwork/app-promotion fields isSkadnetworkAttribution, promotedObject, and buyingType (AUCTION/RESERVED).
September 9, 2026New Feature
Meta ads now support explicit Promotions and Advantage+ creative enhancements across create/update flows, with a promotion readback status.

You can attach an offer via promotion and control enhancements via creativeFeatures. When promotion is used, responses include promotionStatus to indicate whether Meta returned promotion metadata.

Use:
• POST /v1/ads/create: promotion, creativeFeatures (also as defaults for creatives[]; per-item override supports promotion: null)
• POST /v1/ads/creatives: promotion, creativeFeatures (response adds promotion, promotionStatus)
• PUT /v1/ads/{adId}: creative.promotion, creative.creativeFeatures (send promotion: null to remove)

promotionStatus values: applied | not_returned | unavailable

Also:
• GET /v1/ads/{adId} adds refreshPromotion=true|false to read live Meta promotion metadata and include promotionStatus.
• POST /v1/ads/{adId}/duplicate now preserves Meta-returned creative settings (including promotion metadata and creativeFeatures) when a native copy requires a creative rebuild.
September 9, 2026Breaking Change
Meta messaging boosts via POST /v1/ads/boost now require an explicit messaging CTA; destinationType no longer auto-selects one.

This prevents accidental messaging-boost behavior when you only intended to set an ad-set destination, and makes CTA/destination handling more predictable.

Key changes:
• Messaging boosts must use goal=engagement with callToAction in WHATSAPPMESSAGE | MESSAGEPAGE | INSTAGRAM_MESSAGE
• destinationType is now independent for non-messaging CTAs; if you do use a messaging callToAction, any explicit destinationType must match
• whatsappPhoneNumber now requires callToAction=WHATSAPP_MESSAGE (not just destinationType=WHATSAPP)

Also: Ad.creative.whatsappPhoneNumber is now returned (when provided) so you can read back the explicit WhatsApp number used on Meta boosts/messaging ads.
September 9, 2026New Feature
Google Ads now supports shared negative keyword lists (create/list/get/update/delete), including attaching lists to campaigns and replacing the full keyword set.

Use:
• GET /v1/ads/accounts/negative-keyword-lists (cached ~10 min; may return stale=true)
• POST /v1/ads/accounts/negative-keyword-lists (not idempotent)
• GET /v1/ads/accounts/negative-keyword-lists/{listId}
• PUT /v1/ads/accounts/negative-keyword-lists/{listId} (rename)
• DELETE /v1/ads/accounts/negative-keyword-lists/{listId}
• PUT /v1/ads/accounts/negative-keyword-lists/{listId}/keywords (replace full set; keywords max 5000)
• GET /v1/ads/campaigns/{campaignId}/negative-keyword-lists
• PUT /v1/ads/campaigns/{campaignId}/negative-keyword-lists (replace associations; listIds max 20)

Key inputs:
• accountId (required on account-scoped routes)
• customerId (required when a connection has multiple customers)
• platform=google (other platforms return 501)
• Keyword matchType: broad | phrase | exact

Google campaign updates now support daily budget via PUT /v1/ads/campaigns/{campaignId}. Shared budgets return 409 unless allowSharedBudgetUpdate=true is explicitly provided.

Campaign analytics for Google now includes impression share ratios in analytics.summary (searchImpressionShare, searchBudgetLostImpressionShare, searchRankLostImpressionShare, searchTopImpressionShare, searchAbsoluteTopImpressionShare) plus analytics.impressionShareCache.{cachedAt,stale}; may return 429 if quota is exhausted without a cached result.

Meta boosts now support messaging boosts and WhatsApp number selection:
• POST /v1/ads/boost adds destinationType=INSTAGRAMDIRECT and whatsappPhoneNumber (E.164), and supports messaging CTAs (WHATSAPPMESSAGE | MESSAGEPAGE | INSTAGRAMMESSAGE) with goal=engagement.

Messaging/CTWA ad creation now supports reusing existing posts:
• POST /v1/ads/messaging and POST /v1/ads/ctwaexistingPostId or objectStoryId (mutually exclusive with fresh creative fields), plus optional whatsappPhoneNumber` for WhatsApp destination.
September 9, 2026Improvement
The bootstrap flow for GET /v1/analytics/delta has changed: take the cursor before loading your baseline.

To avoid missing changes during initial sync, first call GET /v1/analytics/delta with no cursor to capture nextCursor, then load the baseline via GET /v1/analytics, then resume the delta feed from that captured nextCursor.

Key behavior:
• Bootstrap: omit cursor → response has empty data + nextCursor
• Resume: pass cursor=<nextCursor> verbatim (opaque)
• Retention: cursors older than ~6 days return 400; recover by repeating the same bootstrap order
September 9, 2026New Feature
Google Ads now supports the attach flow on POST /v1/ads/create via adSetId.

You can add a new ad into an existing Google Ads ad group (inherits the ad group’s budget/targeting), instead of creating a new campaign/ad group.

Use:
• adSetId (Google AD GROUP id)
• campaignType: search | display (default display)

Notes:
• goal is still required even though budget/targeting are inherited
• In attach mode, budgetAmount/budgetType and bidding fields (bidStrategy, bidAmount, portfolioBidStrategyId) return 400
• For Search ad groups, set campaignType to search (otherwise it’s treated as Display and requires images.landscape + images.square + businessName)
September 9, 2026Breaking Change
GET /v1/ads/audiences/{audienceId} response renamed metaData to platformData.

This field still contains fresh data from the underlying ads platform API (when available), but clients must now read platformData instead of metaData.

Also clarified across audience endpoints that {audienceId} is the Zernio audience id (audiences[].id from GET /v1/ads/audiences), not the platform segment id. audiences[].id can be null when the audience wasn’t created through Zernio.
September 9, 2026New Feature
WhatsApp BYO numbers now support Meta re-verification via 2 new endpoints: POST /v1/accounts/{accountId}/whatsapp/request-code and POST /v1/accounts/{accountId}/whatsapp/verify-code.

Use this when Meta demotes a migrated/bring-your-own number to re-verification: request an OTP to the customer handset, then submit it to verify with Meta (and call POST /v1/accounts/{accountId}/whatsapp/register afterward to complete Cloud API activation).

Request a code with optional:
• method: SMS | VOICE
• language (Meta locale, e.g. en_US)

Verify with:
• code (6 digits; separators like 749-456 are accepted)

Note: request-code is rate-limited to 1 request per 10 minutes per account and may return 429 with Retry-After.
September 9, 2026Breaking Change
Reddit posting behavior has changed for forceSelf and nativeVideo in RedditPlatformData.

forceSelf now creates a text-only self post and skips native media uploads even if media is provided. For native video with body text, omit forceSelf and provide content plus a video mediaItem.

nativeVideo fallback behavior is now stricter when body text is present: if the post includes body text, upload failures or subreddit video restrictions will fail the post instead of falling back to a link. Automatic link fallback applies only when there is no body text.
September 9, 2026New Feature
WhatsApp connect now supports a hosted Embedded Signup flow to avoid double-selection when a Facebook login can access multiple WhatsApp numbers.

Start it with GET /v1/connect/whatsapp + signup=hosted (optional onboarding: api | business_app). The returned authUrl points to a Zernio-hosted page (no state in the response).

You can co-brand the hosted page using:
• brandName
• primaryColor (hex #RRGGBB)
• language: en | es

New endpoint: GET /v1/connect/whatsapp/sdk-config returns the Embedded Signup config (appId, configId, branding). Auth via API key or X-Connect-Token.

POST /v1/connect/whatsapp/embedded-signup now also supports the hosted flow:
• Auth via API key or X-Connect-Token
• Request adds redirectUrl and echoConnectToken
• Response adds account and optional redirectUrl

Hosted flow adds two WhatsApp error values you may see on redirect: connectioncancelled, sessionexpired.
September 8, 2026Improvement
PUT /v1/posts/{postId} can now return 409 when the requested scheduledFor collides with another post already occupying that slot in the same queue (code: queueslotconflict).

This lets clients distinguish queue slot conflicts from generic validation errors and handle rescheduling explicitly.

If you hit 409, you can:
• choose a different scheduledFor
• omit scheduledFor to let the queue pick the next open slot
• send queueId: null to schedule outside the queue
September 8, 2026New Feature
Meta generic templates now support setting the image aspect ratio on Facebook via imageAspectRatio.

This lets you control how Messenger renders carousel element images (default is horizontal). Note: Instagram does not support this setting and will return 400 if it’s included.

Use template.imageAspectRatio with:
• horizontal
• square

Applies to:
• POST /v1/inbox/conversations/{conversationId}/messages (generic template)
• CommentAutomationTemplate.imageAspectRatio
September 8, 2026New Feature
WhatsApp templates now support configuring a per-template delivery TTL via messagesendttl_seconds.

This lets you set a validity window for template messages (messages not delivered within the TTL are dropped) and read back the custom TTL when it’s set.

Set messagesendttl_seconds on:
• POST /v1/whatsapp/templates
• PATCH /v1/whatsapp/templates/{templateName}
• PATCH /v1/whatsapp/templates/id/{templateId}

Read messagesendttl_seconds from:
• GET /v1/whatsapp/templates
• GET /v1/whatsapp/templates/{templateName}
• GET /v1/whatsapp/templates/id/{templateId}

TTL ranges by category:
• AUTHENTICATION: 30–900
• UTILITY: 30–43200
• MARKETING: 43200–2592000

Use -1 to restore the category default (for AUTHENTICATION and UTILITY).
September 8, 2026Breaking Change
GET /v1/ads/library now supports searching Meta’s Ad Library without a connected account.

Meta searches run using Zernio’s developer access, so you can call the endpoint with platform=meta and omit accountId. LinkedIn searches still require a connected accountId.

Use:
• platform: meta | linkedin (required unless accountId is provided)
• accountId: required for LinkedIn; optional for Meta (passing a Meta account also selects Meta)

Meta-specific behavior changes:
• Requests may return 429 due to shared Meta quota (back off ~1 minute)
• New 503 when Meta Ad Library is unavailable on Zernio’s side (PLATFORM_DISABLED)

Also updated: 403 no longer includes Meta identity confirmation errors; it’s now “Ads access required” (legacy plans may need the Ads add-on).
September 7, 2026Improvement
Several Google Ads endpoints now serve cached data (with fallback when Google quota is exhausted), and responses include freshness metadata.

These reads may no longer be strictly live; when quota is exhausted, the API can return the last-good copy instead of failing.

New response fields:
• cachedAt (date-time | null) — when the data was fetched from Google
• stale (boolean) — true when quota was exhausted and the response is a cached fallback

Applies to:
• GET /v1/ads/search-terms
• GET /v1/ads/bid-strategies
• GET /v1/ads/campaigns/{campaignId}/bidding
• GET /v1/ads/campaigns/{campaignId}/negative-keywords
• GET /v1/ads/campaigns/{campaignId}/targeting
• GET /v1/ads/accounts/callouts
• GET /v1/ads/accounts (Google only)
• GET /v1/ads/conversions/actions

Also: PUT /v1/ads/campaigns/{campaignId}/targeting now returns the refreshed devices/locations/languages state read back from Google after the edit (and invalidates the cached GET copy).
September 7, 2026New Feature
Google Ads now supports portfolio bid strategies and live campaign bidding reads.

You can list/create/update shared (portfolio) bidding strategies and attach them to campaigns, plus fetch a campaign’s current bidding setup for pre-filling edit forms.

New endpoints:
• GET /v1/ads/bid-strategies (accountId, optional customerId, fromDate, toDate)
• POST /v1/ads/bid-strategies (accountId, name, type, optional targetCpa/targetRoas)
• PATCH /v1/ads/bid-strategies/{strategyId} (accountId, optional name, type, targetCpa, targetRoas)
• GET /v1/ads/campaigns/{campaignId}/bidding (accountId, platform=google, optional customerId)

Attach a portfolio strategy to Google campaigns using portfolioBidStrategyId on:
• POST /v1/ads/campaigns
• PUT /v1/ads/campaigns/{campaignId}
• POST /v1/ads/create

Portfolio strategy type values:
• TARGETCPA, TARGETROAS, MAXIMIZECONVERSIONS, MAXIMIZECONVERSION_VALUE

Also on Google ad updates, PUT /v1/ads/{adId} now supports device bid adjustments via targeting.devices with devices MOBILE, DESKTOP, TABLET, CONNECTED_TV (optional bidModifier 0.1–10).
September 7, 2026New Feature
Google Ads now supports editing more campaign and account assets via new endpoints.

You can now read/update campaign targeting (devices, locations, languages), create/list standalone ad groups, manage account-level callout extensions, and list/create conversion actions (with tag snippets).

Campaign targeting:
• GET /v1/ads/campaigns/{campaignId}/targeting (platform=google)
• PUT /v1/ads/campaigns/{campaignId}/targeting body: platform=google, targeting with any of devices/locations/languages (each provided field replaces existing)
• devices enum: MOBILE, DESKTOP, TABLET, CONNECTED_TV

Ad groups (ad sets):
• GET /v1/ads/ad-sets (filters: accountId, campaignId, platform)
• POST /v1/ads/ad-sets (Google-only) with optional Idempotency-Key

Account-level callouts (Google-only):
• GET /v1/ads/accounts/callouts
• POST /v1/ads/accounts/callouts (callouts: 1–25 chars, up to 20)
• DELETE /v1/ads/accounts/callouts (remove by assetId)

Conversion actions (Google-only):
• GET /v1/ads/conversions/actions (optional customerId, optional type)
• POST /v1/ads/conversions/actions create type=WEBPAGE and returns tagSnippets

Also updated:
• POST /v1/ads/create now supports Google campaign language targeting via languages (e.g. ["en","de"]; unknown codes return 400)
• GET /v1/ads/targeting/search adds dimension=language (Google-only) to resolve Google language_constant options
September 7, 2026New Feature
Google Ads Search keyword management is now available via new endpoints.

You can add keywords without replacing the existing set, pause/enable a keyword, remove a keyword, and manage campaign-level negative keywords separately from ad-group negatives.

Add keywords to an ad group with POST /v1/ads/keywords using:
• accountId
• adSetId
• keywords (string = BROAD, or { text, matchType } where matchType is exact/phrase/broad)
• negative (boolean) to add ad-group-level negatives

Pause/enable a keyword with PATCH /v1/ads/keywords/{keywordId}:
• status: active or paused (note: negative keywords can’t be paused/enabled)

Remove a keyword with DELETE /v1/ads/keywords/{keywordId}.

Campaign-level negative keywords:
• List: GET /v1/ads/campaigns/{campaignId}/negative-keywords
• Replace full set: PUT /v1/ads/campaigns/{campaignId}/negative-keywords with keywords (send an empty array to clear all)

Also updated: POST /v1/ads/create (Google Search) now accepts match types for keywords and negativeKeywords via KeywordEntry, and adds campaignNegativeKeywords for campaign-level negatives at create time.
September 7, 2026Improvement
GET /v1/ads/timeline now returns allConversions per day, and clarifies Google customer-level reporting when adAccountId is a Google customer id.

This lets you chart Google’s “all conversions” alongside conversions (tracked conversions), matching Google Ads’ metrics.all_conversions.

New field in each rows[] item:
• allConversions - all conversions (0 on platforms without this concept)

Google note:
• With adAccountId set to a Google customer id, the timeline represents customer-level performance (clicks, cost, impressions, conversions, allConversions per day).
September 7, 2026New Feature
New endpoint GET /v1/ads/library is available to search the public Ad Library for competitor/market research using your connected account token.

It supports both Meta and LinkedIn and returns ads in the platform’s raw shape under data, with pagination via paging.after (cursor; null when exhausted).

Required: accountId
Common query params: q, countries, since, until, limit, after
Meta-only: pageIds, adType (ALL/POLITICALANDISSUEADS/HOUSINGADS/EMPLOYMENTADS/FINANCIALPRODUCTSANDSERVICESADS), status (ACTIVE/INACTIVE/ALL), platforms, mediaType (ALL/IMAGE/MEME/VIDEO/NONE), languages, searchType (KEYWORDUNORDERED/KEYWORDEXACTPHRASE), fields
LinkedIn-only: advertiser (and limit max is 25)

Notes: Passing a platform-unsupported param returns 400 naming the param. Meta may return 403 metaidentityconfirmation_required until the Facebook user behind the connection confirms identity/location.

Also: POST /v1/inbox/comments/{postId}/{commentId}/private-reply now documents an additional Instagram failure case as a non-retryable 400 when Meta declines the private reply (subcode 2534066).
September 7, 2026Breaking Change
POST /v1/phone-numbers/stock-watches now supports watching stock by number type via numberType.

You can create a watch for a specific type in a country, or omit numberType to watch all types. Watches are now unique per country + numberType (up to 20 watches total).

Request body:
• country
• numberType (optional): local | mobile | national | toll_free

Response schema PhoneNumberStockWatch now includes required numberType (nullable; null means “all types”).
September 4, 2026Improvement
POST /v1/inbox/conversations/{conversationId}/messages now includes a documented 500 response when the upstream platform rejects or fails the send.

This clarifies that Zernio does not retry sends internally, and that retrying after a 500 can result in duplicate messages because upstream failures may be ambiguous.

Key response:
• 500 - platform rejected/failed the send; retry only if duplicates are acceptable (use Idempotency-Key to protect the “response lost after success” case, but it won’t guarantee safety after an upstream-accepted failure).
September 4, 2026New Feature
Meta ad sets now support minimum spend targets via platformSpecificData on PUT /v1/ads/ad-sets/{adSetId} and when creating Meta ads via POST /v1/ads/create.

This lets you reserve a share of a CBO (campaign-level) budget for a specific ad set using Meta’s dailyminspendtarget / lifetimeminspendtarget.

Set one of:
• platformSpecificData.dailyMinSpendTarget
• platformSpecificData.lifetimeMinSpendTarget

Notes:
• dailyMinSpendTarget and lifetimeMinSpendTarget are mutually exclusive (400)
• Requires campaign-level budget (CBO); on an ABO ad set this is rejected (PUT returns 409; create returns 400)
• Read back via GET /v1/ads/ad-sets/{adSetId}?fields=dailyminspendtarget (or lifetimeminspendtarget)
September 4, 2026Improvement
GET /v1/ads/tree: the ads array in AdTreeAdSet is now explicitly defined as the 100 highest-spending ads over the requested date range when an ad set has more than 100 ads.

This means the contents of AdTreeAdSet.ads can change based on fromDate/toDate; use adCount for the true total size of the ad set.

Key fields: AdTreeAdSet.ads, fromDate, toDate, adCount
September 3, 2026New Feature
WhatsApp Flows now supports managing the Flows endpoint encryption key via new endpoints.

You can check whether a business RSA public key is registered (and retrieve it) and register/replace the key used for endpoint-backed flows (flowaction: dataexchange).

New endpoints:
• GET /v1/whatsapp/flows/encryption-key (query accountId) returns publicKey, registered, signatureStatus (VALID | MISMATCH)
• POST /v1/whatsapp/flows/encryption-key body: accountId, businessPublicKey (PEM)

Note: only one key is active per phone number; replacing the key requires your flow endpoint to serve the matching private key or data_exchange flows will fail at runtime.
September 3, 2026New Feature
Meta ads created via POST /v1/ads/create now support Multiple Text Options (Advantage+ Flexible Format) using new top-level fields bodies, headlines, and optional descriptions.

This lets you send 1–5 variations of primary text/headlines (and optionally descriptions) and have Meta optimize delivery across them, without using full dynamicCreative.

Use:
• bodies (1–5)
• headlines (1–5)
• descriptions (0–5)

Requires imageUrl or video, plus linkUrl and callToAction. When set, top-level body and headline are used for preview text (objectstoryspec), and this mode is mutually exclusive with dynamicCreative, placementAssets, carouselCards, and creatives[].
September 3, 2026New Feature
Instagram publishing now supports paid partnership, sponsor tagging, comment toggling, and location tagging via new fields in platformSpecificData.

Use:
• isPaidPartnership (boolean) — adds the “Paid partnership” label (feed/Reels/carousels; Stories return 400). Requires Facebook Login connection.
• brandedContentSponsors (array, max 2) — tag sponsor brands by username (optional @) or numeric IG user ID; implies isPaidPartnership.
• commentsEnabled (boolean, default true) — when false, turns off comments after publish (best-effort; Stories ignored).
• locationId (string digits) — tags the post location using a Facebook Page ID with location data (feed/Reels/carousels; Stories rejected with 400).
September 3, 2026Improvement
Meta ads now support regional regulation identity mapping via regionalRegulationIdentities on POST /v1/ads/create and POST /v1/ads/boost.

When you include regionalRegulatedCategories (e.g. BRAZILREGULATION, SINGAPOREUNIVERSAL, TAIWANUNIVERSAL, THAILANDUNIVERSAL, AUSTRALIAFINSERV, INDIAFINSERV, TAIWAN_FINSERV), you can now pass the required Meta-verified beneficiary/payer entity IDs in regionalRegulationIdentities.

Set:
• regionalRegulatedCategories: [...]
• regionalRegulationIdentities: { universalbeneficiary: 123, universalpayer: 456, ... }

If omitted, Meta may use Ads Manager defaults when configured.
September 2, 2026New Feature
New endpoint GET /v1/analytics/delta returns a cursor-based feed of analytics snapshots that changed across all readable accounts.

This lets integrations stay in sync with far fewer calls than polling GET /v1/analytics per account; bootstrap once from GET /v1/analytics, then poll deltas.

Key params:
• cursor (opaque; omit to start “from now” and receive data: [] + nextCursor)
• limit (1–100, default 50; out-of-range = 400)
• platform (optional)
• profileId (default all)

Response fields:
• data (oldest-first changed snapshots)
• nextCursor (always present, even on empty pages)
• hasMore (if true, call again immediately)

New webhook event available for subscriptions: analytics.synced. It’s a trigger (no metrics/cursor); on receipt, call GET /v1/analytics/delta using your last stored nextCursor.
September 2, 2026Breaking Change
Instagram comment-to-DM private replies may now fail for non-followers when you include interactive elements.

Since late Aug 2026, Instagram can refuse buttons (and other attachments/cards) to commenters who don’t follow the account, returning a non-retryable 400 (Meta code 2, subcode 1545133). The failed call still consumes the comment’s single private reply.

If you need to reach non-followers, send plain text using message only, then add buttons after they reply. (quickReplies and buttons remain mutually exclusive.)
September 2, 2026Improvement
Meta ads now accept regional regulated categories on ad set creation/boosting.

Use this when targeting countries where Meta requires extra regulation flags (e.g. Singapore/Taiwan/Thailand/Australia/India). Without it, Meta may reject the ad set.

Send regionalRegulatedCategories (array of strings) on:
• POST /v1/ads/create
• POST /v1/ads/boost

Example values include SINGAPOREUNIVERSAL, TAIWANUNIVERSAL, THAILANDUNIVERSAL, AUSTRALIAFINSERV, INDIA_FINSERV.
September 1, 2026Improvement
Several Ads endpoints can now return 403 with code adsallowanceexceeded.

This happens when a team has no payment method on file and has reached the 500 free live ads limit; add a card to resume/continue.

You may see this on:
• PUT /v1/ads/campaigns/{campaignId}/status
• PUT /v1/ads/campaigns/{campaignId}
• POST /v1/ads/campaigns/bulk-status
• POST /v1/ads/campaigns/{campaignId}/duplicate
• POST /v1/ads/ad-sets/{adSetId}/duplicate
• POST /v1/ads/{adId}/duplicate
• PUT /v1/ads/ad-sets/{adSetId} and PUT /v1/ads/ad-sets/{adSetId}/status
• PUT /v1/ads/{adId} and PUT /v1/ads/{adId}/status
• PATCH /v1/ads/{adId}/tracking-tags
• POST /v1/ads/create
• POST /v1/ads/boost
• POST /v1/ads/messaging, POST /v1/ads/call, POST /v1/ads/ctwa
• POST /v1/ads/campaigns/{campaignId}/assets

If you have retry logic, treat 403 adsallowanceexceeded as non-retryable until billing is set up.
September 1, 2026Improvement
GET /v1/connect/pending-data pending OAuth tokens are now repeatable and expire 1 hour after issuance (previously one-time use, 10 minutes).

This makes headless OAuth selection flows more resilient (you can re-fetch the pending selection data until the user completes selection; completion deletes the pending record).

Use token (query) from pendingDataToken on the redirect. selectionType can be: organizations, pages, boards, locations, profiles.

Scope note: this pending-data flow now covers Google Business locations and Slack channels in addition to existing large selection lists.
September 1, 2026New Feature
GET /v1/ads now supports filtering by Meta ad set via adSetId.

This makes it easier to build adset-centric dashboards by listing only ads within a specific platform ad set.

Use adSetId (platform ad set id) alongside existing filters like campaignId.

PUT /v1/ads/{adId} now supports richer creative updates, especially for Meta.

Meta creative updates are now patch-style: send any subset of creative fields and omitted fields are preserved (including existing media), and you can also repoint an ad to an existing creative.

Key Meta-only options in creative:
• description (link description)
• videoId (reuse an already-uploaded video)
• existingCreativeId (repoint to a creative from GET /v1/ads/creatives; ignores other creative fields)

GET /v1/ads/targeting/search now supports Meta work demographics via dimension values workPosition, workEmployer, workIndustry.

These ids can be used in TargetingSpec as:
• workPositions
• workEmployers
• workIndustries

Also, TargetingSpec audience lists are now supported: audienceInclude and audienceExclude accept platform audience IDs (not supported on OpenAI Ads; returns 400).
August 31, 2026Breaking Change
WhatsApp template sends via POST /v1/inbox/conversations now validate templateParams more strictly for positional templates.

If you provide fewer values than the template’s required header + body + dynamic URL-button slots, the request is rejected with 400 (INVALIDTEMPLATEPARAMS) instead of sending a message with misaligned values.

Key fields:
• templateParams - must cover all required slots for positional templates
• templateButtonParams - can override a button’s value; overridden buttons may not need a value in templateParams unless later uncovered dynamic URL buttons follow
August 31, 2026Improvement
Creating or retrying posts can now return 403 with code=ACCOUNTNOTENABLEDFORPOSTING.

This happens when the target accountId is connected for ads only (i.e., enabled: false) and cannot be posted to. Connect the account as a posting account, refresh IDs via GET /v1/accounts, then retry.

Applies to:
• POST /v1/posts (403 code enum now includes ACCOUNTNOTENABLEDFORPOSTING)
• POST /v1/posts/{postId}/retry (403 may include ACCOUNTNOTENABLEDFORPOSTING, PROFILEOVERLIMIT, or insufficient_permissions)
August 31, 2026Breaking Change
Some endpoints now return additional billing/capability errors you may need to handle.

Posts listing may now fail with 403 when X analytics isn’t enabled for the account (code XANALYTICSNOT_ENABLED) on GET /v1/accounts/{accountId}/posts.

OpenAI Ads connect may now return 402 PaymentRequired on POST /v1/connect/openai-ads/credentials.

SMS sender ID creation now has explicit billing-related failures on POST /v1/sms/sender-ids:
• 402 (code payment_required) when no payment method is on file
• 409 (code billingsetupincomplete) when billing setup is incomplete
August 29, 2026Breaking Change
PostAnalytics.follows can now be null in GET /v1/analytics responses.

This affects Instagram where Meta doesn’t expose follows for Reels and non-Reels video; in those cases Zernio will return null instead of 0. Update your parsing/aggregation to handle follows: null (treat as “unknown/not available”, not zero).
August 28, 2026Breaking Change
WhatsApp templates now support working with a specific Meta template variant via its Meta template id.

New endpoints:
• GET /v1/whatsapp/templates/id/{templateId} (requires accountId)
• PATCH /v1/whatsapp/templates/id/{templateId} (body: accountId, components)
• DELETE /v1/whatsapp/templates/id/{templateId} (requires accountId)

Template listing and name-based operations are now language-aware (Meta stores templates per name + language):
• GET /v1/whatsapp/templates adds filters: name, language, status (APPROVED/REJECTED/PENDING/PAUSED/DISABLED/INAPPEAL/PENDINGDELETION)
• GET /v1/whatsapp/templates/{templateName} adds optional language; if multiple variants exist and no language is provided, it can return 409 ambiguous_template with details.languages
• PATCH /v1/whatsapp/templates/{templateName} now takes language in the request body (required when multiple variants exist); missing language can return 409 ambiguous_template

Delete behavior change:
• DELETE /v1/whatsapp/templates/{templateName} without language deletes all language variants; with language it deletes only that variant and returns scope = all_languages or language.
August 28, 2026Improvement
WhatsApp sends via POST /v1/inbox/conversations/{conversationId}/messages now document a per-recipient rate limit.

If you send bursts to the same recipient, WhatsApp may reject excess messages with Meta error code 131056 ("Too many messages sent to this recipient"). Pace sends to a single recipient to ~10/min; sends to other recipients are unaffected.

If you see 400 responses with platformError.code = 131056, throttle per recipient and retry later.
August 28, 2026New Feature
Phone number stock watches are now available via new endpoints: GET /v1/phone-numbers/stock-watches, POST /v1/phone-numbers/stock-watches, DELETE /v1/phone-numbers/stock-watches/{id}.

You can watch an out-of-stock country and get notified the first time deliverable numbers are available again (email + webhook). Watches are checked every 6h, consumed when they fire, and you can watch up to 20 countries.

Create a watch with country (ISO 3166-1 alpha-2). Responses: 201 created, 200 if it already existed, 409 if the country is currently in stock or the 20-country limit is reached.

Webhooks now support the new event phonenumber.stockavailable in events (for POST /v1/webhooks/settings and PUT /v1/webhooks/settings). Payload includes stock.country and stock.types[] with numberType (local/mobile/national/toll_free) and availableCount.
August 28, 2026New Feature
GET /v1/usage metering mode now supports spend attribution by profile/account, plus optional projection onto a single profile or account.

This lets you break down billed spend (USD) across profiles/accounts for a given window, or fetch the metering payload scoped to one group for chargeback/reporting.

New query params (metering mode):
• groupBy: profile | account (adds attribution)
• profileId: project results onto one profile (pair with range)
• accountId: project results onto one account (pair with range)

New response fields in UsageMetering:
• attribution (with groupBy, groups, unattributed, totals, restricted)
• scope (present with profileId/accountId)

Note: when profileId or accountId is set, peaks, callUsage, and tax return null (workspace-level only).
August 28, 2026New Feature
New endpoint: GET /v1/accounts/{accountId}/youtube-captions to fetch a YouTube video transcript (plain text plus timed cues), including auto-generated (asr) and uploaded (standard) tracks.

Supports cached reads to avoid repeated YouTube calls; response includes source = cache | youtube and fetchedAt. Use refresh=true only when captions changed (re-downloads from YouTube).

Key params:
• accountId (path)
• videoId (query, required)
• language (query, optional; BCP-47)
• format (query) = json | srt (default json)
• refresh (query, boolean)

Response highlights: trackKind = asr | standard, cues (when format=json), srt (when format=srt), and availableTracks for language selection.
August 27, 2026New Feature
New endpoint: DELETE /v1/ads/ad-sets/{adSetId} deletes an ad set on the platform and cancels its ads locally (it never deletes the campaign).

Provide the ad set ID via adSetId (path).

Response includes deleted and adCount (number of local Ad documents marked status: cancelled). Returns 501 if deletion isn’t supported on the target platform.
August 27, 2026New Feature
Instagram is now supported (limited release) for liking/unliking posts and comments via:
• POST /v1/inbox/posts/{postId}/like, DELETE /v1/inbox/posts/{postId}/like
• POST /v1/inbox/comments/{postId}/{commentId}/like, DELETE /v1/inbox/comments/{postId}/{commentId}/like

This enables engagement actions on Instagram feed media (posts/reels/carousels) and their comments/replies, but access is currently restricted by Meta permissions.

Instagram requirements/behavior:
• Requires permission instagrammanageengagement
• Only works for accounts connected via Facebook Login; Instagram Login returns 400 with code instagramlikesrequirefacebooklogin
• Accounts whose token predates the permission return 403 with code reconnect_required
• Limited release: non-allowed accounts return 403 with code PLATFORMBETARESTRICTED
• Burst limit: 50 like/unlike calls per 5s per Instagram account; exceeding can lock likes for 1 hour

Also note: these endpoints’ 403 may now indicate missing platform permission (not only Inbox add-on).
August 27, 2026Breaking Change
PlatformTarget.platformSpecificData is now returned as a free-form object (with additionalProperties: true) instead of a oneOf union of *PlatformData schemas.

This avoids generated clients failing to deserialize PlatformTarget responses when platformSpecificData doesn’t match exactly one variant. Treat platformSpecificData as an untyped JSON object on responses (it may be omitted rather than returned as an empty object).
August 26, 2026New Feature
WhatsApp inbox conversation creation now supports sending values for template buttons that carry their own payload (copy-code / flow tokens), via templateButtonParams on POST /v1/inbox/conversations.

Use this when your approved WhatsApp template has buttons that need a per-send value (e.g. Pix copy-and-paste code, coupon code, Flow token). This complements templateParams (text variables + dynamic URL buttons).

Set templateButtonParams as an array of:
• index (0-9)
• subType: url | copy_code | flow
• value

Message lineage is now exposed as sentVia on GET /v1/inbox/conversations/{conversationId}/messages and on the message.sent webhook, with values:
human | api | broadcast | sequence | workflow | comment_automation | bulk-api | null

Note: existing stored messages are not backfilled; treat sentVia: null as “unknown”.
August 26, 2026New Feature
DELETE /v1/ads/campaigns/{campaignId} now accepts platform=google.

You can delete Google campaigns using the same endpoint and request body shape as Facebook/Instagram.

Set platform to one of: facebook, instagram, google

For empty campaigns (0 ads), include accountId to allow deletion: accountId (Zernio SocialAccount id).
August 26, 2026Improvement
GET /v1/whatsapp/template-library now supports selecting a specific language variant via an optional language query param.

This lets you request a desired locale (e.g. es, en_US); if it’s not available, the API returns the first available variant and reports the actual variant in template.language.

New:
• Query param: language
• Response field: template.availableLanguages (array of supported language codes)
August 26, 2026Improvement
GET /v1/users now returns lastLoginAt for each user.

This lets you track recent workspace sign-ins (stamped at most once per hour). The field may be omitted for users with no recorded sign-in since it was introduced.

New field:
• users[].lastLoginAt

The message.sent webhook payload now supports more platforms and can include send context in a new metadata object.

Updated enum:
• message.platform: instagram, facebook, telegram, whatsapp, twitter, reddit, bluesky, slack

New optional fields:
• metadata.quotedMessageId
• metadata.threadTs (Slack only)
August 26, 2026New Feature
You can now fetch a single Google Business Profile review by ID via GET /v1/accounts/{accountId}/gmb-reviews/{reviewId}.

This returns one review in the same shape as the items from GET /v1/accounts/{accountId}/gmb-reviews, which is useful for checking the latest state before replying (replies are overwritten in place).

Key params:
• accountId (path)
• reviewId (path, ID portion only)
• locationId (query, optional override; otherwise uses the account’s selected location)

Response includes review (GoogleBusinessReview).
August 26, 2026New Feature
Instagram now supports publishing videos muted via muteAudio in platformSpecificData.

When muteAudio=true, we strip the audio track before sending the video to Instagram, so the published video is permanently silent.

Use:
• muteAudio (boolean, default false)

Applies to Reels, Stories, and video carousel slides; ignored for images. Videos above 200MB cannot be muted (mute before uploading).
August 26, 2026New Feature
Threads posts now support an optional first comment via platformSpecificData.threads.firstComment.

This lets you automatically publish a reply immediately after the main post is published; when using threadItems, it replies to the root post.

Key field:
• firstComment (string, max 500)

Note: the first comment is a Threads post and counts toward the profile’s 250 posts / 24h limit.
August 26, 2026New Feature
Pinterest posts now support targeting a board section and optionally disclosing AI-modified content via PinterestPlatformData.

Use boardSectionId to publish into a specific section within boardId (omit it to post to the board). Pinterest rejects the pin if the section doesn’t belong to the board, so send both together.

Set isAiGenerated to true to include Pinterest’s AI_MODIFIED disclosure (shows an “AI modified” label); false (default) omits the disclosure.
August 25, 2026New Feature
Google Search ads created via POST /v1/ads/create now support campaign-level sitelinks.

You can create and attach sitelink assets during ad creation (Google reviews them asynchronously; approval is not immediate).

Use sitelinks (Google Search only):
• 2–20 items
• each item: text (max 25) + linkUrl (URI)
• optional: description1, description2 (each max 35)

Applies when campaignType is search.
August 25, 2026New Feature
Meta ads now support the Page Likes objective via goal = page_likes.

This lets you create campaigns/ads optimized for Page Likes (Meta maps this to Engagement/Page Likes) using the same create flows.

Use goal with:
• POST /v1/ads/campaigns: goal now includes page_likes
• POST /v1/ads/create: goal now includes page_likes

Notes for page_likes (Meta): promotedObject.pageId is optional (auto-filled from the connected Page). Creative fields like headline, body, linkUrl, callToAction, imageUrl/video are optional (Meta derives the Like button + destination from the Page).
August 25, 2026Improvement
Inbox list endpoints now return which connected accounts were skipped during aggregation via meta.accountsSkipped.

This helps you distinguish between accounts that failed (meta.failedAccounts) vs accounts that were intentionally not queried because the platform/account doesn’t support the feature or isn’t enabled.

New response field:
• meta.accountsSkipped: array of { accountId, platform }

Applies to:
• GET /v1/inbox/conversations
• GET /v1/inbox/comments
• GET /v1/inbox/reviews
August 25, 2026Breaking Change
GET /v1/ads/tree now supports scoping the tree to multiple ad accounts via adAccountId.

You can pass a comma-separated list to limit results to a subset of connected accounts (useful for agency profiles).

Use adAccountId=act1,act2,act_3 (max 50 per request). Single values keep the old behavior.

Note: the plural aliases adAccountIds and platformAdAccountIds are now rejected with 400 (to avoid silently returning unfiltered results).
August 25, 2026Improvement
GET /v1/ads/videos now returns a playable video URL via source in the default response.

This lets you preview a video before selecting it (useful when multiple videos share the same first frame).

Key field:
• data[].source — signed CDN MP4 URL (expires; use for preview only and re-list to refresh)
August 25, 2026New Feature
WhatsApp interactive messages now support native address capture via interactive.type = address_message on POST /v1/inbox/conversations/{conversationId}/messages.

This lets you prompt users with Meta’s structured shipping-address form in-chat; submissions arrive as metadata.interactiveType = nfm_reply on the message.received webhook.

To send, set:
• interactive.type: address_message
• interactive.action.name: address_message
• interactive.action.parameters.country: ISO 3166-1 alpha-2 (required)

Webhook parsing update: WebhookPayloadMessage.metadata now includes nfmReplyName (e.g. flow vs address_message) to distinguish address submissions from Flow submissions; address fields are included in flowResponseData.
August 25, 2026New Feature
New endpoint GET /v1/ads/{adId}/media returns direct signed URLs for every image/video asset used by an ad’s live creative (normalized across single, carousel, Reels/Story, and dynamic creative).

Use it to fetch short-lived asset URLs on demand (don’t cache long-term; re-fetch before serving/downloading).

Key details:
• Path param: adId (Zernio 24-char hex or platform ad id)
• Response: platform (facebook | instagram), media[] items with type (image | video), url, and for videos: thumbnailUrl, videoId, length, index
• Errors: 422 (no active Meta connection), 501 (only supported on Meta)
August 25, 2026New Feature
CTWA ad creation now supports a custom chat welcome message via welcomeMessage.

This lets you override Meta’s default greeting and prefilled user text when the WhatsApp chat opens, so ads can steer the first message toward what they promote.

Set welcomeMessage (single-creative) or creatives[].welcomeMessage (multi-creative) with:
• text
• prefillText
August 24, 2026Improvement
GET /v1/inbox/comments and GET /v1/inbox/comments/{postId} responses are now cached for up to 10 minutes.

This means comment counts/threads may lag new comments within that window; for real-time updates, use the comment.received webhook instead of polling.

Notes:
• Cache window: up to 10 minutes
• Recommended real-time mechanism: comment.received
• On GET /v1/inbox/comments/{postId}, your own writes (create/reply/delete) refresh the cache immediately.
August 24, 2026Breaking Change
Meta ad videos can now be managed directly via the API: upload standalone videos and delete them from an ad account’s video library.

Use POST /v1/ads/videos to upload before an ad exists (for reuse in POST /v1/ads/create via video.id or in POST /v1/ads/preview).

Upload params:
• accountId, adAccountId
• Exactly one of videoUrl (https) or videoBase64
• Optional filename (only for videoBase64)
Returns video.id and optional video.thumbnailUrl.

Use DELETE /v1/ads/videos/{videoId} with accountId + adAccountId to remove a video; returns { success: true } on success.

Breaking change (CTWA): when using multi-creative creatives[], setting any top-level headline/body/imageUrl/video now returns 400 (previously those fields could be silently ignored).

Also added for sorting: createdTime on AdTreeCampaign and AdTreeAdSet (proxy based on earliest ad creation time).
August 24, 2026Improvement
CTWA ad creation request (CtwaAdRequestBody) now supports reusing existing videos and more control over activation.

You can now pass video.id to reuse a video already uploaded to the ad account (instead of uploading via video.url). video.thumbnailUrl is now optional (Meta may auto-pick a thumbnail).

Video fields:
• video.url or video.id (either; id wins)
• video.thumbnailUrl (optional)

New status controls:
• status: ACTIVE | PAUSED (ad-level; default ACTIVE)
• campaignStatus: ACTIVE | PAUSED (campaign-level; default ACTIVE; rejected when using adSetId)
August 24, 2026Breaking Change
Connecting WhatsApp via POST /v1/connect/whatsapp/credentials now explicitly subscribes your Meta app to the WABA with a Zernio override callback, cutting over webhook delivery immediately (no overlap). Unsubscribing the app from the WABA afterward will also stop Zernio delivery and requires calling this endpoint again.

Response updates:
• webhookNotice may be returned on success with cutover/unsubscribe guidance
• account.phoneNumber replaces account.selectedPhoneNumber
• account.verifiedName and account.qualityRating are now included when available
August 24, 2026New Feature
WhatsApp Flows now let you set a data exchange endpoint via endpointUri when creating or updating a flow.

This enables configuring the HTTPS callback endpoint directly on DRAFT flows (and it will be used only if your uploaded Flow JSON declares dataapiversion "3.0").

New field:
• POST /v1/whatsapp/flows: endpointUri
• PATCH /v1/whatsapp/flows/{flowId}: endpointUri

Notes:
• endpointUri is uri format and HTTPS-only
• Settable only while the flow is in DRAFT
August 24, 2026Improvement
LinkedIn reshare behavior is now explicitly defined for platformSpecificData.reshareUrl on POST /v1/posts.

When you set reshareUrl, the published post is always a reshare authored by your account with the original embedded underneath: with content it’s a quote-style reshare, and with no content it’s a text-free reshare.

Note: a text-free reshare is not LinkedIn’s one-click “Repost” feed treatment (LinkedIn’s API doesn’t support creating that), so it will still appear authored by you.

Use: platformSpecificData.reshareUrl (mutually exclusive with media).
August 24, 2026New Feature
SIP trunking is now available for Voice: you can create SIP trunks and attach Zernio numbers to route inbound/outbound calls through an external SIP platform.

New endpoints:
• GET /v1/phone-numbers/sip-trunks (returns trunks, plus enabled)
• POST /v1/phone-numbers/sip-trunks (create; returns digestPassword only once)
• GET /v1/phone-numbers/sip-trunks/{id}
• DELETE /v1/phone-numbers/sip-trunks/{id}
• POST /v1/phone-numbers/sip-trunks/{id}/rotate-credentials (returns new digestPassword only once)
• POST /v1/phone-numbers/{id}/sip-trunk (attach with trunkId)
• DELETE /v1/phone-numbers/{id}/sip-trunk (detach)

Create trunk request fields: label, sipHost, optional sipPort, transport (tls/tcp/udp).

Phone number responses now include sipTrunkId (null when not trunked) on GET /v1/phone-numbers and GET /v1/phone-numbers/{id} (and the deprecated WhatsApp aliases).

Behavior change: when a number is attached to a trunk, these endpoints can return 409 (invalidresourcestate) and require detaching first:
• DELETE /v1/phone-numbers/{id}
• POST /v1/phone-numbers/{id}/voice
• POST /v1/phone-numbers/{id}/whatsapp/calling
August 22, 2026New Feature
LinkedIn company_list audiences can now be updated after creation via POST /v1/ads/audiences/{audienceId}/companies.

This uploads a new full company list and replaces the existing list (not appended/merged). Matching is asynchronous and the audience stays processing while LinkedIn processes the update.

Send:
• audienceId (path)
• companies (array, 1..300000) — each row must include at least one of name, domain, website, linkedinPageUrl

Notes:
• company_list is filled at creation from companies on POST /v1/ads/audiences; use the new endpoint for later changes
• POST /v1/ads/audiences/{audienceId}/users is customerlist-only and returns 422 for companylist (use /companies instead)
August 22, 2026New Feature
New endpoint GET /v1/ads/videos is available.

It lists a Meta ad account’s existing ad video library so you can reuse an already-uploaded video when creating ads (avoid duplicate uploads and share one upload across multiple ads).

Call with:
• accountId (required)
• adAccountId (required, act_<n>)
• fields (optional Graph field override)
• limit (optional, default 25, max 100)
• after (optional cursor)

Use returned data[].id as video.id on POST /v1/ads/create. A video is only usable once status.video_status is ready.

Note: this endpoint is Meta-only; other platforms return 501.
August 22, 2026Improvement
POST /v1/inbox/conversations now returns more specific Reddit error details.

This improves client-side handling when creating a Reddit DM thread fails (missing user, DM not allowed, or rate limiting).

Key response changes:
• 404 now includes code: PARTICIPANTNOTFOUND when the u/username doesn’t exist
• 422 now also applies to Reddit when the recipient doesn’t accept private messages (still code: DMNOTALLOWED)
• 429 may now be returned for Reddit rate limits with code: rate_limited
August 22, 2026New Feature
POST /v1/posts/{postId}/edit now supports editing published posts on more platforms and can target a specific account’s copy of a post.

You can now edit on platform: twitter, discord, facebook, reddit, linkedin, telegram, pinterest, googlebusiness, youtube, slack.

When a post was published to multiple accounts on the same platform, pass accountId to choose which one to edit (defaults to the first).

Request body: platform, content, optional accountId.
August 22, 2026Improvement
External Instagram posts and analytics responses now include additional Instagram-specific metadata fields.

This helps you distinguish content types like Reels vs Feed, detect AI-generated labels, and see whether a Reel was shared to the main feed.

New optional fields:
• mediaProductType (Instagram only; e.g. FEED, REELS, STORY, AD)
• isAiGenerated (Instagram only)
• isSharedToFeed (Instagram Reels only)
• mediaAudioType (Instagram only; MUSIC or ORIGINAL_SOUND)

Also, GET /v1/whatsapp/account-events events now include accountId in each event item.
August 21, 2026Improvement
Phone number listings now include WhatsApp Business Calling status via callingEnabled.

This lets you detect whether Calling is enabled per number when listing purchased numbers and connected (bring-your-own) numbers.

New fields:
• GET /v1/phone-numbers
- numbers[].callingEnabled
- connected[].callingEnabled
• GET /v1/whatsapp/phone-numbers (deprecated alias)
- numbers[].callingEnabled
- connected[].callingEnabled
August 21, 2026New Feature
New endpoint: GET /v1/whatsapp/account-events.

You can now poll a WhatsApp account’s Meta-originated event history (newest first), including template review outcomes and WABA status changes. This complements webhook push events with a queryable feed.

Required query: accountId
Optional query: limit (1–200, default 50)

Response: events[] with id, type (e.g. templateapproved, templaterejected, accountrestricted, accountdisconnected), severity (info/success/warning/critical), title, detail, createdAt.
August 21, 2026New Feature
New endpoint: POST /v1/connect/shopify/token to connect a Shopify store using a merchant-created custom-app Admin token (token-paste alternative to OAuth).

Use this when OAuth connect isn’t available or when users prefer not to install a third-party app; the token is validated against the store before saving and reconnecting replaces the stored token.

Request body:
• profileId
• shop (e.g. your-store.myshopify.com)
• accessToken (starts with shpat_)

Response includes account with platform = shopify and username = store domain.
August 20, 2026Improvement
WhatsApp connect endpoints now return a clearer 409 conflict when the selected number is already connected on another profile/workspace.

This lets you distinguish between “number pinned to another profile” vs “number already connected elsewhere” and handle the conflict programmatically.

On POST /v1/connect/whatsapp/credentials and POST /v1/connect/whatsapp/select-phone-number, 409 may include:
• WHATSAPPNUMBERPINNEDTOPROFILE — move it first with PATCH /v1/whatsapp/phone-numbers/{id}/profile
• WHATSAPPNUMBERALREADY_CONNECTED — disconnect it from the other profile/workspace first (a number can only be live on one profile)
August 20, 2026Improvement
mediaStatus on external/synced post media items can now be explicitly returned as available.

This makes media availability explicit instead of relying only on the field being absent, while keeping backward compatibility (older synced items may still omit mediaStatus).

Updated enums:
• ExternalPostMediaItem.mediaStatus: available | unavailable
• AnalyticsSinglePostResponse.mediaItems[].mediaStatus: available | unavailable

Notes: unavailable indicates the media file couldn’t be retrieved (often url is null, or LinkedIn video may return a cover image), and unavailableReason may be platform_withheld.
August 20, 2026Improvement
In post.external.* webhooks, ExternalPostMediaItem now clarifies LinkedIn video behavior: when LinkedIn returns no video file, url falls back to the cover image and the item includes mediaStatus.

This lets you distinguish a real downloadable media file from a thumbnail/cover-image fallback.

Check:
• url (may be null, or for LinkedIn videos may be a cover image)
• mediaStatus: unavailable (present only when the media file could not be retrieved)
• unavailableReason: platform_withheld
August 19, 2026New Feature
X (Twitter) posts now support long-form X Articles via platformSpecificData.article in POST /v1/posts.

This lets you create and publish an X Article (or save it as a draft) from the same post creation endpoint. When posting only Articles, content can be omitted.

Use platforms[].platformSpecificData.article with:
• title
• content_state
• mode: publish | draft
• optional cover.url (+ cover.altText)

Billing/pricing: GET /v1/billing/x-pricing now includes articledraft and articlepublish (both in tier xapi010, $0.010/call).
August 19, 2026New Feature
POST /v1/ads/boost now accepts a Meta-only CTA value VIEWINSTAGRAMPROFILE in callToAction.

This lets you boost a post with a CTA that drives clicks to an Instagram profile (typically paired with the profile URL).

Use:
• callToAction: VIEWINSTAGRAMPROFILE
• linkUrl: (Instagram profile URL)

Applies to Meta boosts only; other platforms will reject Meta-only options.
August 18, 2026Improvement
PlatformTarget.errorCategory now includes more granular failure types: platformratelimit and quota_exhausted.

Use these to distinguish platform throttling vs. shared daily API quota exhaustion and adjust retry/backoff or alerting logic accordingly.

Updated errorCategory values now include: authexpired, usercontent, userabuse, accountissue, platformrejected, platformerror, platformratelimit, quotaexhausted, systemerror, unknown.
August 18, 2026Improvement
WhatsApp messages sent via the inbox endpoints can now disable link previews using linkPreview.

This lets you send WhatsApp text without the URL thumbnail/preview (useful for cleaner utility messages or when previews are undesirable).

Set linkPreview (boolean, default true):
• false - send without link preview
• true - send with link preview

Available on:
• POST /v1/inbox/conversations (WhatsApp Direct Send with category = utility)
• POST /v1/inbox/conversations/{conversationId}/messages (WhatsApp; ignored on other platforms)
August 18, 2026Improvement
Comment webhooks now include comment.author.isOwnAccount in WebhookPayloadComment.

This lets you detect when a comment.received event was authored by the connected account itself (Meta can re-deliver the account’s own replies as comment events), so you can filter or route them differently.

New field:
• comment.author.isOwnAccount (boolean) — populated on Instagram/Facebook realtime webhooks only; if absent, it was not evaluated (don’t treat missing as false).
August 18, 2026Improvement
Webhook message.received payload now may include metadata.noRenderableContent (Instagram / Facebook Messenger only).

This flag indicates the inbound message contains nothing you can render (e.g., a template attachment with no text/parseable content, or Meta’s is_unsupported flag). Use it to suppress/handle “empty” messages in your UI.

New field: metadata.noRenderableContent (boolean)
August 17, 2026New Feature
Bluesky now supports default post languages at the account level, and per-post language tagging for feed-generator filtering.

Use the new endpoints to get/set an account default that applies at publish time when a post omits platformSpecificData.langs:
• GET /v1/accounts/{accountId}/bluesky-settings → returns defaultLangs
• PATCH /v1/accounts/{accountId}/bluesky-settings with defaultLangs = 1–3 BCP-47 codes (or null to clear)

On POST /v1/posts, Bluesky posts now accept platformSpecificData.langs (1–3 BCP-47 codes, e.g. pt, en-US). Per-post langs overrides the account default; if neither is set, the field is omitted. When using threadItems, the same langs applies to every item in the thread.
August 17, 2026Improvement
Meta ad creation now supports setting campaign status independently via campaignStatus on POST /v1/ads/create.

This lets you create a campaign ACTIVE while keeping the ad set/ad PAUSED (or the reverse), instead of using a single status value for all levels.

Set campaignStatus to ACTIVE or PAUSED (Meta only). If omitted, it follows status.

Ads now also return Meta’s creative id as ad.creative.creativeId, which you can reuse via existingCreativeId on POST /v1/ads/create.
August 17, 2026Improvement
Campaign/ad set status toggles now return clearer, more explicit results and always confirm what switch was written.

For PUT /v1/ads/campaigns/{campaignId}/status, the API writes the campaign on/off switch and returns the confirmed status (active | paused). updated/skipped now describe only ads whose stored status changed alongside the campaign switch, so updated: 0 can be a normal successful resume.

Response additions:
• status (active | paused)
• skippedReasons (why ads were skipped)

For ad sets (PUT /v1/ads/ad-sets/{adSetId} and PUT /v1/ads/ad-sets/{adSetId}/status), status toggling is now defined as writing the ad set’s own switch where supported (Meta/LinkedIn). Where a platform has no ad-set switch, the API may return no status and instead return a message when nothing was written.

Response additions/changes:
• status may be absent (see statusMessage/message)
• statusSkippedReasons / skippedReasons
• statusMessage / message

Also clarified: on Meta, resuming a campaign doesn’t automatically unpause ad sets paused independently—pair with PUT /v1/ads/ad-sets/{adSetId}/status when needed.
August 15, 2026New Feature
GET /v1/accounts/{accountId}/health now includes platformConnection for WhatsApp accounts.

This adds a live probe of the Meta link behind the channel (same read as GET /v1/whatsapp/number-info), so WhatsApp liveness can be detected even when tokenStatus is valid. When the Meta link is dead, platformConnection.status is disconnected and overall status is error.

New WhatsApp-only fields:
• platformConnection.status: connected | disconnected | unknown
• platformConnection.checkedAt
• platformConnection.phoneStatus
• platformConnection.metaError (when disconnected: code, subcode, message)
August 14, 2026New Feature
Webhooks now support the new WhatsApp event template category updates via whatsapp.template.category_updated.

This fires when Meta reclassifies a WhatsApp template’s category on a connected WABA, so you can react to upcoming (scheduled) and applied (applied) changes.

Subscribe by adding whatsapp.template.category_updated to events when calling POST /v1/webhooks/settings or PUT /v1/webhooks/settings.

Payload highlights:
• event: whatsapp.template.category_updated
• template.changeType: scheduled | applied
• template.category: UTILITY | MARKETING | AUTHENTICATION
• template.previousCategory (only when applied)
• template.scheduledCategory, template.effectiveAt (only when scheduled)
August 14, 2026Breaking Change
Meta ad creation changed how the default Instagram identity is chosen when instagramAccountId is omitted on POST /v1/ads/create.

If you don’t pass instagramAccountId, we now use the Instagram actor Meta already runs the Page’s other ads as, falling back to the Page’s page-backed Instagram account.

To force a specific IG identity, set instagramAccountId to the desired Instagram Business Account ID (e.g. 17841...).
August 14, 2026Breaking Change
Meta conversion setup on POST /v1/ads/create is now stricter/clearer around promotedObject.

Meta rejects promotedObject.pixelId unless promotedObject.customEventType is also provided (even if promotedObject.customConversionId is set). If you want to optimize for a Custom Conversion without sending pixel/event, you can now send promotedObject.customConversionId alone.

Key fields:
• promotedObject.pixelId + promotedObject.customEventType (required together for Meta pixel-based conversion optimization)
• promotedObject.customConversionId (can be sent alone; if pixelId is also sent, customEventType is still required)
• For pixel custom events: promotedObject.customEventType = OTHER + promotedObject.customEventStr
August 14, 2026Breaking Change
SMS is now supported in inbox webhooks, and SMS sends now explicitly fail for opted-out recipients.

Inbound SMS replies arrive via message.received with message.platform = sms, and outgoing SMS outcomes arrive via message.delivered / message.failed (carrier error details included on failure).

When sending via POST /v1/sms/messages:
• Opted-out recipients (replied STOP) are refused with 409
• 409 can also mean the same Idempotency-Key is still in flight

Webhook platform enum now includes sms (alongside instagram, facebook, telegram, whatsapp).
August 14, 2026Improvement
KYC address payloads now accept an optional extended_address field on:
• POST /v1/phone-numbers/kyc
• POST /v1/phone-numbers/kyc/validate-address
(and the deprecated /v1/whatsapp/... aliases).

Use extendedaddress for address complements like apartment/suite/unit (or quadra/lote in some countries). This can help pass address validation without changing streetaddress.

Set address.extendedaddress (KYC submit) or top-level extendedaddress (validate-address).
August 14, 2026New Feature
Webhooks now support referral click events via the new referral.received event.

This lets you capture Meta referral payloads even when a click opens an existing thread without generating a message (previously you could only see referrals on message.received when Meta attached them).

To subscribe, include referral.received in events when creating/updating a webhook:
• POST /v1/webhooks/settings events
• PUT /v1/webhooks/settings events

The webhook payload is WebhookPayloadReferral with:
• event = referral.received
• referral (Meta object forwarded verbatim; may include ref, source, type, refereruri, adid, adscontextdata)
• sender, conversation, account, timestamp
August 14, 2026New Feature
You can now attach a Meta multi-language ad when adding an ad to an existing ad set via POST /v1/ads/create.

In adSetId (attach) mode, you can provide a per-locale asset feed so one ad carries localized copy (and optional localized media) while keeping social proof on a single post.

Use:
• adSetId (attach mode)
• defaultLocale (e.g. en, pt_BR)
• translations[] with required fields: locale, headline, body, description (optional per-locale: linkUrl, imageUrl, videoUrl, thumbnailUrl)

Note: translations is mutually exclusive with dynamicCreative, placementAssets, carouselCards, existingCreativeId and creatives[].
August 13, 2026New Feature
POST /v1/posts now supports LinkedIn plain reposts (no commentary).

If every platform entry is a LinkedIn repost using platformSpecificData.reshareUrl, you can omit top-level content (and omit customContent) to publish LinkedIn’s one-click “Repost” with no text.

Use platformSpecificData.reshareUrl (LinkedIn post URL or urn:li:share/urn:li:ugcPost/urn:li:groupPost). If you include content, it becomes a quote-reshare (repost with your thoughts).
August 13, 2026Improvement
Workflow nodes now support an optional display label via WorkflowNode.label.

This lets you set a human-friendly name shown in the builder canvas/inspector (falls back to the node type when omitted). If you update a workflow, remember the nodes array is replaced wholesale—so resend label to keep it.

Use label (string, 1–80 chars) on WorkflowNode.
August 13, 2026Improvement
GET /v1/ads/targeting/search expands geo targeting with a new geoType option all and additional geo types.

You can now search across multiple geo levels in one call (relevance-ranked) instead of picking a single level.

Updated geoType enum:
• all, country, region, city, subcity, neighborhood, place, zip, metroarea, geomarket

Pinterest notes: city is treated as an alias for all (Pinterest has no city-level catalog), and some geo types (e.g. zip) return 400 because Pinterest doesn’t expose those catalogs.
August 13, 2026Improvement
GET /v1/inbox/comments/{postId} now supports passing a Facebook/Instagram comment ID as postId to fetch that comment’s replies (instead of the post’s top-level comments).

This lets you page deeper reply threads without a separate endpoint; on YouTube, postId must still be a video id.

Use:
• GET /v1/inbox/comments/{postId}?accountId=... where {postId} can be a post ID or (FB/IG only) a comment ID
• New response signal (Facebook only): repliesHasMore (when true, fetch remaining replies by calling the same endpoint with that comment’s id as postId)

Also updated for GET /v1/inbox/comments pagination:
• nextCursor pagination can continue past the first page on facebook, instagram, threads, linkedin, youtube
• Cursor paging is only coherent for sortBy=date + sortOrder=desc; other sort combinations can make page 2 unreliable
• Treat nextCursor as opaque and dedupe results by id
August 13, 2026New Feature
Shopify is now supported as a connect-only platform, with a new Blogs API to manage store blogs and articles.

Start the OAuth flow with GET /v1/connect/shopify using profileId, shop (e.g. your-store.myshopify.com), and optional redirect_url. This creates an account on the profile with platform shopify.

Manage blogs on a connected Shopify account (accountId):
• GET /v1/accounts/{accountId}/blogs (limit 1-50, cursor)
• POST /v1/accounts/{accountId}/blogs (title, optional handle)
• GET/PATCH/DELETE /v1/accounts/{accountId}/blogs/{blogId} (blogId is platform-native numeric)

Manage articles within a blog:
• GET /v1/accounts/{accountId}/blogs/{blogId}/articles (limit 1-50, cursor)
• POST /v1/accounts/{accountId}/blogs/{blogId}/articles (supports bodyHtml, tags, image.url, seo.title/seo.description, isPublished, publishDate)
• GET/PATCH/DELETE /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} (articleId is platform-native numeric)

Notes: a future publishDate schedules publication natively on Shopify; DELETE on a blog deletes the blog and all its articles permanently.

Also added: GET /v1/connect/{platform} now accepts WhatsApp-only onboarding = api | business_app.
August 12, 2026Improvement
Campaign listing and tree endpoints now support filtering by delivery/spend within a date window.

On GET /v1/ads/campaigns, you can now return only campaigns that actually delivered in fromDate–toDate, or that reached a spend threshold.

New query params:
• hasDelivery (boolean) — only campaigns with delivery in the window (spend > 0 or impressions at zero spend)
• minSpend (number, min 0) — only campaigns whose spend in the window reaches this amount (in the campaign’s own currency); implies hasDelivery

On GET /v1/ads/tree, the same filters were added, and fromDate is clarified: it affects metrics only unless you also pass hasDelivery or minSpend. Pagination totals reflect the filtered campaign set.
August 12, 2026New Feature
Google Local Services Ads lead insights are now available via two new endpoints.

You can fetch LSA leads live from Google (newest first) and then pull the conversation history (calls/messages) for a specific lead. Note: data is not persisted (Google is the source of truth), and requests draw from the shared Google Ads operations budget.

Get leads with GET /v1/ads/local-services/leads
Required: accountId
Optional filters: customerId, fromDate, toDate, leadType (PHONECALL/MESSAGE/BOOKING), leadStatus (e.g. NEW, BOOKED, WIPEDOUT), chargedOnly, pageToken
Key fields: id, contact (null when WIPED_OUT), charged, creditState, createdTime

Get lead conversations with GET /v1/ads/local-services/leads/{leadId}/conversations
Params: leadId, accountId (+ optional customerId, pageToken)
Conversation fields include channel (PHONECALL/MESSAGE/SMS/EMAIL/WHATSAPP/ADSAPI), participantType (ADVERTISER/CONSUMER), plus phoneCall.callRecordingUrl / message.attachmentUrls when applicable.
August 12, 2026New Feature
TikTok ads now populate engagement breakdown in AdEngagementCounts (previously Meta-only).

You can now read paid engagement interactions for TikTok directly from engagementBreakdown (while TikTok follows remain in actions).

Key fields:
• AdMetrics.engagementBreakdown.reactions (TikTok paid likes)
• AdMetrics.engagementBreakdown.comments
• AdMetrics.engagementBreakdown.shares
• AdMetrics.actions.follow (TikTok followers objective result)

Other platforms are unchanged (still 0 where not supported).
August 12, 2026New Feature
POST /v1/ads/create now supports TikTok Smart+ campaigns via smartPlus.

When smartPlus: true, Zernio creates an Upgraded Smart+ campaign where TikTok automates targeting/bidding/delivery (within your provided geo targeting).

Key fields:
• Enable: smartPlus (boolean)
• Supported goal: conversions, leadgeneration, apppromotion
• Web Smart+ requires promotedObject.pixelId + promotedObject.customEventType
• App Smart+ requires promotedObject.applicationId
• Budget is campaign-level; budgetType=lifetime requires endDate
• Cannot be combined with adSetId
August 12, 2026New Feature
LinkedIn posts now support polls via poll in LinkedInPlatformData.

Use this to publish a LinkedIn poll with your post. Polls cannot be combined with media or reshareUrl, cannot be edited after publishing, and API-created polls are non-sponsored only.

Set poll with:
• question (1-140 chars)
• options (2-4 items, 1-30 chars each)
• duration (optional): ONEDAY | THREEDAYS | SEVENDAYS | FOURTEENDAYS (default SEVEN_DAYS)
August 12, 2026Improvement
Ad analytics now include additional Meta click + cost breakdown fields in AdMetrics.

This adds per-action cost metrics and separates different click definitions (attributed vs in-session vs outbound), making it easier to build accurate dashboards and optimize campaigns.

New fields:
• costPerAction (object) — derived spend / actions[type] per action type
• outboundClicks, outboundClicksCtr
• inlineLinkClicks, inlineLinkClickCtr
• uniqueClicks, uniqueCtr

Note: these click fields are Meta-only; other platforms return 0 (and costPerAction may be {} when not applicable).
August 11, 2026New Feature
Google Ads now supports keyword edits on existing ads via PUT /v1/ads/{adId}.

You can update Google Search ad group keywords by sending targeting.keywords and/or targeting.negativeKeywords. Each list you send becomes the FULL new set of that kind (items not included are removed); omit a kind to leave it unchanged.

Use:
• targeting.keywords (Google only) — items are "string" (defaults to broad) or { text, matchType } where matchType is exact | phrase | broad
• targeting.negativeKeywords (Google only) — same shape as above

Also added for Google Search creation via POST /v1/ads/create:
• negativeKeywords — broad-match negative keywords on the new ad group (editable later via PUT /v1/ads/{adId} targeting.negativeKeywords).
August 11, 2026Improvement
TikTok ad reporting now populates additional video metrics in AdMetrics (previously documented as Meta-only).

This lets you use the same video KPI fields across Meta and TikTok when reading ad/campaign metrics.

TikTok sources now apply to:
• videoPlayActions
• videoP25WatchedActions
• videoP50WatchedActions
• videoP75WatchedActions
• videoP100WatchedActions
• videoAvgTimeWatchedActions

Meta-only remains:
• video30SecWatchedActions
• videoThruplayWatchedActions
• videoP95WatchedActions
• costPerThruplay
August 10, 2026New Feature
Instagram now supports attaching catalog audio to Reels via platformSpecificData.audioConfiguration.

You can search/trend audio with GET /v1/accounts/{accountId}/instagram/audio (omit q for trending) and fetch metadata with GET /v1/accounts/{accountId}/instagram/audio/{audioId}. Use the returned audioId when creating a Reel.

Search params:
• audioType: music | original_sound
• q: optional keywords (omit for trending)

To publish with audio, set:
• platformSpecificData.audioConfiguration.audioId (required)
• platformSpecificData.audioConfiguration.audioVolume (0-100)
• platformSpecificData.audioConfiguration.videoVolume (0-100)

Requires an Instagram account connected via Facebook Login. Classic Instagram Login accounts will get a 400 instagramaudiorequiresfacebooklogin and must be reconnected using the Facebook option.
August 10, 2026Improvement
GET /v1/broadcasts/{broadcastId}/recipients now returns errorTraceId for failed recipients.

Use errorTraceId (Meta fbtrace_id) to correlate a failed send and quote it when escalating to Meta Direct Support. It’s only populated when status is failed (Meta platforms).

New field: errorTraceId (string | null)
August 10, 2026Improvement
GET /v1/accounts/{accountId}/instagram/stories/{storyId}/insights can now return 502 when Instagram rejects the upstream request.

This makes upstream failures explicit so clients can distinguish Instagram-side rejection from successful-but-empty expired story results.

Handle these responses:
• 200 with data.source = live | cached | unavailable
• 502 - Instagram rejected the request
August 10, 2026New Feature
Meta ads now support scheduled budget increases via POST /v1/ads/high-demand-periods.

This lets you pre-schedule temporary budget bumps (e.g. Black Friday) instead of editing budgets manually on the day.

Send:
• accountId
• exactly one of campaignId / adSetId
• budgetValueType: ABSOLUTE | MULTIPLIER
• budgetValue
• timeStart, timeEnd (Unix seconds, 15-min boundaries)
• optional recurrenceType: ONE_TIME | WEEKLY | MONTHLY

Meta custom conversions are now manageable via the API:

• List: GET /v1/accounts/{accountId}/custom-conversions (requires adAccountId)
• Create or reuse: POST /v1/accounts/{accountId}/custom-conversions

Creating supports reuse (to avoid duplicates) by matching name + pixelId; reused conversions return 200 with reused: true, otherwise 201. The response includes customConversionId for promotedObject.customConversionId on POST /v1/ads/create.

Meta creatives now accept multiAdvertiser: OPTIN | OPTOUT on:
• POST /v1/ads/creatives
• POST /v1/ads/create

Use OPT_OUT to disable Meta’s multi-advertiser ads behavior (Meta auto-enrolls by default).
August 10, 2026Breaking Change
POST /v1/media/presign no longer accepts the permanent request field.

If you were sending permanent: true to write uploads to non-expiring storage, remove this parameter from your request payload. Uploads will now follow the default behavior described by the endpoint.

Removed parameter: permanent (boolean)
August 10, 2026New Feature
POST /v1/media/presign now supports permanent uploads.

By default, presigned uploads go to temporary storage and auto-delete 7 days after upload (so the publicUrl stops working after that). Set permanent: true to write to permanent storage with no expiry—useful for media that must remain reachable longer (e.g., covers/thumbnails for posts scheduled >7 days out).

New request field: permanent (boolean, default false)

When permanent is true, the response publicUrl and key use the media/ prefix instead of temp/.
August 9, 2026New Feature
You can now like/unlike posts via the Inbox API.

This enables executive engagement (like/react as any connected accountId on a brand post). postId accepts a Zernio post ID or the platform native post ID.

New endpoints:
• POST /v1/inbox/posts/{postId}/like with accountId (+ optional reactionType for LinkedIn: LIKE, PRAISE, EMPATHY, INTEREST, APPRECIATION, ENTERTAINMENT; optional cid for Bluesky)
• DELETE /v1/inbox/posts/{postId}/like with accountId (+ likeUri required for Bluesky)

Also updated: comment like/unlike now supports LinkedIn on POST /v1/inbox/comments/{postId}/{commentId}/like and DELETE /v1/inbox/comments/{postId}/{commentId}/like. For LinkedIn likes, pass the composite comment URN as commentId and you can set reactionType (same enum as above).
August 9, 2026New Feature
Workflow AI nodes now support openrouter as an ai.provider value.

This lets you route AI steps through OpenRouter using your BYOK setup (set provider + model).

Set ai.provider to one of: anthropic, openai, google, mistral, groq, openrouter.
August 9, 2026New Feature
Inbox messages now support refreshable attachment URLs for Instagram/Facebook DMs.

Meta DM media URLs expire; attachments now include a stable refreshUrl you can store and call later to get a working media URL.

New endpoint: GET /v1/inbox/conversations/{conversationId}/messages/{messageId}/attachments/{index}
Key params: accountId (required), format (redirect | json, default redirect)

Response behavior:
• Default format=redirect returns 302 to the live media URL
• format=json returns { url, refreshed }

Also added on GET /v1/inbox/conversations/{conversationId}/messages attachments:
• attachments[].refreshUrl (Instagram/Facebook only)
• attachments[].url is now documented as expiring on Instagram/Facebook (use immediately; don’t store).
August 9, 2026New Feature
Comment-to-DM automations can now also trigger from inbound DMs via alsoMatchInDms.

Set alsoMatchInDms: true on a trigger=comment automation to answer users who DM the keyword instead of commenting it. Deduplication is per source (comment vs DM).

Key details:
• alsoMatchInDms (boolean, default false) on POST /v1/comment-automations and PATCH /v1/comment-automations/{automationId}
• Requires at least one keywords entry (empty keyword lists are rejected when enabling)
• Not allowed for trigger=story_reply

Responses now include:
• alsoMatchInDms on automation objects (list/get/create/update)
• source on log entries: comment | story_reply | dm
August 9, 2026New Feature
Instagram connect now supports headless mode for loginMethod=facebook_login on GET /v1/connect/instagram.

This lets you run the Facebook Login-based Instagram flow with your own UI: after OAuth, the callback redirects to your redirect_url with selection-step params, and you finish by calling GET /v1/connect/instagram/select-account.

Use:
• GET /v1/connect/instagram?loginMethod=facebook_login&headless=true
Callback includes: platform=instagram, step=selectaccount, profileId, tempToken, connecttoken
August 7, 2026Breaking Change
WhatsApp connect now rejects Zernio-provisioned numbers that are pinned to a different profile with a 409 conflict.

This prevents accidentally connecting a provisioned WhatsApp number from the wrong profileId. If you need to connect it on another profile, move the number first, then retry the connect.

New 409 responses:
• GET /v1/connect/whatsapp (when profileId doesn’t match the provisioned number’s profile)
• POST /v1/connect/whatsapp/credentials
• POST /v1/connect/whatsapp/select-phone-number

Remediation:
• Move the number with PATCH /v1/whatsapp/phone-numbers/{id}/profile, then connect again.
August 7, 2026Improvement
Google Business location selection/updates now validate the provided Google account resource name and will reject mismatches.

If you pass an account identifier to speed up location resolution for large GBP accounts, the API will now return 400 when the provided account is not one of the accounts the connection manages.

Applies to:
• POST /v1/connect/googlebusiness/select-location with accountId
• PUT /v1/accounts/{accountId}/gmb-locations with googleAccountId (legacy body field accountId still accepted)
• POST /v1/accounts/{accountId}/gmb-locations/assign with googleAccountId
August 7, 2026Breaking Change
POST /v1/ads/boost now supports attaching a boost to an existing Meta ad set via adSetId, and adds Meta support for CTA destinations via linkUrl + callToAction.

This lets you refresh/boost an existing post while keeping the ad set’s learning phase (attach to an existing ad set instead of provisioning a new campaign/ad set).

Key changes:
• Meta-only attach: send adSetId to attach under an existing ad set
• When adSetId is set, budget/schedule/targeting are owned by the ad set and sending any of them is a 400
• budget is now required only when adSetId is NOT set
• Meta-only additions: instagramAccountId, destinationType (INSTAGRAMPROFILE, WEBSITE, ONAD, MESSENGER, WHATSAPP)
• linkUrl is now supported on Meta boosts (still supported on TikTok); callToAction must be sent together with linkUrl (sending either alone is a 400)

POST /v1/ads/ctwa (deprecated) also adds an attach flow:
• Send adSetId to add creatives to an existing messaging ad set; then budgetAmount/budgetType/endDate/objective/countries/interests/audienceId are rejected with 400

Schema note (breaking for generated clients): CtwaAdRequestBody no longer lists budgetAmount/budgetType as always-required (they’re required unless adSetId is set).
August 7, 2026New Feature
New endpoint: PATCH /v1/whatsapp/phone-numbers/{id}/profile to move a provisioned number to another profile.

This re-homes the number and any associated accounts together (so you don’t split the number across profiles by moving only the social account).

Send { "profileId": "..." } where id is the phone number record id from GET /v1/phone-numbers (not an account id).

Response may include movedPlatforms with values phone, sms, whatsapp. Conflicts return 409 if the destination profile already has an account on any of those platforms.
August 7, 2026New Feature
PUT /v1/ads/campaigns/{campaignId} now supports Google campaigns (in addition to Meta) and enforces platform-level field support (unsupported fields return 400, not silently ignored).

For Google, you can set campaign bidding via:
• platform: google
• bidStrategy (Meta + Google)
• bidAmount (Google only; required for LOWESTCOSTWITHBIDCAP and COST_CAP)
• roasAverageFloor (Google only; required for LOWESTCOSTWITHMINROAS)

The 200 response now echoes back only the fields you sent, plus updated (and may include bidAmount/roasAverageFloor when provided).
August 7, 2026New Feature
Message reactions are now supported on Instagram, Facebook Messenger, and Slack via the existing reactions endpoints.

You can add/remove reactions across more inbox channels, and reaction webhooks now cover these platforms too.

Use:
• POST /v1/inbox/conversations/{conversationId}/messages/{messageId}/reactions with emoji
• DELETE /v1/inbox/conversations/{conversationId}/messages/{messageId}/reactions with accountId (query)

Notes:
• Slack reactions require a Slack emoji name like :thumbsup: (unnamed Unicode emoji returns 400)
• Instagram/Messenger reactions are subject to Meta’s 24h messaging window
• reaction.received webhook now includes Slack/Instagram/Messenger (sender can be the business platform id; compare reaction.sender.id with conversation.participantId)

Comment-to-DM automations now support sending a Meta generic template (product card) as the first DM.

Set template on POST /v1/comment-automations / PATCH /v1/comment-automations/{automationId}:
• template.type: generic
• template.elements: 1–10 cards (each supports title, subtitle, imageUrl, buttons)

Constraints:
• template is mutually exclusive with buttons (sending both is a 400)
• For PATCH, pass template: null to clear the card

Also: for POST /v1/inbox/conversations/{conversationId}/messages on Instagram/Facebook, top-level buttons are now explicitly mutually exclusive with template (sending both returns 400).
August 7, 2026New Feature
Instagram follow checks are now available via GET /v1/accounts/{accountId}/follow-status/{userId}.

This resolves the follow relationship (when Meta allows it) and returns isFollower (can be null when unknown) plus basic public counters; use refresh=true to bypass the short cache.

Key fields:
• isFollower: true | null (null = unknown, never “no”)
• unavailableReason: consentrequired | dmaccessdisabled | notmessageable | error | null

Comment-to-DM automations now support follower-based targeting + optional follow verification (Instagram only), and delayed sends.

New request fields on POST /v1/comment-automations and PATCH /v1/comment-automations/{automationId}:
• dmDelaySeconds (0–86400)
• commentReplyDelaySeconds (0–86400)
• audience (followerStatus: any | follower | non_follower; whenUnknown: send | skip | verify; optional minFollowerCount)
• followGate (message, buttonLabel, optional notFollowingMessage)

Automation logs (GET /v1/comment-automations/{automationId} and .../logs) add new status values pending and gated, plus fields like audienceOutcome (passed | blocked | gatesent | gatepassed | gate_failed) and nextDueAt while queued.

Webhook comment payloads now may include Instagram-only comment.author.instagramProfile (best-effort) with isFollower/followerCount when available; absence means “unknown”.
August 6, 2026Improvement
GET /v1/comment-automations/{automationId}/logs now returns non-matching comment diagnostics via a new misses object.

Use misses to see how many comments reached the automation but matched none of its keywords (and a few recent samples) to troubleshoot keyword/exclusion setups.

New response fields:
• misses.total
• misses.retentionDays
• misses.samples[]: commentText, commenterName, excludedBy, at

Request validation tightened:
• limit now 1..200
• skip now >= 0
• Invalid values may return 400 BadRequest
August 6, 2026Improvement
PATCH /v1/comment-automations/{automationId} now accepts trigger to control what fires an automation.

You can switch an automation between comment-based and story-reply-based triggers; changing it will detach the automation from its currently bound post/story unless the same request sets a new binding.

Set trigger to: • comment • story_reply (Instagram only)
August 6, 2026New Feature
Ads analytics now includes funnel step counts, an engagement breakdown, and cost per ThruPlay.

This makes it easier to report on Meta ads without parsing/deduping raw actions keys (and avoids double-counting), and adds a direct cost metric for ThruPlays.

New fields in AdMetrics:
• costPerThruplay
• funnel (ref AdFunnelCounts): landingPageViews, contentViews, searches, wishlistAdds, cartAdds, checkoutsInitiated, paymentInfoAdds, purchases, leads, registrationsCompleted, appInstalls, messagingConversationsStarted, messagingFirstReplies
• engagementBreakdown (ref AdEngagementCounts): postEngagement, pageEngagement, reactions, comments, shares, saves, pageLikes, videoViews, linkClicks

Also new in Ad:
• creativeType: carousel | video | document | image | null
August 6, 2026New Feature
Comment-to-DM automations now support more precise keyword matching and exclusions via matchMode=word, plus new fields excludeKeywords and typoTolerance.

This lets you avoid accidental triggers (e.g., keyword app no longer firing on happy) and optionally match close misspellings when using word-based matching.

Use matchMode:
• contains • word • exact

New fields:
• excludeKeywords: string[] - comments matching these never trigger (compared using the same matchMode)
• typoTolerance: boolean - only with matchMode=word

Available on POST /v1/comment-automations, PATCH /v1/comment-automations/{automationId}, and returned by GET endpoints.
August 6, 2026Improvement
LinkedIn connected accounts now include additional profile details in SocialAccount.metadata.profileData (refreshed on each daily snapshot).

This lets you read LinkedIn bio/headline and vanity slug info directly from the account object without extra lookups.

Key fields:
• metadata.profileData.bio (nullable)
• metadata.profileData.extraData.vanityName (personal accounts)
• metadata.organizationInfo.vanityName (organization accounts)
August 6, 2026New Feature
Meta ads creation now supports optimizing for pixel custom events via promotedObject.customEventStr on POST /v1/ads/create.

Use this when you want goal=conversions (or lead_conversion) to optimize against a custom event name from Events Manager (instead of a standard event or a Custom Conversion).

Set:
• promotedObject.customEventType = OTHER
• promotedObject.customEventStr = <youreventname>

customEventStr is case-sensitive, and OTHER requires customEventStr (and vice versa).
August 6, 2026Improvement
Incoming message.received webhooks now include quote-reply context via metadata.quotedMessageId.

This lets you correlate an inbound message that quotes/replies to an earlier message (WhatsApp/Instagram/Facebook Messenger) using the original message’s platformMessageId.

New field:
• metadata.quotedMessageId - platformMessageId of the message being quoted/replied to

Also clarified POST /v1/inbox/conversations/{conversationId}/messages replyTo behavior:
• WhatsApp: pass wamid
• Telegram: pass Telegram message ID
• Instagram: outbound quote replies are not supported by Meta's Send API; replyTo is ignored. Quote context is available on inbound messages only.
• Slack: threads the reply (uses thread_ts)
• Silently ignored on platforms without reply support (incl. Facebook Messenger)
August 6, 2026Improvement
Meta multi-language ads now support per-locale destination URLs via translations[].linkUrl on POST /v1/ads/create.

This lets you send users to different landing pages per language while keeping a single ad (shared social proof).

Set translations[].linkUrl to override the destination for that locale; when omitted it inherits the top-level linkUrl (which must be present if any locale uses translations[].linkUrl).
August 6, 2026New Feature
Webhooks now support per-subscription event blocking via disabledResourceGroups on POST /v1/webhooks/settings and PUT /v1/webhooks/settings.

This lets you prevent specific resource-group events (e.g. private messages) from ever being delivered to a webhook endpoint, even if those events are listed in events. The same filtering applies to replay paths (test fire, redelivery, dead-letter requeue).

Use disabledResourceGroups (enum: publishing, engagement, messages, contacts, analytics, ads, telephony, accounts, billing, webhooks):
• Create: include disabledResourceGroups to opt out; omit or [] to receive everything in events
• Update: sending disabledResourceGroups replaces the denylist; send [] to clear; omit to leave unchanged

Restricted zrk_ keys: the key’s own disabled groups are always unioned into the subscription, so a restricted key can’t create or widen a subscription beyond itself.

New/expanded 403 behavior (ResourceGroupForbidden) was added to:
• GET /v1/webhooks/settings
• DELETE /v1/webhooks/settings
• POST /v1/webhooks/test (note: webhook.test is in the webhooks group)

Note: after updating a webhook denylist, already-queued events may still arrive for up to ~5 minutes.
August 5, 2026Breaking Change
Non-draft updates to PUT /v1/posts/{postId} now run the same per-platform validation as post creation.

If the resulting post violates platform rules (media requirements, platform-specific fields, etc.), the update will return 400 instead of succeeding.

Also, PostUpdateResponse may now include warnings (array of strings).
August 5, 2026New Feature
WhatsApp now supports requesting a user’s contact info via interactive messages.

You can send a consent-based “share contact” prompt, and inbound replies include contact cards plus an origin flag so you can distinguish consented self-share vs address-book shares.

Send with interactive.type:
• requestcontactinfo

On message.received, check:
• metadata.contacts (array)
• metadata.contactsOrigin: contact_request | other
August 5, 2026New Feature
Slack DMs are now supported in the Inbox API.

You can list eligible Slack workspace members (for a recipient picker) via GET /v1/accounts/{accountId}/slack-members, then start a DM by passing the member id as participantId to POST /v1/inbox/conversations.

Key params:
• GET /v1/accounts/{accountId}/slack-members: query, limit
• POST /v1/inbox/conversations: participantId (Slack member id, e.g. U01ABCDEF)

Also updated: POST /v1/inbox/conversations now lists supported platforms as X/Twitter, Bluesky, Reddit, WhatsApp, SMS, and Slack.
August 5, 2026Improvement
OAuth connect now surfaces upstream token-exchange failures via a new 502 response on POST /v1/connect/{platform}.

This helps distinguish platform-side rejections from internal errors; handle 502 separately from 500 (the upstream 4xx is forwarded as platform_error).

Key response codes:
• 502 - platform rejected token exchange (platform_error)
• 500 - internal error while connecting the account

Lead form creation can now return 422 on POST /v1/ads/lead-forms when Meta rejects the form (error code 3), typically because the Facebook Page hasn’t accepted the Lead Ads Terms.

Key response code:
• 422 - Meta rejected lead form (often requires a Page admin to accept Lead Ads Terms)
August 5, 2026Improvement
Contacts listing now supports filtering by SocialAccount via accountId on GET /v1/contacts.

Use accountId to list contacts through their channel; when accountId is set, the profileId contact filter is not applied (a provided profileId is still access-checked and still scopes filters.tags).

New query param: accountId

Also, POST /v1/contacts 409 is now explicitly documented as: Duplicate channel. The platformIdentifier is already bound to a channel on this accountId.
August 5, 2026New Feature
New endpoint GET /v1/auth/verify was added to validate a bearer credential (API key or OAuth access token) without calling a data endpoint.

Use it to confirm a credential is valid before proceeding (e.g., when verifying an incoming token).

On 200 it returns valid, userId, authType (api_key | oauth | session), and scope (space-separated OAuth scopes; null for API keys). 401 if invalid.
August 5, 2026Improvement
POST /v1/ads/boost now supports Meta-only optimization goal overrides via optimizationGoal.

This lets you explicitly set the ad set optimization_goal instead of relying on the default derived from goal, which can be useful when Meta requires a different optimization goal for the boost objective.

Set optimizationGoal to a Meta-compatible value for the objective derived from your goal (e.g. boost goal engagement maps to Meta objective OUTCOMEAWARENESS, so values like REACH, IMPRESSIONS, ADRECALLLIFT, or THRUPLAY-class are accepted; POSTENGAGEMENT is rejected under that objective).
August 5, 2026New Feature
Meta Ads now supports Value Rule Sets (bid adjustments by audience segment) via new endpoints under /v1/ads/value-rule-sets.

You can create/list/read/replace/delete value rule sets, then attach one to an ad set to increase/decrease bids for matching segments (rule order matters: first match wins).

New endpoints:
• GET /v1/ads/value-rule-sets
• POST /v1/ads/value-rule-sets
• GET /v1/ads/value-rule-sets/{valueRuleSetId}
• PUT /v1/ads/value-rule-sets/{valueRuleSetId} (full replace, not patch)
• DELETE /v1/ads/value-rule-sets/{valueRuleSetId}

Attach/detach on ad sets (Meta only) via PUT /v1/ads/ad-sets/{adSetId}:
• Attach/replace: send valueRuleSetId (optionally valueRulesApplied: true)
• Detach: send valueRulesApplied: false and omit valueRuleSetId
• Sending valueRulesApplied: false with valueRuleSetId returns 400 mutuallyexclusivefields

Also supported on create (Meta only) via POST /v1/ads/create:
• Attach to the new ad set with valueRuleSetId (optional valueRulesApplied: true)
• valueRulesApplied: false is rejected on create

Eligibility (Meta enforced): only ad sets on LOWESTCOSTWITHOUTCAP or COSTCAP.
August 4, 2026New Feature
WhatsApp template creation now supports NAMED variables via parameter_format on POST /v1/whatsapp/templates.

This lets you use {{customer_name}}-style tokens (in addition to the existing positional {{1}} style) and provide per-variable examples for Meta review.

Set parameter_format to POSITIONAL | NAMED (also accepts positional | named).

For NAMED templates, include examples in:
• components[].example.bodytextnamed_params
• components[].example.headertextnamed_params

Each named param example uses: { param_name, example }.
August 4, 2026Breaking Change
Ad.creative.thumbnailUrl can now be null (was always a string).

This affects ad preview/rendering: don’t assume a thumbnail URL is always available; fall back to other creative fields when it’s missing.

Key change: creative.thumbnailUrl: string | null
August 4, 2026Improvement
Connect flow redirects now include the created social account id in the success redirect_url for these endpoints:
• POST /v1/connect/facebook/select-page
• POST /v1/connect/googlebusiness/select-location
• POST /v1/connect/pinterest/select-board
• POST /v1/connect/snapchat/select-profile

This lets you reliably capture the connected account immediately after OAuth without extra lookup.

Look for accountId in the returned redirect_url query params (e.g. ...&accountId=...).
August 3, 2026Breaking Change
PUT /v1/posts/{postId} now requires platforms when sending root-level tiktokSettings or facebookSettings.

If you include tiktokSettings or facebookSettings in an update request without a platforms array, the API will return 400. Include the target platforms (with platform + accountId) in the same request when updating these settings.
August 3, 2026Breaking Change
In PUT /v1/posts/{postId}, platforms[].platformSpecificData update behavior is now explicitly defined.

A Settings namespace (e.g. facebookSettings, tiktokSettings) that is omitted from the request is preserved from the stored post. If you send the key, it replaces the entire namespace (it is not deep-merged).

Key field: platforms[].platformSpecificData
August 3, 2026New Feature
GET /v1/inbox/conversations now supports filtering by platform=whatsapp, and conversation objects can include new ad attribution data via metadata.

This lets you identify conversations that started from Meta ads (Click-to-Message / Click-to-Direct, and WhatsApp Click-to-WhatsApp) and store/route them differently.

Use platform enum values: facebook, instagram, twitter, bluesky, reddit, telegram, whatsapp

New field: metadata (nullable). Keys are optional and may include:
• WhatsApp CTWA: ctwaclid, ctwasourceid, ctwasourcetype, ctwasourceurl, ctwaheadline, ctwacapturedat
• IG/FB ads: metaadid, metaadsource, metaadtype, metaadref, metaadtitle, metaadphotourl, metaadvideourl, metaadpostid, metaadproductid, metaadflowid, metaadcapturedat

GET /v1/inbox/conversations/{conversationId} also returns metadatametaad*` family there.
August 3, 2026Breaking Change
POST /v1/sms/sender-ids no longer enforces global uniqueness for alphanumeric sender IDs.

The same senderId can now be registered by multiple workspaces, so creating a sender ID should no longer fail due to another workspace already having it.

Changes:
• Removed 409 (senderidtaken) conflict response
• Increased active sender ID limit in 403 from 5 to 1,000 (senderidlimit_reached)

Request remains: senderId
August 3, 2026New Feature
WhatsApp now supports Meta Direct Send utility messages via category on inbox messaging endpoints.

Eligible WhatsApp Business Accounts can start or send a business-initiated utility text message without an approved template (Meta matches/auto-creates a template asynchronously). If the WABA isn’t eligible, the request fails and you must use an approved template.

Use category:
• POST /v1/inbox/conversations: set category=utility with message and no templateName (WhatsApp only)
• POST /v1/inbox/conversations/{conversationId}/messages: set category=utility to send as a business-initiated utility message (WhatsApp only)

Notes:
• category enum: utility
• category cannot be combined with templateName (create-conversation) or template (send-message)
• category is accepted on application/json only (not multipart/form-data)
August 3, 2026Improvement
Sending inbox messages now supports idempotent retries via Idempotency-Key on POST /v1/inbox/conversations/{conversationId}/messages.

This prevents duplicate sends when you retry after timeouts: same key + same body replays the original response (with Idempotent-Replayed: true) instead of sending again. Works for both application/json and multipart/form-data requests; keys are retained for 24 hours.

Use:
• Idempotency-Key (header)

New error responses:
• 409 - Idempotency key still in flight
• 422 - Idempotency key reused with a different body
August 2, 2026Improvement
PostAnalytics now includes videoDurationSeconds (nullable integer).

This exposes the video length (currently Instagram Reels only) so you can estimate retention when combined with igReelsAvgWatchTime (ms).

New field:
• videoDurationSeconds - video length in seconds; null when unknown (non-video/other platforms, or when Instagram doesn’t expose the media URL, e.g. some copyrighted-audio reels).
August 1, 2026New Feature
LinkedIn ads now return delivery diagnostics and effective bidding details via new fields on Ad and GET /v1/ads/tree nodes.

This lets you distinguish “configured active” vs “actually serving” and understand why a LinkedIn campaign/creative is not delivering (holds, review, budget, etc.).

New fields:
• Ad.costType (LinkedIn only): CPM | CPC | CPV
• Ad.servingStatuses (LinkedIn only): array of serving/hold reasons (e.g. RUNNABLE, ACCOUNTTOTALBUDGET_HOLD)
• Ad.creative.isServing (LinkedIn only): true | false | null
• Ad.creative.servingHoldReasons (LinkedIn only): creative-level hold reasons (e.g. UNDER_REVIEW, REJECTED)

Also added to tree responses:
• AdTreeAdSet.costType, AdTreeAdSet.servingStatuses

Note: for LinkedIn, bidAmount represents the campaign’s effective unitCost and can be 0 (a real, delivery-stopping value), independent of bidStrategy.
August 1, 2026Improvement
GET /v1/contacts now supports filtering by multiple tags via a new tags query parameter.

Use tags as a comma-separated list to match contacts that have any of the provided tags.

New parameter: tags (comma-separated, OR match). Example: tags=vip,lead,partner
August 1, 2026Improvement
GET /v1/ads/accounts now returns additional platform status details for ad accounts.

This lets you distinguish platform-specific states (e.g., Meta disabled vs. billing vs. review) and show better “why can’t I run ads?” messaging instead of relying only on selectable.

New fields in accounts[]:
• accountStatus (platform status; type varies by platform)
• disableReason (Meta only; integer code when accountStatus indicates disabled)
• approvalStatus (X only; observed: ACCEPTED, PENDING, REJECTED)

Note: LinkedIn continues to use status (filtered to active accounts).
August 1, 2026Improvement
POST /v1/contacts/bulk now treats missing platformIdentifier as a per-contact failure when importing in channel mode.

If you send a top-level accountId, each contact is expected to include platformIdentifier; rows missing it are rejected individually and returned in errors[] under an HTTP 200 (the whole import no longer fails with HTTP 400 for that case).

Key behavior:
• Channel mode: accountId set + contact missing platformIdentifier → HTTP 200 with errors[]
• Request-level 400 still applies for missing required name, or when a row includes channel data but there is no top-level accountId to attach it to
July 31, 2026Improvement
GET /v1/ads/campaigns now supports listing Meta empty campaigns (campaigns with adCount: 0), which were previously invisible because campaigns are aggregated from ad documents.

Use includeEmpty=true to include these campaigns (metrics will be zeroed). Requires accountId and adAccountId when includeEmpty=true.

PUT /v1/ads/campaigns/{campaignId} can now update an empty Meta campaign by sending accountId in the request body (otherwise it may 404 due to no local ad docs). The response will return updated: 0 for empty campaigns.
July 31, 2026Improvement
Meta ads campaign create/delete behavior has been tightened and now supports deleting “empty” campaign shells.

On POST /v1/ads/campaigns, budgetAmount is explicitly in WHOLE currency units (not cents), and bidStrategy now requires a campaign budget (budgetAmount + budgetType = daily | lifetime). Sending bidStrategy without a campaign budget is now a 400, and invalid bid states (e.g. missing bidAmount for LOWESTCOSTWITHBIDCAP/COST_CAP) are rejected up front.

On DELETE /v1/ads/campaigns/{campaignId}, you can now delete a campaign with zero ads by including accountId in the body (alongside platform = facebook | instagram). accountId is only required for empty campaigns.

On POST /v1/ads/create, budgetAmount is also clarified as WHOLE currency units (not cents) to avoid Meta minor-unit mixups.
July 31, 2026Breaking Change
POST /v1/contacts/bulk request requirements and account/platform resolution have changed.

You can now do a CRM-only import without channels, but when importing channel identifiers you must attach them to an account; duplicates are still skipped, but any new tags are merged onto the existing contact.

Key changes:
• Top-level required fields are now profileId and contacts (no longer always requiring accountId/platform)
• accountId is required when contacts include platformIdentifier (or a row-level accountId); otherwise the request returns 400
• platform is ignored when accountId is set; the platform is derived from the resolved account, and a mismatch can 404 as account not found
July 31, 2026Breaking Change
Facebook post options have been reorganized: draft/carousel/colored-background text settings are now grouped under facebookSettings.

This makes Facebook-specific options consistent and clarifies which fields belong under platformSpecificData vs nested settings.

Move these fields under facebookSettings (root-level or per-platform):
• draft
• carouselCards
• carouselLink
• textFormatPresetId

Applies to POST /v1/posts and PUT /v1/posts/{postId}. Also note: firstComment is skipped when facebookSettings.draft is true.
July 31, 2026Improvement
GET /v1/accounts/{accountId}/linkedin-mentions now returns 403 when LinkedIn blocks person-mention lookup due to missing company page posting privileges.

This lets you distinguish “insufficient LinkedIn org/page role” from invalid input (400) or not found (404), and show a clearer remediation message.

New response:
• 403 - account has no organization role that can resolve person mentions (error message indicates needing Admin/Content Admin on a page, or mentioning an organization instead)
July 31, 2026Breaking Change
Create/duplicate ads endpoints now support safe retries via an Idempotency-Key header.

This prevents accidental duplicate campaigns/ad sets/ads when your client retries after timeouts or network errors.

Now supported on:
• POST /v1/ads/campaigns
• POST /v1/ads/campaigns/{campaignId}/duplicate
• POST /v1/ads/ad-sets/{adSetId}/duplicate
• POST /v1/ads/{adId}/duplicate

Behavior:
• same Idempotency-Key + same body → replay original 2xx response
• same key + different body → 422
• key still processing → 409

Also: metrics conversions is now a number (can be fractional) in GET /v1/ads/timeline and the AdMetrics schema (attribution splitting + Google modeled conversions).
July 31, 2026New Feature
You can now manage OAuth connected apps for the authenticated user: list authorized clients and revoke their access.

List connected apps with GET /v1/me/connected-apps. Each item includes clientId, clientName, redirectHost, scopes, authorizedAt, lastUsedAt, tokenCount.

Revoke an app with DELETE /v1/me/connected-apps/{clientId} (idempotent). Response includes revoked, revokedTokens, invalidatedCodes.

Auth note: requires a session or full-scope API key; profile-scoped API keys and OAuth access tokens get 403 (insufficient_permissions).
July 31, 2026New Feature
WhatsApp now supports re-registering a connected number on Meta Cloud API, and the credentials connect flow can include a 2-step verification PIN.

If a number has its own 2-step PIN, Meta can reject registration (error 133005) and sends may fail with misleading permission errors. You can now pass the PIN during connect, or re-run registration later.

Use:
• POST /v1/connect/whatsapp/credentials with optional pin (6 digits, ^\d{6}$)
• New: POST /v1/accounts/{accountId}/whatsapp/register with optional pin

Connect response may include registrationWarning when the account was created but Cloud API registration was rejected (number can’t send until resolved).

Inbox comments endpoints now also return platform/upstream error responses you may want to handle: 403 (platform permission), 429 (rate limited), 502 (upstream platform error) on GET/POST/DELETE /v1/inbox/comments/{postId}.
July 30, 2026Improvement
Facebook text background posts via textFormatPresetId no longer reject long captions.

Previously, requests were rejected with 400 when content exceeded ~130 characters. Now, length is not rejected; Facebook may publish and return a warning instead.

Use textFormatPresetId (numeric string, ^\d+$) for Pages + text-only feed posts. Requests are still rejected with 400 if mediaItems or carouselCards are present, if contentType is story or reel, or if content is empty.
July 30, 2026New Feature
WhatsApp Calling config now supports a call duration cap and forwarded caller ID control on calling endpoints.

You can limit forwarded call length (to prevent dead-air billing) and, for SIP forwards, choose whether the destination sees the business number or the end-caller.

New fields:
• maxCallDurationSeconds (request/response) — 30..14400 or null (no cap / clear cap)
• forwardCallerId (request/response) — business | caller

Available on:
• GET /v1/whatsapp/calling
• GET /v1/phone-numbers/{id}/whatsapp/calling
• POST/PATCH /v1/phone-numbers/{id}/whatsapp/calling (and the deprecated alias /v1/whatsapp/phone-numbers/{id}/calling)
July 30, 2026Breaking Change
Bulk contact import (POST /v1/contacts/bulk) now validates and normalizes phone identifiers for phone platforms.

When platform is whatsapp or sms, each contact’s platformIdentifier is normalized to digits; values that aren’t phone-shaped are rejected per contact and reported in errors[] (not imported).

Check your handling of errors[] in the 200 response (now returned as strings describing per-contact failures).
July 30, 2026New Feature
WhatsApp Calling now supports caller-ID verification for customer-brought (BYO) phone numbers, so tel: call forwards can present the business number as caller ID.

Start (or resend) a verification code via POST /v1/phone-numbers/{id}/whatsapp/caller-id-verification with optional method: sms | call.

Confirm the code via POST /v1/phone-numbers/{id}/whatsapp/caller-id-verification/verify with required code.

GET /v1/whatsapp/calling now returns:
• callerIdMode: business | platform
• callerIdVerified: true | false
July 30, 2026Improvement
WhatsApp Calling endpoints now return caller ID details for tel: forwards via callerIdMode (and verification status on GET).

This helps you understand whether the PSTN forward-leg will present the business number or a Zernio platform number when caller ID isn’t verified.

New response fields:
• POST /v1/phone-numbers/{id}/whatsapp/calling and POST /v1/whatsapp/phone-numbers/{id}/calling: callerIdMode = business | platform
• GET /v1/phone-numbers/{id}/whatsapp/calling: callerIdMode = business | platform, callerIdVerified (boolean)

Also clarified: 400 can be returned if forwardTo is set to the number itself.
July 30, 2026New Feature
Facebook posts now support large-text background presets via textFormatPresetId in FacebookPlatformData.

This lets you render a text-only feed post as large text on a colored background using Meta’s textformatpreset_id.

Use textFormatPresetId (numeric string, pattern ^\d+$). Constraints:
• Text-only feed posts only: request is rejected if mediaItems or carouselCards are present
• Not allowed with contentType story or reel
• content must be non-empty and ≤ 130 chars (otherwise rejected)
• If a URL is present in content, no link preview is attached while a preset is set
July 30, 2026Improvement
Rate limit guidance has been updated: the per-account velocity limit referenced in 429 responses is now 25 posts/hour per account (was 15) for:
• POST /v1/posts
• POST /v1/posts/{postId}/retry

If you throttle based on the error text for 429, update your assumptions to match the new 25 posts/hour limit.
July 30, 2026Improvement
Meta ad URL tags now preserve Meta dynamic macros in tracking parameters.

When setting Meta urlTags, macros like {{ad.id}}, {{campaign.id}}, {{placement}} are sent through unescaped so Meta can expand them; all other characters are percent-encoded.

Applies to:
• PATCH /v1/ads/{adId}/tracking-tags via urlTags
• POST /v1/ads/boost via tracking.urlTags
• POST /v1/ads/create via tracking.urlTags
July 30, 2026Improvement
Meta ads reporting now supports filtering by Facebook Page via pageId on ads, campaigns, and the campaign tree.

This lets you scope results and rolled-up metrics to ads whose creative is backed by a specific Page (useful when one Meta ad account serves multiple Pages).

New query param:
• pageId (Meta only)

Available on:
• GET /v1/ads
• GET /v1/ads/campaigns
• GET /v1/ads/tree

Matches against ad.creative.pageId (may be null for non-Meta ads and rare IG-only creatives).
July 30, 2026New Feature
Facebook analytics now supports per-post monetization earnings via GET /v1/analytics/facebook/post-earnings.

This returns lifetime cumulative earnings for a single post, read live from Meta on each request (no date range supported).

Required params:
• accountId
• postId (e.g. {pageId}_{postId} or bare Reels video ID)

Optional:
• metrics = contentmonetizationearnings, monetizationapproximateearnings (defaults to both)

Notes:
• since/until/period/metricType/breakdown are rejected with 400 (lifetime only).
• Money metrics include unit (micro_amount | unspecified) and currency (string | null), and are never rescaled.
• Metrics Meta can’t serve are omitted from metrics and listed in unavailableMetrics (HTTP 200).

Also: GET /v1/analytics/facebook/page-insights now accepts monetization metrics (contentmonetizationearnings, monetizationapproximateearnings) and may return unavailableMetrics with the same omit-vs-zero behavior.
July 29, 2026New Feature
POST /v1/media/presign now supports generating presigned upload URLs for audio files.

You can upload audio by setting contentType to one of: audio/mpeg, audio/mp4, audio/aac, audio/ogg, audio/wav, audio/webm, audio/x-m4a.
July 29, 2026Improvement
Phone number purchases can now be auto-assigned to a different profile when the requested profileId already has a number.

Send profileId as the preferred profile, but the API may assign the number to the next free profile (or create one) and will return the actual assignment.

Check the assigned profile in:
• phoneNumber.profileId (when a number is provisioned)
• profileId in the { status: "already_purchased" } response when using purchaseIntentId
July 29, 2026Breaking Change
Instagram user tagging behavior has changed for platformSpecificData.instagram.userTags.

userTags items now require only username (previously username + x + y were required). Tag handling now depends on media type:
• Photos: x/y are required; photo tags without valid coordinates are skipped
• Reels/videos: username-only tags; any x/y provided are ignored
• Stories: x/y are optional

Carousel targeting still uses mediaIndex (defaults to 0); out-of-range indices are ignored.
July 29, 2026New Feature
GET /v1/inbox/conversations/search now supports filtering by Slack via platform=slack.

This lets you search conversations and restrict results to Slack accounts the same way as other stored-message platforms.

Use platform = facebook | instagram | telegram | whatsapp | sms | slack

The conversation.started webhook payload also adds conversation.platform=slack in WebhookPayloadConversationStarted.
July 29, 2026New Feature
POST /v1/posts/sync-external now supports LinkedIn personal profiles.

Because LinkedIn personal profiles don’t have a listing API, you must provide url to import a single post authored by the connected member (works even for posts published before the account was connected).

Use accountId + url (required for LinkedIn personal), where url can be:
• linkedin.com/posts/…
• linkedin.com/feed/update/urn:li:activity:…
• urn:li:share:… / urn:li:ugcPost:…

Imported LinkedIn personal posts return analytics (impressions, reach, reactions, comments, reshares, saves) but no content/media.
July 29, 2026New Feature
Slack accounts now support reading and updating per-channel default message identity via new endpoints GET /v1/accounts/{accountId}/slack-settings and PATCH /v1/accounts/{accountId}/slack-settings.

This lets you set default author name/avatar for Slack messages on an account (per-post platformSpecificData.username/platformSpecificData.iconUrl can still override).

Update with defaultUsername (max 80) and defaultIconUrl. Send an empty string to clear either field.

The settings response includes channel details like channelId, channelName, channelType (public or private), plus teamId/teamName.
July 29, 2026Improvement
POST /v1/inbox/conversations/{conversationId}/messages now returns more detailed send failures in 400 responses.

You can distinguish missing-recipient cases and, for Instagram/Facebook, get Meta’s diagnostic fields to debug rejected sends.

New 400 fields:
• code: PLATFORMLIMITATION | MISSINGPARTICIPANT
• platformError (Instagram/Facebook only): code, subcode, fbtraceId, type
July 29, 2026New Feature
Slack is now supported as a posting platform via POST /v1/posts and POST /v1/tools/validate/post, and can be connected via GET /v1/connect/{platform}.

You can schedule/publish Slack messages to the channel tied to the connected Slack accountId (one connected account per channel).

Use platform=slack and Slack options in platformSpecificData:
• threadTs
• unfurlLinks
• unfurlMedia
• username
• iconUrl

Slack is also available for filtering in GET /v1/accounts/health via platform=slack.
July 29, 2026New Feature
New Google Ads keyword research endpoints are available, and GET /v1/ads/insights now supports Google Ads queries.

You can now pull Keyword Planner data via:
• POST /v1/ads/keywords/ideas (generate ideas from seedKeywords and/or seedUrl)
• POST /v1/ads/keywords/historical-metrics (metrics for up to keywords = 1000)

Key request fields:
• accountId (required)
• customerId (optional; needed if the connection has several Google Ads accounts)
• countries (optional; omitted = worldwide)
• languageConstantId (default 1000)
• network: GOOGLESEARCH | GOOGLESEARCHANDPARTNERS

GET /v1/ads/insights now routes by platform:
• Meta: use objectId (+ fields/breakdowns/filtering as before)
• Google Ads: use query (GAQL SELECT), with paging via pageToken and paging.nextPageToken (fixed 10,000 rows/page)

Note: Google responses return raw rows (camelCase) with int64 counters encoded as strings; bid/CPC values are in micros.
July 29, 2026Improvement
Instagram and Facebook Inbox now include pre-connect DM history replay in GET /v1/inbox/conversations and GET /v1/inbox/conversations/{conversationId}/messages.

When an IG/FB account is connected, Zernio replays existing Meta DM history in the background so older threads can appear in listings; if you sync into your own store, you should re-sweep rather than relying on a single pass at connect time.

Key behavior:
• GET /v1/inbox/conversations: up to 500 conversations per account are replayed; replayed threads keep their original lastMessageAt/updatedTime so they sort into date order (not bumped to the top)
• Replayed history emits no webhooks and is stored as read (does not affect unreadCount)
• GET /v1/inbox/conversations/{conversationId}/messages: replay includes the 500 most recent messages per conversation; older messages beyond that are not retrievable
July 29, 2026Breaking Change
POST /v1/sms/registrations no longer requires phoneNumbers in the request body.

For 10DLC registrations, if phoneNumbers is omitted or an empty array, it now defaults to your active SMS-enabled US local numbers that aren’t already covered by another registration.

Key request fields:
• registrationType: standard10dlc | soleprop10dlc | tollfree
• phoneNumbers: optional (new defaulting behavior for 10DLC)
July 29, 2026New Feature
Webhooks now support post.platform.deleted in events for POST /v1/webhooks/settings and PUT /v1/webhooks/settings.

This event fires when Zernio’s background sync detects a platform post (published via Zernio) was later deleted on the platform (poll-driven ~hourly, not real-time).

Subscribe by including post.platform.deleted in events.

In WebhookPayloadPostPlatform:
• event: post.platform.published | post.platform.failed | post.platform.deleted | post.tiktok.url_resolved
• platform.status: published | failed | deleted
• New field on delete events: platform.deletedAt (ISO 8601)
July 28, 2026New Feature
Instagram is now supported for typing indicators via POST /v1/inbox/conversations/{conversationId}/typing.

Calling this endpoint can now show typing... to the Instagram recipient (works for both Instagram Login and Facebook Login accounts; recipient must be signed in to Instagram to see it).

Request: { "accountId": "..." }
Response: { "success": true|false } where success indicates whether the typing indicator was actually sent (false on unsupported platforms or if the platform call failed).

The endpoint may now also return 400 (BadRequest) in addition to existing responses.
July 28, 2026New Feature
Meta conversion goals on POST /v1/ads/create now support optimizing against a Custom Conversion.

This lets you run goal conversions or lead_conversion without providing a standard event type, by pointing the ad set at an existing Custom Conversion.

Use promotedObject.customConversionId (instead of promotedObject.customEventType) for:
• goal=conversions
• goal=lead_conversion
July 28, 2026New Feature
SMS carrier registrations now support a preflight dry-run and change-request replies.

Use POST /v1/sms/registrations/preflight to validate and preview the exact 10DLC brand/campaign payloads that would be stored, without creating a registration. It returns compliance findings (including AI review when available) so you can fix issues before submitting.

Request: registrationType (standard10dlc | soleprop_10dlc), brand, campaign, optional phoneNumbers, optional messagingBrandName
Response: composed, advisories (severity block | warn), verdict (pass | warn | fail | unreviewed), aiUnavailable

Use POST /v1/sms/registrations/{id}/respond to reply to a reviewer change request (when a registration is in changes_requested) with a note, files (hosted URLs), or both—without resubmitting the full registration. On success, status returns to requested.

Also added to POST /v1/sms/registrations: messagingBrandName (2–60 chars) to brand samples/auto-replies when it differs from the legal brand.displayName.
July 28, 2026Improvement
Webhook limits have increased: you can now configure up to 50 webhooks per user (was 10).

This lets you split event subscriptions across more endpoints/environments without hitting the previous cap.

Applies to:
• GET /v1/webhooks/settings (list up to 50)
• POST /v1/webhooks/settings (create up to 50; 400 if max reached)
July 28, 2026New Feature
New endpoint GET /v1/twitter/search lets you search public tweets from the last 7 days using an X query (passed through unchanged) to discover tweets to reply to.

Use it with:
• accountId (required)
• query (required, 1–512 chars)
• limit (10–100, default 10)
• cursor (pagination)
• sinceId / untilId (numeric tweet IDs)
• startTime / endTime (ISO 8601 UTC, within last 7 days)
• sortOrder: recency | relevancy (default recency)

To reply to a found tweet, set platformSpecificData.replyToTweetId to the returned tweets[].id when creating a post.

Rate limit: 300 requests per 15 minutes per connected account.
July 27, 2026Improvement
KYC submission now enforces stricter validation when requesting multiple numbers with an areaCode.

If you pass areaCode and request quantity above that area’s live inventory, the request is rejected with 400 (instead of best-effort skipping).

Applies to:
• POST /v1/phone-numbers/kyc
• POST /v1/whatsapp/phone-numbers/kyc

Key params: areaCode, quantity (1–5).
July 27, 2026Breaking Change
GET /v1/accounts now enforces stricter pagination and query validation.

If you use server-side pagination, you must send page and limit together; sending only one now returns 400. Out-of-range page/limit values are also rejected with 400 (instead of being silently clamped).

Key params:
• page (1-based) + limit (1–100) must be supplied together
• profileId must be a valid ObjectId
July 27, 2026Improvement
POST /v1/tools/validate/post now accepts accountId per platform to validate against account-specific limits.

For platform=twitter, providing platforms[].accountId lets validation apply the correct character limit (280 vs 25000 for X Premium). Missing/invalid/foreign IDs fall back to 280 and won’t error.

Use:
• platforms[].platform (enum: twitter, instagram, tiktok, youtube, facebook, linkedin, bluesky, threads, reddit, pinterest, telegram, snapchat, googlebusiness, discord)
• platforms[].accountId
July 27, 2026New Feature
New endpoint GET /v1/ads/keywords is available to list Google Search keyword criteria (positive and negative) synced from connected Google Ads accounts (one row per ad-group keyword).

Keywords are populated by the periodic ads discovery sweep (~every 3 hours per account). Campaign-level negative keywords aren’t included (ad-group-level only).

Filter with:
• accountId, adAccountId (Google customer ID), profileId
• campaignId, adSetId (Google ad group)
• status: active | paused
• matchType: exact | phrase | broad | unknown
• negative: true (negative only) | false (positive only)
• search (substring match), plus pagination via page/limit (1–500)

Response returns keywords with fields like keyword, matchType, status, negative, and syncedAt, plus pagination.
July 24, 2026Improvement
WhatsApp template endpoints can now return 502 when Meta rejects the request or is unreachable.

This makes Meta-side failures explicit so you can distinguish platform outages/rejections from Zernio validation/auth errors.

Applies to:
• GET /v1/whatsapp/templates/{templateName}
• PATCH /v1/whatsapp/templates/{templateName}
• DELETE /v1/whatsapp/templates/{templateName}

Also added ad review state support via reviewStatus (schema AdReviewStatus: inreview, approved, rejected, withissues) and Meta-only configuredStatus on Ad to separate delivery status from the ad’s on/off toggle.
July 24, 2026Improvement
message.sent webhook payload now includes message.source for WhatsApp.

This lets you tell whether a sent message came from the WhatsApp Business phone app on a Coexistence number vs being sent through Zernio (dashboard/API/broadcasts).

New field: message.source = whatsappbusinessapp | cloud_api

Only present for WhatsApp; absent on non-WhatsApp platforms.
July 23, 2026New Feature
New endpoint: POST /v1/phone-numbers/{id}/remediate/respond lets you respond to a regulatory reviewer for a phone number awaiting remediation.

You can send a reviewer message and/or submit corrected requirement data in a single call; submitting corrections re-submits the number back to review, while message-only posts to the reviewer thread.

Request body supports:
• message (max 2000)
• documents[] keyed by requirementId (use base64 or documentId)
• address (corrected address record)
• entityType: individual | business | null
• attachments[] (max 5; each filename + base64)

Response includes status: resubmitted | replied, plus posted, phoneNumber, and siblingsResubmitted.
July 23, 2026Improvement
GET /v1/profiles now supports exact-name filtering and pagination, and the default sort order is now default-first (then creation date).

Use name (exact match) to find a profile by name, and limit/skip to paginate. When limit or skip is provided, the response may include total, skip, and limit.

New query params: name, limit (1..1000), skip (>=0)

POST /v1/profiles now supports idempotent retries via Idempotency-Key (header).

With Idempotency-Key: same key + same body replays the original 201; same key + different body returns 422; key still processing returns 409. Duplicate name conflicts (409) now include details.existingProfileId.
July 23, 2026New Feature
New endpoint: POST /v1/phone-numbers/{id}/remediate/reply lets you reply to a regulatory reviewer on a phone number awaiting remediation, including optional file attachments.

Use it when the reviewer asks for info the structured remediation form can’t express; posting a reply can move the number back to in review (comment-style asks). Requires text or at least one attachments item.

Request body:
• text (string, max 2000)
• attachments (array, max 5) items: { filename, base64 } (PDF/JPG/PNG/WEBP, max 10MB each)

Response 200:
• posted (boolean)
• attachments (integer uploaded)

Errors: 400, 401, 404, 502 (retry).
July 23, 2026New Feature
You can now view KYC documents on file via GET /v1/phone-numbers/kyc/document/{documentId}.

This streams the stored verification document inline as application/pdf, so users can confirm what’s on file before reusing a verification. Access is auth-scoped (returns 404 if the documentId isn’t referenced by one of your numbers).

KYC spec responses now include documentId on document rows:
• GET /v1/phone-numbers/kyc → reusable.options[].details[].documentId
• GET /v1/whatsapp/phone-numbers/kyc → reusable.options[].details[].documentId

Use that documentId as the {documentId} path parameter when fetching the PDF.
July 23, 2026Improvement
KYC reuse selection now supports a stable option identifier via reusable.options[].id, and KYC reuse options now include instant to indicate activation speed.

This makes selecting which prior verification to reuse unambiguous (a phone number may label multiple verifications) and lets you know whether reuse activates in minutes or still queues for carrier review.

Use on submit:
• POST /v1/phone-numbers/kyc: pass reuseOptionId (preferred)
• reuseFrom is now a legacy fallback and may be ambiguous

Returned on form spec:
• GET /v1/phone-numbers/kyc (and the deprecated WhatsApp alias): reusable.options[] now includes id and instant
July 23, 2026New Feature
PUT /v1/ads/ad-sets/{adSetId} now supports updating bid strategy for platform=openai.

You can set OpenAI ad group bidding via bidStrategy + bidAmount; bidAmount is converted to micros (× 1,000,000).

Use bidStrategy:
• LOWESTCOSTWITHBIDCAP (requires bidAmount)
• COST_CAP (requires bidAmount)

LOWESTCOSTWITHMINROAS is not supported on OpenAI and will return 422.
July 23, 2026New Feature
Zernio Ads now supports OpenAI Ads (ChatGPT Ads), including connect, create/manage, reporting, and conversions.

Connect an OpenAI Ads account via POST /v1/connect/openai-ads/credentials with apiKey and profileId (requires Ads add-on).

Ads endpoints now accept platform=openai:
• GET /v1/ads, GET /v1/ads/campaigns, GET /v1/ads/tree, GET /v1/ads/timeline
• Campaign/ad set status updates: PUT /v1/ads/campaigns/{campaignId}/status, POST /v1/ads/campaigns/bulk-status, PUT /v1/ads/ad-sets/{adSetId}, PUT /v1/ads/ad-sets/{adSetId}/status

Creating ads: POST /v1/ads/create supports OpenAI Ads with:
• goal: traffic | awareness | conversions
• Required creative fields: headline, body, imageUrl, linkUrl
• Budget: budgetType must be lifetime (sending daily returns 422) and requires endDate

Conversions: POST /v1/ads/conversions now supports accountId platform openaiads.
• Use GET /v1/accounts/{accountId}/conversion-destinations to get the OpenAI pixel destinationId (pixel wire id)
• If no pixel exists yet, create one via POST /v1/accounts/{accountId}/tracking-tags (OpenAI Ads may return 422 if pixel management isn’t enabled)

Note: PUT /v1/ads/{adId} supports OpenAI Ads for status + budget only (no targeting/creative updates). DELETE /v1/ads/{adId} archives OpenAI Ads (no platform delete API).
July 23, 2026Improvement
YouTube analytics endpoints now support returning provisional (not-yet-finalized) days when you set an explicit endDate up to today.

By default, endDate still resolves to the newest fully finalized day (~3-day delay). If you request into the delay window, the response indicates which days may still change.

Applies to:
• GET /v1/analytics/youtube/daily-views
• GET /v1/analytics/youtube/video-retention
• GET /v1/analytics/youtube/demographics

New response field:
• provisionalSince — first date in the returned range whose numbers are provisional and may be revised by YouTube.
July 22, 2026New Feature
WhatsApp template creation now supports carousel and limited time offer components in components on POST /v1/whatsapp/templates.

This lets you build richer MARKETING templates (multi-card carousels and offer labels/countdowns) using the same template creation endpoint.

New component types:
• type=carousel with cards (2–10), each card components can include type=header, type=body, type=buttons
• type=limitedtimeoffer with limitedtimeoffer.text (<=16 chars) and optional limitedtimeoffer.has_expiration

Phone number purchase adds wantsWhatsapp on POST /v1/phone-numbers/purchase (used when connectWhatsapp=false) to ensure the assigned number is WhatsApp-eligible by swapping it during purchase if needed.
July 22, 2026Improvement
Profile create/update now return 409 Conflict when the profile name is already taken.

This lets clients distinguish name collisions from generic validation errors and handle retries/renames cleanly.

New response:
• POST /v1/profiles → 409 (code: profilenameconflict)
• PUT /v1/profiles/{profileId} → 409 (code: profilenameconflict)

Several Ads read endpoints now also document 400 Bad Request for invalid query input:
• GET /v1/ads → 400
• GET /v1/ads/catalogs → 400
• GET /v1/ads/catalogs/{catalogId}/product-sets → 400
• GET /v1/ads/conversions/quality → 400
July 22, 2026Improvement
Meta ads now support additional promoted-object fields, and some payloads are validated more strictly.

In POST /v1/ads/create, promotedObject adds:
• offlineConversionDataSetId
• whatsappPhoneNumber

In PUT /v1/ads/ad-sets/{adSetId}, platformSpecificData.promotedObject adds the same fields and now rejects unknown keys (additionalProperties: false), returning 400.

In POST /v1/ads/boost and POST /v1/ads/create, Meta adds specialAdCategoryCountry (array of 2-letter ISO codes). It requires specialAdCategories to be set (400 otherwise).

GET /v1/ads/audiences and POST /v1/ads/audiences add a new audience type value: meta_engagement.
July 22, 2026Breaking Change
Discord: new member lookup + search endpoints, and guild member listing now fails fast when the Server Members Intent is missing.

You can now fetch a single member or prefix-search members without the privileged intent (useful when full listing is blocked).

New endpoints:
• GET /v1/discord/guilds/{guildId}/members/{userId} (accountId)
• GET /v1/discord/guilds/{guildId}/members/search (accountId, query, optional limit) — query is prefix-only

Breaking change:
• GET /v1/discord/guilds/{guildId}/members now returns 403 if the Server Members Intent is not enabled (previously Discord could return an empty array).

SMS: alphanumeric Sender IDs are now supported.

Create and manage branded one-way SMS sender IDs (3–11 chars; letters/digits/spaces; at least one letter) and view the workspace-wide daily cap.

New endpoints:
• GET /v1/sms/sender-ids (returns senderIds + budget with cap/usedToday/level/pendingRequest)
• POST /v1/sms/sender-ids (senderId)
• DELETE /v1/sms/sender-ids/{id}
• POST /v1/sms/sender-ids/limit-request (requestedCap, reason)

Ads (Meta): new campaign/creative utilities + validate-only dry runs.

You can now create campaign shells, manage the creative library, duplicate ad sets/ads, and run a Meta validation pass without creating anything.

New endpoints:
• POST /v1/ads/campaigns (standalone campaign)
• POST /v1/ads/creatives, GET /v1/ads/creatives, GET /v1/ads/creatives/{creativeId}, PUT /v1/ads/creatives/{creativeId}, DELETE /v1/ads/creatives/{creativeId}
• GET /v1/ads/images, GET /v1/ads/labels, GET /v1/ads/businesses, GET /v1/ads/high-demand-periods
• POST /v1/ads/ad-sets/{adSetId}/duplicate, POST /v1/ads/{adId}/duplicate

New option on POST /v1/ads/create:
• validateOnly: true (Meta only; single standalone shape only) — returns 200 with validation results instead of creating entities.
July 22, 2026Breaking Change
WhatsApp inbound attachments can now be downloaded via GET /v1/whatsapp/media/{mediaId}.

This streams the binary for a WhatsApp attachment referenced by attachments[].url in message.received webhooks and List messages.

Call it with:
• mediaId (path) from attachments[].payload.id
• accountId (query) = the WhatsApp account that received the media
• Authorization: Bearer <your API key> (required; otherwise 401)

Important behavior:
• Download on receipt: WhatsApp media expires on Meta’s side (typically ~7 days). After expiry this endpoint returns 400 permanently (do not retry).

Removed endpoints:
• GET /v1/sms/sender-ids, POST /v1/sms/sender-ids, POST /v1/sms/sender-ids/limit-request, DELETE /v1/sms/sender-ids/{id}
• POST /v1/ads/campaigns
• POST /v1/ads/ad-sets/{adSetId}/duplicate
• POST /v1/ads/{adId}/duplicate
• GET /v1/ads/labels
• GET /v1/ads/high-demand-periods
• GET/POST/GET/PUT/DELETE /v1/ads/creatives (and /v1/ads/creatives/{creativeId})
• GET /v1/ads/images

Breaking change (SMS): POST /v1/sms/messages no longer supports alphanumeric sender IDs in from (now requires an SMS-enabled number).
July 22, 2026New Feature
Meta Ads: new endpoints for campaign/creative management, duplication, and library access.

You can now create a standalone campaign shell, duplicate ad sets/ads via Meta native copy, manage creatives separately from ads, and query labels/images/creative library + budget schedules.

New endpoints:
• Create campaign (ODAX shell): POST /v1/ads/campaigns with goal (engagement, traffic, awareness, videoviews, leadgeneration, leadconversion, jobapplicants, conversions, apppromotion, catalogsales), optional CBO via budgetAmount + budgetType (daily|lifetime), status (ACTIVE|PAUSED)
• Duplicate ad set: POST /v1/ads/ad-sets/{adSetId}/duplicate with deepCopy, campaignId, statusOption (ACTIVE|PAUSED|INHERITEDFROMSOURCE), syncAfter
• Duplicate ad: POST /v1/ads/{adId}/duplicate with adSetId, statusOption (ACTIVE|PAUSED|INHERITEDFROMSOURCE), syncAfter
• Creative library + CRUD: GET /v1/ads/creatives, POST /v1/ads/creatives (use imageUrl or imageHash or carouselCards), GET /v1/ads/creatives/{creativeId}, PUT /v1/ads/creatives/{creativeId} (rename via name), DELETE /v1/ads/creatives/{creativeId}
• Libraries/metadata: GET /v1/ads/images, GET /v1/ads/labels
• Budget schedules: GET /v1/ads/high-demand-periods (exactly one of campaignId or adSetId)

Audience updates expanded:
PUT /v1/ads/audiences/{audienceId} now supports updating platform audiences’ name/description (Meta only). spec is still allowed only for saved_targeting; sending spec for a platform audience returns 400. Non-Meta platform audience updates return 501.
July 21, 2026New Feature
SMS now supports alphanumeric sender IDs (e.g. ZERNIO) for one-way international texting.

Create/manage sender IDs via:
• GET /v1/sms/sender-ids (includes workspace daily budget: budget.cap, budget.usedToday, budget.level, budget.pendingRequest)
• POST /v1/sms/sender-ids with senderId (3–11 chars; letters/digits/spaces; at least one letter)
• DELETE /v1/sms/sender-ids/{id}
• POST /v1/sms/sender-ids/limit-request with requestedCap + reason

Sending: POST /v1/sms/messages now accepts from as either an SMS-enabled number or an approved sender ID.

Notes for sender IDs: text-only (no MMS), recipients can’t reply, and sends to US/CA/PR are blocked.

New responses to handle on POST /v1/sms/messages:
• 403 (gate: registration/MMS not supported/alpha to US-CA-PR)
• 429 (alphanumeric daily cap reached; code alphadailylimit_reached; resets midnight UTC)
• 404 now covers “no SMS-enabled number or sender ID matches from”.
July 21, 2026New Feature
Meta ads now support Reach & Frequency (RESERVED) buying via new R&F prediction + reservation endpoints.

You can request a quote (no spend, no entities created), reserve it to lock price/inventory, then create an ad using the reserved prediction.

New endpoints:
• POST /v1/ads/rf-predictions (create quote; provide date range + exactly one of budgetAmount or reach)
• GET /v1/ads/rf-predictions/{predictionId}
• POST /v1/ads/rf-predictions/{predictionId}/reserve (returns a NEW RESERVED predictionId + expiresAt)
• DELETE /v1/ads/rf-predictions/{predictionId} (cancel reservation)

POST /v1/ads/create adds:
• buyingType: AUCTION | RESERVED
• rfPredictionId (required when buyingType=RESERVED)

Notes: RESERVED uses the single-ad shape only (no creatives[], adSetId, existingCampaignId, or dynamicCreative). Budget/schedule/pricing come from the reservation; bid fields are ignored.
July 21, 2026New Feature
New Meta Ads endpoints are available for live reads, auditing, studies, finances, and asset upload.

You can now:
• Read a live Meta ad set (incl. learning phase) via GET /v1/ads/ad-sets/{adSetId} with accountId and optional fields (default includes learningstageinfo).
• Fetch account audit/activity log via GET /v1/ads/activity using accountId, adAccountId, optional since/until, optional objectId (client-side filter), plus paging limit/after.
• List A/B tests & lift studies via GET /v1/ads/studies with accountId, adAccountId, optional fields, and paging limit/after.
• Read ad account finances via GET /v1/ads/accounts/finance (balance, amountSpent, spendCap, fundingSource).
• Upload ad images from base64 via POST /v1/ads/images with accountId, adAccountId, imageBase64 (optional filename); returns image.hash and image.url.

Also updated:
• specialAdCategories now includes FINANCIALPRODUCTSSERVICES and ONLINEGAMBLINGAND_GAMING on POST /v1/ads/boost and POST /v1/ads/create.
• callToAction enums expanded on POST /v1/ads/create (and Meta creatives[].callToAction) to include values like ADDTOCART, APPLYNOW, BOOKNOW, ORDERNOW, INSTALLMOBILEAPP, USEAPP (plus others).
• POST /v1/ads/conversions: consent now affects Meta too — if adUserData or adPersonalization is DENIED, events are sent with Meta Limited Data Use (LDU); GRANTED (or omitted) uses default processing.
July 20, 2026Deprecation
New Meta Ads capabilities are available: ad previews, click-to-message ads, and click-to-call ads.

You can now render embeddable preview iframes before creating an ad via POST /v1/ads/preview (provide exactly one of existingCreativeId or creativeSpec) or for an existing ad via GET /v1/ads/{adId}/preview. Use formats (Meta ad_format values, max 10) to control placements.

Click-to-message ads are now unified under POST /v1/ads/messaging with destination:
• whatsapp
• messenger
• instagram_direct

Click-to-call ads are supported via POST /v1/ads/call using phoneNumber (E.164) and linkUrl.

POST /v1/ads/ctwa is now deprecated; use POST /v1/ads/messaging with destination: whatsapp (the old endpoint remains for back-compat).
July 20, 2026New Feature
Meta insights now supports action-level segmentation and attribution controls on both the sync and async insights APIs.

You can segment actions[] and control attribution windows/reporting time without changing endpoints.

New on GET /v1/ads/insights:
• actionBreakdowns
• actionAttributionWindows
• actionReportTime
• useUnifiedAttributionSetting

New on POST /v1/ads/insights/reports:
• actionBreakdowns
• actionAttributionWindows (array of strings)
• actionReportTime
• useUnifiedAttributionSetting

Meta ad creation also adds hand-built carousels via carouselCards on POST /v1/ads/create (2–10 cards, deterministic order).
July 20, 2026Improvement
Google Business batch reviews now supports server-side sorting via orderBy on POST /v1/accounts/{accountId}/gmb-reviews/batch.

This lets you fetch newest-updated reviews first (default) and stop paginating earlier when polling for recent changes.

Use orderBy:
• updateTime desc (default)
• rating
• rating desc

Also note: locationNames is now capped at maxItems: 50 per request (chunk larger sets).

Responses now clarify attribution: each locationReviews[].name is the LOCATION resource name (not the review resource name), and review includes review.name (full review resource name) plus reviewMediaItems (photos/videos).

Inbox reviews (GET /v1/inbox/reviews) now includes Google Business location attribution fields: locationId (bare id) and locationName (display name).
July 20, 2026New Feature
POST /v1/inbox/comments/{postId} now supports attaching an image when replying on Facebook via attachmentUrl.

This lets you publish a photo comment alongside the text reply. The image URL must be publicly accessible so Meta can fetch it.

New request field:
• attachmentUrl (uri) — Facebook only

If attachmentUrl is sent for non-Facebook platforms, the API now returns 400 (e.g. code PLATFORMNOTSUPPORTED).
July 19, 2026Improvement
POST /v1/ads/create now supports more TikTok controls and targeting.

You can create TikTok ads/campaigns in a paused state and use additional geo targeting fields for TikTok.

Key changes:
• status now applies to Meta and TikTok: ACTIVE | PAUSED (TikTok pauses campaign > ad group > ad)
• Geo targeting fields now support TikTok: cities, regions, metros

GET /v1/ads/targeting/search: TikTok geo searches now return mixed levels in one list (type can be country | region | city | district | metro), and geoType is not applied for TikTok results.
July 18, 2026Improvement
TikTok conversion ads created via POST /v1/ads/create now accept TikTok Events Manager event names in promotedObject.customEventType (auto-resolved to the underlying optimization_event code).

This makes it easier to configure goal=conversions without manually mapping to TikTok’s internal codes; note that ONWEBORDER is now deprecated.

Use promotedObject.customEventType with one of:
• SHOPPING, ONWEBCART, INITIATEORDER, FORM, ONWEBREGISTER, ONWEB_DETAIL

Applies to TikTok only; promotedObject.pixelId is still required for goal=conversions.
July 17, 2026New Feature
New Meta Ads insights endpoints are available: GET /v1/ads/insights, POST /v1/ads/insights/reports, GET /v1/ads/insights/reports/{reportRunId}.

You can now run a flexible live insights query that forwards your chosen Meta Graph parameters (fields/breakdowns/filtering) and returns rows in Meta’s raw shape; use the async report flow for long ranges or large accounts.

Sync query: GET /v1/ads/insights with accountId, objectId, optional level (ad/adset/campaign/account), fields, breakdowns, filtering, datePreset or fromDate+toDate, timeIncrement, limit, after.

Async flow:
• Submit: POST /v1/ads/insights/reports (body includes accountId, objectId, optional level, fields, breakdowns, filtering, datePreset or fromDate/toDate, timeIncrement (1-90 | monthly | all_days)) → returns reportRunId
• Poll: GET /v1/ads/insights/reports/{reportRunId} with accountId, limit, after → returns status and percentCompletion; when status is Job Completed, includes data + paging.

Meta only (facebook/instagram). Invalid field/breakdown combos return 400 with Meta’s error message.
July 17, 2026New Feature
Google Business Profiles: you can now assign a GBP location to a different profile by reusing an existing OAuth grant (no browser re-auth).

Use POST /v1/accounts/{accountId}/gmb-locations/assign with:
• profileId (target profile)
• selectedLocationId (e.g. locations/123)
• googleAccountId (optional, recommended for accounts with many locations)
Returns 409 if the target profile already has a Google Business connection.

Phone number purchasing now supports selecting a number type.

On POST /v1/phone-numbers/purchase you can pass numberType: local | mobile | national | toll_free. Availability/KYC can vary by (country, type).

Country/type discovery updates:
• GET /v1/phone-numbers/countries now includes inStock and types[] (per-type monthlyCents, needsKyc, whatsappAvailable, smsAvailable, callsAvailable, inStock)
• GET /v1/phone-numbers/availability adds optional numberType
• GET /v1/phone-numbers/kyc adds optional numberType

Facebook connect flow: POST /v1/connect/facebook/select-page can now return 409 with code RECONNECTACCOUNTMISMATCH when a force=true token-recovery re-auth lands on a different user/page than the existing connection.
July 16, 2026New Feature
Google Business reviews now include attached photos in review responses.

You can display/download reviewer-uploaded images and know how many photos are attached per review.

Added fields:
• GET /v1/accounts/{accountId}/gmb-reviews → reviews[].photoCount, reviews[].photos[] (each item: url)
• GET /v1/inbox/reviews → data[].photos[] (Google Business only; each item: url)

Notes: photoCount counts photos only (videos not counted). For non-Google platforms, photos is always an empty array.
July 16, 2026New Feature
Meta campaign updates now support a lifetime spend cap via platformSpecificData.spendCap on PUT /v1/ads/campaigns/{campaignId}.

This lets you set or remove a campaign-level spend cap without recreating the campaign.

Use:
• platformSpecificData.spendCap = number (sets cap)
• platformSpecificData.spendCap = null (removes cap; Meta rejects 0)

Meta ad set updates now accept post-launch delivery settings via platformSpecificData on PUT /v1/ads/ad-sets/{adSetId}.

Set any of:
• platformSpecificData.optimizationGoal
• platformSpecificData.billingEvent
• platformSpecificData.startDate, platformSpecificData.endDate
• platformSpecificData.promotedObject (e.g. pixelId, customEventType, pageId, applicationId, objectStoreUrl, customConversionId, productCatalogId, productSetId)

Also added: billingEvent (Meta only) on POST /v1/ads/create to explicitly set the ad set’s billing event (defaults to IMPRESSIONS).
July 16, 2026Breaking Change
Queue schedules: DELETE /v1/queue/slots now supports deleting all queues for a profile by omitting queueId.

This lets you clear a profile’s entire queue schedule in one call (previously queueId was required).

Use:
• profileId (required)
• queueId (optional) — omit to delete all queues

Sequences: POST /v1/sequences/{sequenceId}/enroll now returns per-contact outcomes.

Response now includes failed and results (array of { contactId, success, error }) in addition to enrolled.

Also: GET /v1/queue/slots expands all to true | false.
July 16, 2026Improvement
GET /v1/accounts/{accountId}/gmb-locations now supports a limit query parameter.

You can raise the maximum number of Google Business Profile locations returned (default was effectively 100) up to 500 to enumerate accounts with more than 100 locations.

Use limit:
• limit (integer, default 100, min 1, max 500)

Invalid values now return 400 (e.g. limit out of range).
July 16, 2026New Feature
YouTube demographics analytics now supports per-video audience breakdowns via videoId on GET /v1/analytics/youtube/demographics.

Pass videoId to fetch demographics for a single video (instead of the whole channel). Note: YouTube may return empty breakdowns for low-traffic videos due to signed-in view thresholds.

Use:
• videoId - YouTube video ID (scoped to the connected channel; otherwise 404)
• startDate - defaults to the video publish date (lifetime) when videoId is provided

Response adds (video mode only): videoId, title, publishedAt.

Also: Meta city targeting docs were updated to reflect that cities[].radius has a minimum (~17 km / 10 mi). Smaller values can produce a 0-size audience and fail at launch; use customLocations for tighter radius targeting.
July 16, 2026Improvement
GET /v1/connect/{platform}/ads now supports force to trigger a fresh OAuth flow even when an ads account is already connected.

This lets you proactively refresh stored tokens (the endpoint normally returns alreadyConnected: true based on account active state, not token liveness).

Use force=true to always receive authUrl; completing OAuth refreshes the stored token on the existing posting + ads accounts in place.

New param: force (true/false, default false)
July 15, 2026Breaking Change
SMS carrier registration status now includes additional lifecycle states in registration APIs.

You may now see pre-submission review and deactivation states when listing, fetching, or reusing registrations, which can affect clients that assume only the previous 3 statuses.

Updated status enum:
• pending, approved, rejected, requested, changes_requested, deactivated

Applies to:
• GET /v1/sms/registrations
• GET /v1/sms/registrations/{id}
• POST /v1/phone-numbers/{id}/sms/reuse-registration

Also added to POST /v1/sms/registrations request body:
• resubmitRequestId (resubmit an existing registration returned for changes)
• wizardValues (optional raw wizard answers; can be omitted by API integrators)
July 15, 2026Improvement
conversions in ads metrics now includes non-Meta platforms.

You can now rely on conversions for more platforms when charting or aggregating performance (instead of always getting 0 outside Meta).

Applies to:
• GET /v1/ads/timeline (rows[].conversions)
• AdMetrics.conversions

New platform behavior:
• Meta: events matching the campaign optimization goal
• Google: tracked conversions
• X / LinkedIn: reported website/lead conversions (added 2026-07)
July 15, 2026Improvement
GET /v1/sms/registrations now supports includeDeactivated (boolean).

Deactivated (terminated) registrations are hidden by default; pass includeDeactivated=true to include them in the results.

New query param:
• includeDeactivated - true to include deactivated registrations (default: hidden)
July 15, 2026New Feature
New endpoint: DELETE /v1/sms/registrations/{id} to deactivate an SMS brand/campaign registration.

This terminates the carrier registry campaign so recurring monthly campaign fees stop; numbers under the registration can no longer send texts (receiving is unaffected). Deactivation is irreversible and the operation is idempotent.

Key parameter: id (path, string)

Response: 200 returns status = deactivated (enum: deactivated)
July 15, 2026New Feature
LinkedIn now supports Thought Leader ads on POST /v1/ads/create via platformSpecificData.thoughtLeader.

This lets you create a LinkedIn ad that references an existing post (share/ugcPost) as the creative, while still using the normal /v1/ads/create flow for targeting/bidding/schedule.

Set:
• platformSpecificData.thoughtLeader.postUrn (pattern: urn:li:share:\d+ or urn:li:ugcPost:\d+)

Notes:
• Mutually exclusive with other LinkedIn creative sources in platformSpecificData (e.g. carousel, document, conversation, etc.)
• When using thoughtLeader, you don’t need headline/body/imageUrl/organizationId for the creative (the referenced post provides them).
July 15, 2026New Feature
New LinkedIn-only Ads helpers are available:

• Suggested bid & budget bounds via POST /v1/ads/targeting/bid-pricing
Returns suggested bid + allowed bid limits and daily budget bounds for a targeting spec (use before POST /v1/ads/create).
Key request fields: accountId, adAccountId, spec, optional campaignType (TEXTAD/SPONSOREDUPDATES/SPONSOREDINMAILS), bidType (CPM/CPC/CPV), matchType (EXACT/AUDIENCEEXPANDED), dailyBudget.
Response: available and pricing (may be null).

• Impressions/clicks/spend forecast via POST /v1/ads/targeting/supply-forecast
Forecasts metrics for a targeting spec over a future time range.
Key request fields: accountId, adAccountId, spec, timeRangeStart, timeRangeEnd, plus either dailyBudget or totalBudget. For manual bidding, set competingBid with bidType (CPM/CPC/CPV) and amount; for auto-bidding, set optimizationTarget.
Response: available and forecast series with metricType and granularity (DAILY/SEVENDAY/THIRTYDAY/CUSTOM).

For non-LinkedIn accounts, both endpoints return available: false (not an error).
July 15, 2026New Feature
New endpoint POST /v1/sms/opt-in-proof uploads SMS opt-in proof (screenshot/PDF) and returns a public URL you can include in a campaign’s messageFlow for carrier review.

Send multipart/form-data with file (PNG/JPG/WebP/GIF/PDF, max 4MB).

Response: { "url": "..." }

Errors: 422 unsupported type/too large, 401 unauthorized. Alias: /v1/sms/registrations/{id}/opt-in-proof.
July 15, 2026New Feature
POST /v1/sms/messages now supports scheduling via sendAt.

Set sendAt (ISO 8601 date-time with offset) to queue an SMS/MMS for a future time; it must be in the future. The message is queued and the message.delivered webhook fires when it actually sends.

New field: sendAt
July 15, 2026Improvement
SMS carrier registration now accepts optional policy URLs on 10DLC campaigns via POST /v1/sms/registrations.

You can include privacy/terms links in the campaign object to help carrier reviewers validate compliance.

New campaign properties:
• privacyPolicyLink
• termsAndConditionsLink

Also, embeddedLink and embeddedPhone now document auto-derivation from your sample messages when omitted.
July 15, 2026New Feature
New endpoint: POST /v1/sms/registrations/{id}/opt-in-proof uploads an SMS opt-in form screenshot/PDF and returns a public URL you can include in an appeal.

Use it to host proof of consent when submitting POST /v1/sms/registrations/{id}/appeal (add the returned URL to your messageFlow).

Request: multipart/form-data with file (PNG/JPG/WebP/GIF/PDF, max 4MB)
Response: { "url": "..." }
Errors: 404 registration not found, 422 unsupported type/too large
July 15, 2026New Feature
New endpoint: POST /v1/accounts/{accountId}/pinterest-boards to create Pinterest boards on a connected Pinterest account.

The returned board.id can be used immediately as platformSpecificData.boardId when creating a Pinterest post.

Request body:
• name (required)
• description
• privacy: PUBLIC | PROTECTED | SECRET (default PUBLIC)

Response (201): board with id, name, description, privacy, url.
July 15, 2026New Feature
Website retargeting audiences are now supported via a new audience type: website_retargeting (LinkedIn only).

You can now create and list these audiences using type=website_retargeting on POST /v1/ads/audiences and filter them via GET /v1/ads/audiences.

When creating, set:
• type: website_retargeting
• matchRules: array of URL patterns (matchType: EXACT | STARTSWITH | CONTAINS | ENDSWITH, plus matchValue)
July 14, 2026New Feature
Custom audiences now support new types: company_list and engagement on /v1/ads/audiences.

You can list and filter these via GET /v1/ads/audiences using type = customerlist | companylist | engagement | website | lookalike | saved_targeting.

To create them via POST /v1/ads/audiences:
• type=engagement (LinkedIn): requires sourceType (VIDEOADS | LEADGENFORMS | ORGANIZATIONPAGES | EVENTPAGES | SINGLEIMAGE_ADS), plus trigger, lookbackDays (30 | 60 | 90 | 180 | 365), and engagementSources.
• type=company_list (LinkedIn): requires companies (array of company rows like name, domain, website, linkedinPageUrl).
July 14, 2026New Feature
LinkedIn firmographic breakdowns are now supported in analytics via the breakdowns query param on GET /v1/ads/{adId}/analytics and GET /v1/ads/campaigns/{campaignId}/analytics.

This lets you request LinkedIn aggregated firmographic segments for the requested date range (note: LinkedIn data is aggregated over the whole range, delayed 12–24h, and segments with <3 events are omitted).

Use breakdowns with LinkedIn dimensions:
• jobtitle, jobfunction, seniority, industry
• company, company_size
• country, region

LinkedIn breakdown rows include the raw pivot value plus a resolved name.
July 14, 2026New Feature
POST /v1/ads/create now supports LinkedIn Jobs ads via a new goal job_applicants.

This lets you create Dynamic Jobs Ads that promote open roles (requires a Company Page with active job postings).

Use:
• goal: job_applicants
• platformSpecificData.jobs with headline and buttonLabel (each takes exactly one of preApproved or custom)

Also added for LinkedIn-only creatives on platformSpecificData:
• conversation (Conversation Ads) with subject, firstMessageId, and messages (buttons use exactly one of nextMessageId or url).
July 14, 2026New Feature
POST /v1/ads/create now supports more LinkedIn ad formats via platformSpecificData.

You can create LinkedIn-only creatives (beyond single image/video) by setting one of these mutually-exclusive options:
• platformSpecificData.carousel (2–10 cards)
• platformSpecificData.document (PDF/PPT/DOC)
• platformSpecificData.spotlight (dynamic spotlight)
• platformSpecificData.follower (dynamic follower)
• platformSpecificData.textAd (right-rail text ad)
• platformSpecificData.event (promote an existing event)

Notes:
• Carousel cards use cards[].imageUrl and optionally cards[].headline / cards[].landingUrl (falls back to ad-level linkUrl).
• Document ads use document.url + document.title.
• Spotlight ads use spotlight.headline + spotlight.callToAction + spotlight.landingUrl.
• Follower ads use follower.callToAction enum: VISITORGANIZATIONCOMPANYPAGE, VISITORGANIZATIONLIFEPAGE, VISITORGANIZATIONJOBSPAGE, VISITORGANIZATIONCAREERSPAGE.

Also clarified: on LinkedIn, imageUrl + linkUrl publishes an ARTICLE-content creative (with longHeadline as the description).
July 14, 2026New Feature
Sole-prop 10DLC registrations can now re-send the OTP via POST /v1/sms/registrations/{id}/resend-otp.

Use this when the original code expired or never arrived. It only works while the registration is pending and awaiting OTP, and is rate-limited to 1 request/min.

Request: id (path)
Response: { sent: boolean }
Errors: 400 not awaiting OTP, 404 not found, 429 wait 1 minute

Port-in updates:
• POST /v1/phone-numbers/port-in: endUser.accountNumber is now required (and endUser.countryCode is restricted to US/CA)
• POST /v1/phone-numbers/port-in/check: response adds lineType (when known) to indicate if a transfer PIN is required at submit

10DLC campaign submissions:
• POST /v1/sms/registrations: campaign.optinMessage, campaign.optoutMessage, and campaign.helpMessage are now optional; when omitted, compliant templates are generated.
July 14, 2026New Feature
LinkedIn ads now support campaign bidding & delivery controls via platformSpecificData on POST /v1/ads/create and POST /v1/ads/boost.

This lets you set LinkedIn-specific options (manual bids, cost model, audience expansion, Audience Network, etc.) while keeping the platform inferred from accountId (sending options for a different platform returns 400).

Use platformSpecificData (LinkedIn only):
• costType: CPM | CPC | CPV
• unitCost (manual bid; requires costType)
• optimizationTargetType (pass-through)
• creativeSelection: OPTIMIZED | ROUND_ROBIN
• audienceExpansionEnabled, offsiteDeliveryEnabled, connectedTelevisionOnly
July 14, 2026New Feature
Comment-to-DM automations now support message variations for random rotation of DM text and public comment replies.

This lets you provide multiple alternate texts so repeat triggers don’t always send identical messages.

New optional fields:
• Create/Update: dmMessageVariations (up to 5), commentReplyVariations (up to 5)
• Returned on list/detail/update responses: dmMessageVariations, commentReplyVariations

Rotation behavior:
• DM text is picked at random from [dmMessage, ...dmMessageVariations]
• Public reply is picked at random from [commentReply, ...commentReplyVariations]

Applies to: POST /v1/comment-automations, PATCH /v1/comment-automations/{automationId}, GET /v1/comment-automations, GET /v1/comment-automations/{automationId}.
July 13, 2026Improvement
Analytics responses now include media accessibility alt text via mediaItems[].altText.

Use this to display or audit the alt text set on each media item when available.

New field:
• mediaItems[].altText (string)

Included in:
• AnalyticsSinglePostResponse
• AnalyticsListResponse
July 13, 2026Improvement
GET /v1/inbox/reviews now returns Google Business review photo info via hasPhotos and photoCount.

This lets you detect and display reviews with attached photos (Google Business only; other platforms return hasPhotos=false and photoCount=0).

New fields:
• hasPhotos
• photoCount

GET /v1/ads/accounts now includes Meta’s minimum daily budget via minimumDailyBudget.

Use this to validate budget inputs against account-level minimums (field may be absent if the connected token can’t read it).

New field:
• minimumDailyBudget
July 13, 2026New Feature
Meta ads now support setting EU DSA (Article 26) defaults at the ad-account level, so EU-targeted calls can omit per-ad dsaBeneficiary/dsaPayor when defaults are configured.

Set defaults once via PATCH /v1/ads/accounts with accountId, adAccountId, defaultDsaBeneficiary, optional defaultDsaPayor (if omitted, payor defaults to beneficiary).

Read current defaults via GET /v1/ads/dsa-defaults (accountId, adAccountId), and fetch Meta-provided suggestions via GET /v1/ads/dsa-recommendations (accountId, adAccountId).

POST /v1/ads/create, POST /v1/ads/boost, and POST /v1/ads/ctwa now treat dsaBeneficiary/dsaPayor as optional for EU targeting when the Meta ad account has defaults set (Meta accounts only; other platforms return 400).
July 13, 2026Improvement
POST /v1/phone-numbers/port-in now supports portType to specify whether you’re porting the entire losing account or only some numbers.

Use portType to avoid ambiguity on ports where the losing account keeps some numbers.

New/updated request behavior:
• portType: full | partial (default: full)
• endUser.billingPhoneNumber: now documented as the number on the losing carrier’s bill; defaults to the ported number itself on single-number orders
• focDatetimeRequested: when omitted, defaults to ~1 week out (shifted off weekends)
July 11, 2026New Feature
New endpoint: PUT /v1/ads/{adId}/status to pause/resume a single ad.

This is ad-scoped only (does not affect the parent ad set/campaign), and is a thin wrapper around updating the ad status.

Set status to active or paused. {adId} accepts Zernio _id, Meta platformAdId, or the creative’s effective story/media IDs.

Response includes updated (1/0) and skipped (1/0); message is returned when the change is skipped (terminal status or already in target state).
July 11, 2026New Feature
conversation.started webhooks now include SMS conversations via conversation.platform.

This lets you receive “new conversation started” events for SMS in the same platform-agnostic subscription.

New enum value:
• conversation.platform: sms (in addition to existing platforms)
July 11, 2026New Feature
WhatsApp template sends via POST /v1/inbox/conversations now support overriding a media-header template asset per message using headerMedia.

This lets you use a single approved template with an image/video/document header while sending a different asset for each recipient (e.g. a unique invoice PDF), instead of always sending the template’s approved sample asset.

Set headerMedia (JSON body only):
• type: image | video | document (must match the template header type)
• Provide exactly one of link (public URL) or id (Meta media id)
• Optional: filename (document only)
July 10, 2026Improvement
GET /v1/ads/accounts now returns Meta ad account usability flags: selectable and unusableReason.

This lets you filter out ad accounts that can’t currently create/run ads and show a user-friendly reason in your UI.

New fields in accounts[] (Meta only):
• selectable (boolean) — whether the account can create/run ads now
• unusableReason (string|null) — reason when selectable is false; null when selectable

On non-Meta platforms these fields are absent (treat selectable as true).
July 10, 2026Improvement
GET /v1/inbox/conversations/search now searches by contact identity (participant name/username/phone) in addition to message text, returning matching conversations once (message matches included when present).

Contact-identity matches are case-insensitive substrings; they return matchCount=0 and matches=[]. Note: identity matching is not applied when direction is set (incoming | outgoing).

Also updated:
• Searchable platforms now include Twitter/X and Reddit; Bluesky is live-fetched and appears in meta.accountsSkipped
• Response adds conversation.lastMessage (most recent message preview)
July 10, 2026Improvement
Meta placements now support delivering ads on WhatsApp by allowing whatsapp in placements.publisherPlatforms.

This lets you explicitly include WhatsApp inventory when using manual placements (instead of relying on automatic placements).

Set placements.publisherPlatforms to include:
• whatsapp (alongside facebook, instagram, threads, messenger, audience_network)

Applies to:
• POST /v1/ads/create
• POST /v1/ads/ctwa
July 10, 2026Improvement
POST /v1/ads/ctwa now supports manual ad placements via placements.

This lets you restrict CTWA ad delivery to specific Meta surfaces/devices instead of using automatic placements (Meta may still enforce eligibility/co-selection rules and return actionable errors).

Use placements with:
• publisherPlatforms: facebook | instagram | threads | messenger | audience_network
• facebookPositions, instagramPositions, messengerPositions, audienceNetworkPositions, threadsPositions, whatsappPositions
• devicePlatforms: mobile | desktop
July 10, 2026Improvement
GET /v1/usage now includes estimated tax via a new tax field on UsageMetering.

This lets you show an estimated tax amount (computed with Stripe Tax) on top of the window’s net spend (totals.total). tax is null when no billing address is on file, the total is ≤ 0, or the estimate fails.

New fields:
• tax.taxUsd — estimated tax in USD (added on top of totals.total)
• tax.ratePercent — combined rate percentage (nullable)
• tax.jurisdictionLabel — human label like “ES VAT” (nullable)
• tax.reverseCharge — true for EU/UK B2B reverse charge (0 tax added)
July 10, 2026Improvement
Webhooks now support additional event subscriptions for conversations, calls, leads, and WhatsApp.

You can subscribe to these new events via POST /v1/webhooks/settings or PUT /v1/webhooks/settings:
• conversation.started
• call.received, call.ended, call.failed, call.permission_request
• lead.received
• whatsapp.automatic_event

SMS carrier registrations (10DLC) now accept campaign.subUsecases on POST /v1/sms/registrations (2–5 values): 2FA, ACCOUNTNOTIFICATION, CUSTOMERCARE, DELIVERYNOTIFICATION, FRAUDALERT, HIGHEREDUCATION, MARKETING, POLLINGVOTING, PUBLICSERVICEANNOUNCEMENT, SECURITY_ALERT.

For rejected registrations, GET /v1/sms/registrations/{id} may now include campaignContent (messageFlow, sample1, sample2), and POST /v1/sms/registrations/{id}/appeal can include corrected messageFlow/sample1/sample2 along with appealReason.
July 9, 2026New Feature
New billing snapshot endpoint: GET /v1/billing.

It returns the billing “statement” view (plan, current cycle, balance/credits, spend caps, and payment/access status). Use this for billing state; use GET /v1/usage for usage/metering.

Key response blocks:
• plan (includes isUsageBased)
• period (start/end may be null on Stripe)
• balance (null for Stripe; populated for Metronome)
• caps (e.g. xSpendUsedCents, xSpendLimitCents)
• status (e.g. hasAccess, suspended, openInvoiceUrl, declineReason)

Also: GET /v1/usage is now dual-mode.
• Default (no metering params): same snapshot as before
• Metering mode (billed spend by product) when passing any of: range, granularity, from, to

Metering params:
• range: cycle | prev-cycle | 7d | 14d | 30d | 3mo | 12mo | custom
• granularity: day | month | total
• from/to required when range=custom

Stripe accounts calling metering mode receive { "supported": false }.
July 9, 2026New Feature
POST /v1/whatsapp/business-profile/photo now accepts an image via URL in addition to multipart upload.

You can either upload bytes with multipart/form-data (file) or send application/json with url; we fetch the image server-side and upload the bytes to Meta.

New JSON body option:
• accountId
• url (public https image URL; must resolve directly without redirects)

New response to handle:
• 422 when the profile photo is locked for WhatsApp coexistence numbers (must be managed in the WhatsApp Business app).
July 9, 2026Breaking Change
POST /v1/sms/registrations now enforces additional required fields for 10DLC registrations.

This can break existing integrations that submit brand/campaign without the newly-required fields.

For brand, these fields are now required: website, street, city, state, postalCode (in addition to existing required fields).

For campaign, sample2 is now required and must be distinct from sample1.

Webhook post events now echo back your post’s free-form metadata (when provided at create time) in:
• WebhookPayloadPost.post.metadata
• WebhookPayloadPostPlatform.post.metadata
July 9, 2026Breaking Change
Google Business Profile posts: offer.offerType has been removed from GoogleBusinessPlatformData.

If you were sending offerType (previously OFFER or BUYONEGET_ONE), remove it from your payloads and use the remaining offer fields instead:
• redeemOnlineUrl
• termsConditions
• couponCode
July 8, 2026Breaking Change
The supported breakdowns values were reduced on analytics endpoints.

If you request breakdowns that are no longer supported, your call may fail (400) or no longer return those breakdowns.

Affected endpoints:
• GET /v1/ads/{adId}/analytics: Meta breakdowns now limited to age, gender, country, publisherplatform, deviceplatform, region (removed: platformposition, impressiondevice, videoasset, imageasset, bodyasset, titleasset; also removed aliases placement and singular breakdown)
• GET /v1/ads/campaigns/{campaignId}/analytics: Meta breakdowns now limited to age, gender, country, publisherplatform, deviceplatform, region, platformposition, impressiondevice, videoasset, imageasset, bodyasset, titleasset (removed aliases placement and singular breakdown)
July 8, 2026New Feature
GET /v1/analytics now supports a new metric: follows.

You can read analytics.follows in responses (Instagram feed posts and stories only: organic accounts that started following from this post; 0 for reels and other platforms).

You can also sort analytics results by sortBy = follows (enum now includes: date, engagement, impressions, reach, likes, comments, shares, saves, clicks, views, follows).
July 8, 2026Breaking Change
GET /v1/ads/{adId}/analytics and GET /v1/ads/campaigns/{campaignId}/analytics now validate breakdown dimensions and will return 400 for unknown values.

This prevents silent/partial breakdown results and makes invalid breakdowns requests fail fast with a supported-values list.

Updates:
• breakdowns: unknown dimensions now return 400 (instead of being ignored)
• placement is accepted as an alias for publisher_platform
• breakdown (singular) is also accepted

New Meta breakdown options include: platformposition, impressiondevice, videoasset, imageasset, bodyasset, titleasset.
July 8, 2026New Feature
You can now update saved_targeting audiences via PUT /v1/ads/audiences/{audienceId}.

This lets you change an audience’s name, description, or fully replace its spec (no merge). Uploaded/derived audiences aren’t updatable and return 422.

Request body (send at least one): name, description, spec

Also: GET /v1/ads/audiences now returns accountId for type=savedtargeting items, and DELETE /v1/ads/audiences/{audienceId} clarifies that savedtargeting deletes only the local record (it exists only on Zernio).
July 7, 2026Improvement
Ads reporting endpoints may now return 202 when part of the requested date range isn’t ingested yet.

When this happens, the response includes currently-available data plus backfillPending: true, and a Retry-After header with the recommended poll interval. Repeat the same request until it returns 200 with the full range.

Applies to:
• GET /v1/ads
• GET /v1/ads/tree
• GET /v1/ads/timeline
• GET /v1/ads/campaigns/{campaignId}/analytics
• GET /v1/ads/{adId}/analytics

Also, POST /v1/posts 403 responses now include a stable code to distinguish causes:
• ACCOUNT_DISCONNECTED
• PROFILEOVERLIMIT
July 7, 2026New Feature
WhatsApp inbox messages now support commerce interactive messages (products, product lists, catalog messages, and carousels) via interactive on POST /v1/inbox/conversations/{conversationId}/messages.

This lets you send catalog-driven messages and receive cart submissions / product inquiries back in webhooks.

New interactive.type values:
• product, productlist, catalogmessage, carousel

Webhook message.received metadata adds:
• metadata.order (cart submitted from commerce messages)
• metadata.referredProduct (product inquiry context)

Note: interactive messages are session messages (must be sent within WhatsApp’s 24-hour window).
July 7, 2026Improvement
GET /v1/inbox/mentions now returns additional LinkedIn author details in each mention.

You can display richer mention attribution (when available) without extra profile resolution on your side.

New fields:
• authorName - display name resolved from authorUrn (can be null)
• authorUsername - LinkedIn vanity name (can be null)
• authorPicture - author profile picture URL (can be null; CDN URLs may expire, so fetch promptly)
July 6, 2026New Feature
Meta ad metrics now include video performance fields in AdMetrics (and therefore AdDailyMetrics).

You can track video starts, thruplays, 30s views, completion percentiles, and average watch time for hook/hold/drop-off analysis. These fields are Meta video ads only (0 for non-video ads and other platforms).

New fields:
• videoPlayActions
• video30SecWatchedActions
• videoThruplayWatchedActions
• videoP25WatchedActions, videoP50WatchedActions, videoP75WatchedActions, videoP95WatchedActions, videoP100WatchedActions
• videoAvgTimeWatchedActions

Note: in AdDailyMetrics, videoAvgTimeWatchedActions is recomputed per day; don’t sum or plain-average it across days (the range value is play-weighted).
July 6, 2026New Feature
New endpoint: GET /v1/inbox/conversations/search.

You can now search message text across stored inbox conversations and get back matching conversations with up to 3 most-recent matching messages per conversation (useful for finding threads by topic or filtering by sent/received).

Key params:
• query (required, 2–200 chars)
• direction: incoming | outgoing
• platform: facebook | instagram | telegram | whatsapp | sms
• profileId, accountId
• limit (1–50, default 20), cursor

Response includes data[].conversation, matchCount, matches (up to 3), plus pagination and meta.accountsSkipped for connected accounts on live-fetched platforms that can’t be searched.

Requires Inbox addon (403 if missing).
July 6, 2026Improvement
Inbox webhook account context now includes optional profileId in InboxWebhookAccount.

Use profileId (workspace/profile ID) to route or filter inbox webhooks by workspace. It may be omitted for the shared WhatsApp sandbox account or when the account has no resolvable profile.
July 6, 2026New Feature
New API surfaces for Usage, Calls, Voice (PSTN), SMS, and Phone Numbers are now available, including a unified call history and a new usage “hub”. Several older WhatsApp-prefixed paths are now deprecated aliases of the new canonical endpoints.

Usage snapshot + breakdowns:
• GET /v1/usage (replaces GET /v1/usage-stats; same contract)
• GET /v1/usage/calls with optional groupBy: day | number | channel and filters since, until, channel (whatsapp | pstn), number
• GET /v1/usage/sms with optional groupBy: day | number and filters since, until, number

Unified call history (WhatsApp + PSTN) + recordings:
• GET /v1/calls filters: channel (whatsapp | pstn), status (ringing | answered | ended | failed), direction (inbound | outbound), number, search, cursor pagination via before + nextCursor
• GET /v1/calls/{id}
• GET /v1/calls/{id}/recording (default 302; as=json returns { url })

WhatsApp call history improvements:
• GET /v1/whatsapp/calls now supports cursor pagination via before and returns nextCursor
• POST /v1/whatsapp/calls now supports idempotent retries via Idempotency-Key

PSTN Voice calling (new):
• POST /v1/phone-numbers/{id}/voice configure inbound routing (forwardTo), recording/transcription, voicemail, business hours, IVR, blocklist
• DELETE /v1/phone-numbers/{id}/voice
• Call control: POST /v1/voice/calls (supports Idempotency-Key), GET /v1/voice/calls, GET /v1/voice/calls/{id}, POST /v1/voice/calls/{id}/end, POST /v1/voice/calls/{id}/transfer, GET /v1/voice/calls/{id}/recording (as=json), GET /v1/voice/calls/estimate
• Browser softphone handshake: POST /v1/voice/calls/web then POST /v1/voice/calls/web/dial

SMS (new):
• POST /v1/sms/messages (supports Idempotency-Key; MMS via mediaUrls)
• GET /v1/sms/lookup (carrier + line type mobile | landline | voip | toll-free | unknown)
• Opt-outs export: GET /v1/sms/opt-outs with format: json | csv
• US carrier registration flows: GET/POST /v1/sms/registrations, GET /v1/sms/registrations/{id}, POST /v1/sms/registrations/{id}/verify-otp, POST /v1/sms/registrations/{id}/appeal, POST /v1/sms/registrations/share

Phone Numbers (new canonical resource):
• GET /v1/phone-numbers, GET /v1/phone-numbers/{id}, DELETE /v1/phone-numbers/{id}
• Purchase + inventory: POST /v1/phone-numbers/purchase, GET /v1/phone-numbers/countries, GET /v1/phone-numbers/available, GET /v1/phone-numbers/availability
• Regulated KYC: GET/POST /v1/phone-numbers/kyc, POST /v1/phone-numbers/kyc/upload-document, POST /v1/phone-numbers/kyc/validate-address, POST /v1/phone-numbers/kyc/share, POST /v1/phone-numbers/kyc/review-packet, remediation via GET/POST /v1/phone-numbers/{id}/remediate
• Port-in: GET /v1/phone-numbers/port-in, POST /v1/phone-numbers/port-in, POST /v1/phone-numbers/port-in/check, POST /v1/phone-numbers/port-in/documents, DELETE /v1/phone-numbers/port-in/{id}

WhatsApp Calling config is now number-keyed:
• New canonical sub-resource: GET/POST/PATCH/DELETE /v1/phone-numbers/{id}/whatsapp/calling
• Existing /v1/whatsapp/phone-numbers/{id}/calling endpoints are deprecated aliases

Deprecated aliases (no immediate break, but new integrations should switch):
• GET /v1/usage-stats → GET /v1/usage
• Many /v1/whatsapp/phone-numbers/* paths → corresponding /v1/phone-numbers/* paths
July 4, 2026Improvement
Webhooks now support TikTok URL backfill via the new event post.tiktok.url_resolved.

This fires when a published TikTok post’s public URL is resolved/backfilled, using the same payload shape as post.platform.published/post.platform.failed (see WebhookPayloadPostPlatform).

Subscribe by including post.tiktok.url_resolved in events when calling POST /v1/webhooks/settings or PUT /v1/webhooks/settings.

Also clarified for PUT /v1/posts/{postId}: to promote a draft to scheduled you must send isDraft: false together with scheduledFor (or publishNow: true / queuedFromProfile). If isDraft is omitted, the post keeps its current draft status.
July 3, 2026Improvement
Webhook payloads now include account.accountId (same value as account.id) as a canonical field for filtering/routing events.

This makes it easier to consistently filter all webhook events by a single field (e.g. route staging vs production by account) while keeping account.id for backward compatibility.

New field:
• account.accountId

Added to these webhook payloads:
• comment.received
• lead.received
• review.new
• review.updated
• post.external.created, post.external.updated, post.external.deleted
• inbox webhook payloads via InboxWebhookAccount
July 3, 2026Improvement
Webhook post events now include the connected social account per platform target via post.platforms[].accountId.

This lets you route/attribute webhook deliveries by the specific SocialAccount used for each platform publish (useful when a single post spans multiple accounts).

New field:
• WebhookPayloadPost.post.platforms[].accountId
• WebhookPayloadPostPlatform.post.platforms[].accountId (on post.platform.published / post.platform.failed; see also the top-level account block there)
July 3, 2026Improvement
WhatsApp KYC form spec now returns an optional audience per requirement in GET /v1/whatsapp/phone-numbers/kyc.

This lets you show/collect only the fields that apply to the end-user type (business vs individual) and avoid submitting both sets of requirements.

New field: fields[].audience = business | individual | null (applies to both).
July 3, 2026Improvement
GET /v1/connect/pending-data now supports Pinterest board selection in headless OAuth flows.

For Pinterest (in addition to LinkedIn orgs and Snapchat profiles), the OAuth redirect returns pendingDataToken and you can fetch the full selection payload via token.

Use token (query) from the redirect, and read selectionType (enum: organizations, pages, boards, locations, profiles) to determine which selection array to consume (e.g. boards for Pinterest).
July 3, 2026Improvement
Workflow send_message nodes now allow omitting interactive.type; it will be inferred from the interactive payload shape.

This makes interactive message configs simpler, but payloads with neither type nor an inferable shape are now rejected.

Applies to send_message.config with messageType = interactive (WhatsApp-only).
July 3, 2026New Feature
POST /v1/invite/tokens now supports inviting users with the new admin role via role.

Use role=admin to grant team-management permissions (invite/remove members, change roles and access) without billing, ownership transfer, or account deletion permissions.

role enum is now: admin, member, billing_admin, viewer.

Note: readOnly (deprecated) can’t be combined with role=billing_admin or role=admin.
July 1, 2026Improvement
WhatsApp document sends via URL now support setting the displayed filename using attachmentName on POST /v1/inbox/conversations/{conversationId}/messages.

This lets you control the recipient-visible file name for attachmentUrl document messages (otherwise WhatsApp derives it from the URL and may show “Untitled”).

Use:
• attachmentType: file
• attachmentUrl: (public URL)
• attachmentName: e.g. Report.pdf

Applies to WhatsApp only; ignored for image/video/audio and for multipart binary uploads.
July 1, 2026New Feature
New endpoint POST /v1/posts/sync-external lets you sync/verify external (non-Zernio) posts on demand, so a just-published post can be retrieved within seconds instead of waiting for the background sync.

Use it to confirm a user-submitted post exists by sending accountId plus either url or postId.

Request body:
• accountId (required)
• url (optional)
• postId (optional)

Response:
• synced.postsFound, synced.postsSynced, synced.skipped
• If url/postId provided: found + post (or null)
• If no locator provided: posts (recent external posts)

Returned posts use ExternalPostSummary (includes platform, platformPostId, platformPostUrl, publishedAt, and basic analytics like likes/comments).
June 30, 2026New Feature
WhatsApp Business Profile now supports managing business usernames via new endpoints.

You can fetch the current username + approval state, claim/transfer a username, release it, and retrieve available suggestions.

New endpoints:
• GET /v1/whatsapp/business-profile/username (accountId query) → returns username (nullable) and status (approved/reserved/none)
• POST /v1/whatsapp/business-profile/username body: accountId, username, optional transferAction (none/force_transfer)
• DELETE /v1/whatsapp/business-profile/username body: accountId
• GET /v1/whatsapp/business-profile/username/suggestions (accountId query) → returns suggestions[]

On username claim failures, expect error codes like whatsappusernameunavailable, whatsappusernameineligible, or whatsappusernametransfer_required.
June 30, 2026New Feature
New endpoint GET /v1/inbox/mentions is available to list mentions of your connected organization accounts delivered via platform webhooks.

Currently supports LinkedIn organization mentions and requires the Inbox addon.

Key query params:
• accountId (filter by social account ID)
• profileId (filter by profile ID)
• sortOrder (asc | desc, default desc)
• limit (1–100, default 25)
• cursor (pagination)

Response includes data items with id, platform (linkedin), content, permalink, authorUrn, organizationalEntity, publishedAt, plus pagination (hasMore, cursor).
June 30, 2026New Feature
New endpoint GET /v1/ads/campaigns/{campaignId}/analytics returns campaign-level analytics in one call (summary metrics, daily[] timeline, and optional Meta demographic breakdowns).

Use query params fromDate, toDate (defaults to last 90 days; max 730 days) and optional platform to disambiguate IDs. For Meta-only breakdowns, pass breakdowns as a comma-separated list: age, gender, country, publisherplatform, deviceplatform, region, platformposition, impressiondevice, videoasset, imageasset, bodyasset, titleasset.

GET /v1/ads/tree now supports daily trends in the same response: set timeIncrement=1 to add daily[] metrics series, and control where it appears with dailyLevel = campaign (default) | adset | ad.
June 29, 2026New Feature
Instagram Trial Reels are now identifiable per platform target in post responses.

When an Instagram reel is created as a Trial (via platformSpecificData.trialParams), the corresponding PlatformTarget may include:
• isTrialReel (boolean) — present and true only for trial reels
• trialGraduationStrategy — MANUAL | SS_PERFORMANCE (present only when isTrialReel is true)

Use these fields to segment trial reels in analytics; note this reflects creation-time intent (Instagram doesn’t expose a readable trial flag).
June 26, 2026Breaking Change
Removed endpoint GET /v1/accounts/{accountId}/posts.

This endpoint previously returned recent posts for a connected social account by calling the platform API directly (Facebook, Instagram, X/Twitter, Bluesky, Threads, YouTube, LinkedIn, Reddit, TikTok, Pinterest).

If you were using YouTube filtering, note that excludeUnlisted is no longer available via this route.
June 26, 2026Breaking Change
New endpoint GET /v1/accounts/{accountId}/posts lists recent posts for a connected social account by calling the platform API directly (Facebook, Instagram, X/Twitter, Bluesky, Threads, YouTube, LinkedIn, Reddit, TikTok, Pinterest).

You can optionally filter YouTube results to avoid returning internal/unlisted content with excludeUnlisted.

Use:
• accountId (path)
• excludeUnlisted (query, boolean; YouTube only)

Also note a targeting spec change: TargetingSpec.cities[].distanceUnit is now TargetingSpec.cities[].distanceunit (with radius still supported). excludedLocations now supports excluded cities with optional radius + distanceunit, plus excluded places, neighborhoods, and excluded customLocations.
June 23, 2026New Feature
Instagram posts now support AI-generated media self-disclosure via isAiGenerated in platformSpecificData.

When isAiGenerated is true, Instagram labels the post as containing AI-generated media (this is for media, not AI-written captions).

Set:
• isAiGenerated (boolean, default false)

Applies to feed posts, Reels, Stories, and carousels.
June 23, 2026New Feature
POST /v1/whatsapp/phone-numbers/kyc now supports provisioning multiple same-country numbers in a single KYC submission via quantity.

Set quantity (default 1, min 1, max 5) to request several numbers; the same verification covers all of them and each number is billed only when it activates (best-effort if some orders fail).

Response now includes numbers (array) with all provisioned numbers. phoneNumber remains and represents the first/primary number for backward compatibility.

status remains: kycsubmitted | kycreused | kycalreadysubmitted
June 23, 2026New Feature
WhatsApp phone number provisioning now supports creating a hosted KYC link via POST /v1/whatsapp/phone-numbers/kyc/share.

This generates a single-use, 7-day link your end customer can complete without a Zernio login; once submitted, the number provisions under your account (only for regulated/KYC countries).

Request fields:
• profileId (required)
• country (required, ISO-2; non-KYC countries return 400)
• branding (optional: companyName, logoUrl, brandColor)
• redirect_url (optional; on success appends kyc=submitted and country=<ISO-2>)

Response fields:
• url
• token
• expiresAt

To react when the form is completed, subscribe webhooks to the new event: whatsapp.number.kyc_submitted (available in POST/PUT /v1/webhooks/settings).
June 23, 2026Improvement
Comment webhooks (comment.received) now include an optional comment.attachment object for Facebook graphic-only comments (sticker, GIF, photo).

This lets you detect and render comments that have no text content; note Meta URLs may be ephemeral, so fetch promptly.

Key fields:
• comment.attachment.type (required)
• comment.attachment.imageUrl
• comment.attachment.url
June 22, 2026New Feature
Meta boosts via POST /v1/ads/boost now support raw Meta-native targeting with rawTargeting.

Use rawTargeting to pass a Meta targeting spec verbatim (snakecase like geolocations, customaudiences, excludedcustomaudiences, flexiblespec, targeting_automation) to target specific custom/lookalike audiences or clone targeting exactly.

Key rule: rawTargeting is mutually exclusive with targeting (sending both returns 422).
June 22, 2026New Feature
POST /v1/ads/create now supports additional Meta geo targeting controls.

You can target more precise location types and explicitly exclude locations when creating Meta ads.

New fields:
• places (Meta geoType place)
• neighborhoods (Meta geoType neighborhood)
• excludedLocations with countries, regions, cities, zips

POST /v1/ads/lead-forms adds richer Meta Instant Form configuration.

New fields:
• formType: MOREVOLUME | HIGHERINTENT | RICH_CREATIVE
• blockDisplayForNonTargetedViewer
• allowOrganicLeadGen
• questionPageCustomHeadline
• contextCard (title, content, style: LISTSTYLE | PARAGRAPHSTYLE, buttonText, coverPhoto)

GET /v1/ads/targeting/search geo search now supports geoType=all and returns coordinates.

Use dimension=geo with geoType=all to search across all geo types in one call, and read latitude / longitude from results (when available).
June 22, 2026Improvement
Moving an account to another profile via PATCH /v1/accounts/{accountId} can now return 409 when the target profile already has an account on the same platform.

This enforces the rule that a profile can hold only one account per platform.

Handle the new response:
• 409 - target profile already has an account of the same platform (profileplatformconflict)
June 19, 2026Improvement
GET /v1/ads/lead-forms/{formId} now returns a fully detailed form object (enough to duplicate the form via POST /v1/ads/lead-forms).

Key fields include form.status (ACTIVE/ARCHIVED), questions, thankyoupage, and context_card.

Geo targeting search via GET /v1/ads/targeting/search now supports additional geoType values when dimension=geo: subcity, neighborhood, place, metroarea, geomarket (in addition to country, region, city, zip).
June 19, 2026Improvement
Inbox conversations now include ad-click attribution via a new metadata object in responses from GET /v1/inbox/conversations and GET /v1/inbox/conversations/{conversationId}.

This lets you identify when a conversation started from a click-to-message ad (vs organic) and store the associated ad identifiers/details.

New field:
• metadata (object, nullable) — only present for ad-originated conversations; absent for organic

Common keys you may see in metadata:
• WhatsApp CTWA: ctwaclid, ctwasourceid, ctwasourceurl, ctwaheadline, ctwasourcetype, ctwacapturedat
• Meta CTM/CTD: metaadid, metaadtitle, metaadsource, metaadtype, metaadref, metaadcapturedat, metaadphotourl, metaadvideourl, metaadpostid, metaadproductid, metaadflowid
June 19, 2026Improvement
Analytics post items now include isAd in AnalyticsListResponse.posts[].

This lets you distinguish ad creatives from organic posts in analytics results (currently only available for LinkedIn posts; false when unavailable).

New field:
• isAd (boolean) — true for ad creatives, otherwise false
June 18, 2026Improvement
Meta ads created via POST /v1/ads/create now allow combining manual placements with rawTargeting.

This lets you clone an ad set’s full Meta-native targeting spec via rawTargeting while still overriding placements using the structured placements object.

When both are set, placements is converted to Meta snake_case and merged into rawTargeting before sending to Meta.

Key fields:
• rawTargeting (Meta TargetingSpec, snake_case)
• placements.publisherPlatforms: facebook, instagram, threads, messenger, audience_network
• placements.facebookPositions, placements.instagramPositions, placements.messengerPositions, placements.audienceNetworkPositions, placements.threadsPositions, placements.whatsappPositions, placements.devicePlatforms: mobile/desktop
June 18, 2026Improvement
POST /v1/ads/create now supports more control over Meta publish state and creative reuse.

You can stage Meta campaigns/ads more safely (e.g., create everything paused, or pause just the campaign while keeping the ad set ready), and you can now attach new ads using an existing creative without resending copy/media.

New/updated fields:
• status (ACTIVE | PAUSED) now explicitly controls desired publish state; in attach mode (adSetId) it affects only the new ad
• campaignStatus (ACTIVE | PAUSED) lets you set campaign state independently on the legacy create shape (ignored when existingCampaignId is set)
• existingCreativeId can now be used with adSetId to attach an ad that reuses an existing Meta creative (copy/media fields can be omitted)
June 17, 2026New Feature
POST /v1/ads/create now supports additional Meta-only creation options: you can create ads/ad sets paused, add a new ad set under an existing campaign, and/or reuse an existing creative.

This enables safer launches (no immediate spend) and easier multi-ad-set testing while keeping creatives consistent.

New request fields:
• status: ACTIVE | PAUSED
• existingCampaignId (Meta only; mutually exclusive with adSetId and creatives[])
• existingCreativeId (Meta only; mutually exclusive with creatives[], dynamicCreative, placementAssets)
June 17, 2026Improvement
LinkedIn posts now support quote-reshare via reshareUrl in LinkedInPlatformData.

Set reshareUrl to a LinkedIn post link ("Copy link to post") or a urn:li:share / urn:li:ugcPost / urn:li:groupPost URN. The published post becomes a repost with your content as commentary and the original post embedded underneath.

Notes:
• reshareUrl is mutually exclusive with media
• Works on personal profiles and organization pages

Error responses are now more structured for programmatic handling: ErrorResponse adds type, code, param, platform, and platformError (raw upstream payload when type is platform_error).

type can be: invalidrequesterror, authenticationerror, permissionerror, notfound, ratelimiterror, platformerror, api_error.
June 15, 2026New Feature
POST /v1/ads/conversions now supports TikTok conversions (platform tiktokads).

You can now relay OFFLINE conversion events to TikTok via the Offline Events API. Use destinationId as the TikTok Offline Event Set ID, and note each event must include an email or phone.

Key details:
• accountId now accepts: metaads, googleads, linkedinads, tiktokads
• Response platform enum now includes tiktokads

GET /v1/accounts/{accountId}/conversion-destinations also now supports tiktokads (platform enum includes tiktokads).

GET /v1/ads/tree adds campaignId (platform campaign id) to restrict the tree to a single campaign, independent of pagination.
June 13, 2026Improvement
Google Ads conversion destination creation via POST /v1/accounts/{accountId}/conversion-destinations is now effectively idempotent by name.

If a conversion action with the same name already exists, the API will reuse it and return the same response as a fresh create. If the same name is used with a different type (category), the API now returns 409 with error code IDEMPOTENCY_CONFLICT.

Key fields:
• name
• type

New response:
• 409 (Google Ads only) — IDEMPOTENCY_CONFLICT
June 13, 2026New Feature
POST /v1/accounts/{accountId}/conversion-destinations now supports creating conversion destinations for Google Ads in addition to LinkedIn.

You can create a Google Ads conversion action (created as UPLOAD_CLICKS for offline uploads). Standard event names are mapped automatically, and actions default to secondary unless you opt in.

Key Google Ads fields:
• countingType: MANYPERCLICK | ONEPERCLICK
• primaryForGoal: true | false (default false)

Notes:
• accountId can now be linkedinads or googleads
• Creation is not idempotent — retries can create duplicates
• 201 response platform now includes linkedinads | googleads
June 12, 2026Improvement
WhatsApp regulated-number KYC reuse now supports choosing which prior approved verification to reuse.

GET /v1/whatsapp/phone-numbers/kyc adds reusable.options (newest first), listing all approved verifications (each with fromPhoneNumber + details).

When submitting with reuse, POST /v1/whatsapp/phone-numbers/kyc adds reuseFrom to select the verification to reuse (set to reusable.options[].fromPhoneNumber). Omit reuseFrom to use the newest; no match returns 409.
June 12, 2026Improvement
GET /v1/whatsapp/phone-numbers/kyc now returns a human-readable summary when a reusable KYC verification is available.

When reusable.available is true, you can show what verification is already on file (best-effort; may be empty if provider lookup fails) and let users skip re-entering the form.

New field: reusable.details (array of { label, value }) alongside reusable.fromPhoneNumber.
June 12, 2026Improvement
GET /v1/connect/{platform}/ads now supports ad sync scoping via adAccountId/adAccountIds across more ads platforms (not just Meta).

You can limit discovery/sync to specific ad accounts for facebook/instagram (Meta), linkedin, googleads, and twitter. Changing the scope will also remove already-synced ads from any de-scoped ad accounts.

Use:
• adAccountId - single ad account id (platform-specific format)
• adAccountIds - multiple ids (repeat param or comma-separated)

Notes:
• tiktok ignores these params (advertisers are scoped during OAuth)
• pinterest ignores these params (no ads discovery)
• Meta ids (act_<digits>) are validated against the connected token; unreachable ids return 400
• Omitting both adAccountId and adAccountIds keeps the previously persisted scope unchanged
June 12, 2026Improvement
Broadcast recipient results now include a plain-language failure reason via errorExplanation on GET /v1/broadcasts/{broadcastId}/recipients.

This helps you interpret WhatsApp errorCode values (e.g. opt-out / re-engagement required) without maintaining your own mapping.

Use:
• recipients[].errorCode (integer, nullable)
• recipients[].errorExplanation (string, nullable; null if unmapped — fall back to error)

Webhooks for delivery status now also include error.explanation in WebhookPayloadMessageDeliveryStatus (populated on event=message.failed).
June 12, 2026Improvement
Google campaign objects now include advertisingChannelType in AdCampaign and AdTreeCampaign.

This exposes the raw Google Ads campaign.advertisingchanneltype so you can distinguish campaign serving surfaces (e.g. Search vs Performance Max) when syncing/reporting.

New field:
• advertisingChannelType (string, nullable) — Google-only (e.g. SEARCH, PERFORMANCEMAX, VIDEO, DEMANDGEN, DISPLAY, SHOPPING)
June 12, 2026New Feature
WhatsApp interactive messages now support voice call buttons when sending inbox messages via POST /v1/inbox/conversations/{conversationId}/messages.

This lets you render WhatsApp’s native “call” CTA in-chat (requires WhatsApp Business Calling enabled on the sending number). Optional attribution data can be echoed back on the calls webhook.

Use interactive.type = voicecall and interactive.action.name = voicecall. Optional interactive.action.parameters:
• display_text
• ttl_minutes
• payload

Also, GET /v1/whatsapp/calling now returns callDeepLink (nullable) for the public https://wa.me/call/<number> deep link when calling is enabled.
June 12, 2026New Feature
YouTube analytics now supports video retention curves via GET /v1/analytics/youtube/video-retention.

This returns an aggregated (not per-day) audience retention curve for a single video, plus durationSeconds so you can render the curve on a time axis.

Call with:
• videoId
• accountId
• optional startDate, endDate

Response highlights:
• retentionCurve[] points with elapsedVideoTimeRatio, audienceWatchRatio (can be > 1), relativeRetentionPerformance
• empty retentionCurve for low-view / not-yet-processed videos (2–3 day delay)

Requires yt-analytics.readonly; if missing you may get 412 with code=youtubeanalyticsscope_missing.

Also added to GET /v1/analytics/youtube/daily-views responses:
• top-level durationSeconds
• per-day averageViewPercentage (can exceed 100; don’t clamp client-side)
June 12, 2026New Feature
GET /v1/analytics/daily-metrics now supports engagement attribution via a new query param attribution.

This lets you choose whether daily totals are attributed to the post’s publish date or bucketed by the day engagement was actually received (engagement-over-time).

Use attribution:
• publish (default) — attribute each post’s lifetime total to its publish date
• received — attribute per-day engagement increases to the day they occurred
June 12, 2026New Feature
WhatsApp adds blocked-status lookup for a single contact via GET /v1/whatsapp/block-users/status.

This returns a definitive blocked state using Zernio’s blocklist mirror (kept in sync by block/unblock; first call per account may backfill from Meta’s full list).

Required query params:
• accountId
• user (consumer wa_id or E.164 phone; leading + optional)

Response 200:
• blocked (boolean)
June 12, 2026New Feature
POST /v1/whatsapp/calls now supports sending the WhatsApp call-consent prompt via a new optional action parameter.

Use this to request consumer permission before placing a Business-Initiated Call; the consumer must tap Allow before start_call is permitted (Meta rate-limits prompts and requires an open 24h service window).

Set:
• action: sendcallpermission_request (omit action to place a call)
• bodyText: optional text shown with the consent prompt (max 1024 chars)
June 11, 2026New Feature
WhatsApp now supports managing your blocklist via /v1/whatsapp/block-users.

You can list blocked users, block up to 1,000 users per request, and unblock in batches (per-user failures are returned without failing the whole request).

New endpoints:
• GET /v1/whatsapp/block-users with accountId, optional limit, after (cursor pagination via nextCursor)
• POST /v1/whatsapp/block-users body: accountId, users (1..1000)
• DELETE /v1/whatsapp/block-users body: accountId, users (1..1000)

WhatsApp interactive messages now also support location requests in POST /v1/inbox/conversations/{conversationId}/messages via interactive.type = locationrequestmessage (and action can be omitted; defaults to {"name":"send_location"}).
June 11, 2026New Feature
Meta catalog ads are now supported via goal = catalog_sales on POST /v1/ads/create.

This lets you create Advantage+ catalog ads (e.g. vehicle inventory) using a Meta product catalog + product set; Meta renders the visuals per catalog item (no imageUrl/video sent).

Discover inputs with:
• GET /v1/ads/catalogs (accountId, adAccountId)
• GET /v1/ads/catalogs/{catalogId}/product-sets (catalogId, accountId)

Create the ad with:
• goal: catalog_sales
• promotedObject.productSetId
• promotedObject.pixelId
• promotedObject.customEventType

Notes: catalog_sales is single-shape only (no creatives[], adSetId, dynamicCreative, or placementAssets).
June 11, 2026Improvement
GET /v1/whatsapp/phone-numbers now also returns connected (bring-your-own) WhatsApp numbers in a separate connected array.

This lets you list Embedded Signup–linked WABA numbers alongside purchased numbers (which remain in numbers). Connected numbers aren’t provisioned or billed by Zernio and use accountId for messaging/inbox endpoints.

New response field: connected[] with:
• accountId
• phoneNumber
• displayName
• profileId
• connectedAt

Included only on the default view and when status=active.
June 10, 2026Improvement
WhatsApp phone number purchase now supports idempotent retries and duplicate-purchase protection on POST /v1/whatsapp/phone-numbers/purchase.

Use purchaseIntentId (optional) as an idempotency key: retry with the same value and, if a number was already bought, the API returns { status: "already_purchased", numberId, phoneNumber } instead of provisioning a second number.

A second purchase within 10 minutes is now rejected with 409 and code PURCHASE_VELOCITY. To confirm an intentional additional purchase (e.g. bulk provisioning), set allowMultiple to true.
June 10, 2026New Feature
New endpoint: GET /v1/accounts/{accountId}/gmb-attribute-metadata.

You can now fetch Google Business Profile attribute metadata (valid attribute names, value types, and allowed enum values) before reading/writing via gmb-attributes.

Query modes (mutually exclusive):
• Location mode: locationId (or omit to use the account’s stored selectedLocationId)
• Category mode: categoryName (must start with categories/) + regionCode (optional languageCode)

Pagination: pageSize (1–200) and pageToken.

Response includes attributeMetadata[] with parent, valueType (e.g. BOOL, ENUM, URL, REPEATED_ENUM), displayName, repeatable, deprecated, and valueMetadata[] (enum options), plus nextPageToken when more results are available.
June 10, 2026Improvement
Meta video ads created via POST /v1/ads/create no longer require a thumbnail.

You can now omit video.thumbnailUrl and Zernio/Meta will auto-generate the poster frame using Meta’s preferred video thumbnail (same candidates as Ads Manager). Provide it only if you need an exact poster frame.

Applies to:
• Top-level video (legacy + attach shapes): video.url required, video.thumbnailUrl optional
• creatives[].video (Meta multi-creative): video.url required, video.thumbnailUrl optional
June 10, 2026New Feature
POST /v1/ads/create now supports Meta link descriptions via a new description field.

This lets you set the secondary link text on Facebook/Instagram ads instead of relying on Meta scraping OpenGraph data.

Use:
• description (Meta only) — applies to legacy + attach + placementAssets
• creatives[].description (Meta multi-creative) — per-ad override; falls back to top-level description

Also clarified for Meta lead ads: when using adSetId (attach shape) into a lead ad set, you must pass leadGenFormId on every attached ad.
June 10, 2026Deprecation
Invite creation now supports a dedicated read-only role via role on POST /v1/invite/tokens.

Use role to control access level:
• member
• billing_admin
• viewer - read-only (no publish/edit/delete/connect)

The readOnly field is now deprecated. If readOnly=true, the invite is created with role=viewer (and it can’t be combined with role=billing_admin).
June 10, 2026New Feature
Webhooks now support a new WhatsApp number event: whatsapp.number.action_required.

Subscribe to this event to be notified when a WhatsApp number needs additional action to proceed.

Add it to events when creating/updating a webhook via POST /v1/webhooks/settings or PUT /v1/webhooks/settings:
• events: ["whatsapp.number.action_required", ...]
June 9, 2026Improvement
Invite token creation now supports assigning an org role and issuing read-only invites via POST /v1/invite/tokens.

Use role to grant org-level permissions (member | billing_admin, default member), and readOnly to restrict the invitee to view-only access within their scope (default false).
June 9, 2026Breaking Change
WhatsApp template buttons updated: WhatsAppTemplateButton now supports a new button type value copy_code, and text is no longer required for all button types.

This enables WhatsApp marketing “copy code” buttons where the label is fixed by WhatsApp, and the coupon code is provided via example.

Key changes:
• type enum now includes: quickreply, url, phonenumber, otp, copy_code, flow, mpm, catalog
• text: required for all types except copy_code
• example is now string or string[] (URL buttons: string[]; copy_code: coupon code as string)
June 9, 2026New Feature
Meta Lead Gen ads created via POST /v1/ads/create now support Dynamic Creative using dynamicCreative together with goal=lead_generation.

You can run Meta’s multi-text / multi-asset optimization ("Multiple Text Options") on instant-form lead ads; Zernio will create the required Dynamic Creative ad set automatically and attach the form for you.

Use:
• goal=lead_generation
• leadGenFormId=<form_id>
• dynamicCreative.imageUrls (send 1+; send a single entry if you only want multi-text)
• Optional variations: dynamicCreative.titles, dynamicCreative.bodies, dynamicCreative.descriptions, dynamicCreative.linkUrls
• Required: dynamicCreative.callToActionTypes (e.g. LEARNMORE, SIGNUP, DOWNLOAD, REGISTER, BUY_NOW)
June 9, 2026New Feature
X (Twitter) posts now support additional labeling and media safety controls via platformSpecificData.

You can label posts as paid promotions or AI-generated media, and mark attached media with sensitive-content warnings.

New fields in TwitterPlatformData:
• paidPartnership (boolean)
• madeWithAi (boolean)
• sensitiveMedia with adultContent, graphicViolence, other (booleans; requires media)

Reddit posts now support additional post options via platformSpecificData.

New fields in RedditPlatformData:
• flairText (string; ignored if flairId is set)
• nsfw (boolean)
• spoiler (boolean)
• sendreplies (boolean)
June 8, 2026Improvement
GET /v1/accounts now supports filtering by connection status via the new status query param.

This lets you fetch only healthy accounts or only accounts that need reconnection (matching the dashboard reconnection check). Pagination totals reflect the filtered result set when used with page/limit.

Use status:
• connected
• disconnected
June 8, 2026Improvement
Meta Lead Gen ads created via POST /v1/ads/create now support per-placement creative using placementAssets.

You can pin different image/video assets to placement groups (e.g. Stories/Reels vs Feed) while still attaching a Lead Gen form via leadGenFormId.

Use:
• goal=lead_generation
• leadGenFormId
• placementAssets with defaultImageUrl or defaultVideoUrl + rules[].placements

Note: dynamicCreative is still not compatible with goal=lead_generation.
June 8, 2026Improvement
Meta ads tracking-tag updates are now simpler on PATCH /v1/ads/{adId}/tracking-tags: you can send urlTags alone and Zernio will preserve the existing creative verbatim while rebuilding/repointing the ad.

This removes the previous requirement to provide the full Meta creative payload just to add/update click-URL tags. For creatives where Meta strips objectstoryspec (e.g. share/page-post/dark/asset_feed), you may still get 422 and need to include creative explicitly.

Key fields:
• urlTags (Meta)
• creative (Meta, optional; required only when preservation isn’t possible)
• trackingUrlTemplate, finalUrlSuffix (Google)
• dynamicValueParameters, customValueParameters (LinkedIn)
June 8, 2026New Feature
Meta ads created via POST /v1/ads/create now support explicitly setting the ad set optimization goal via optimizationGoal.

This lets you override the default optimization derived from goal (e.g. traffic defaulting to LINKCLICKS) and pass a Meta-native optimizationgoal value directly; Meta will validate and reject incompatible combinations.

Use optimizationGoal (Meta only), e.g. LANDINGPAGEVIEWS, LINKCLICKS, REACH, IMPRESSIONS, OFFSITECONVERSIONS, THRUPLAY, LEAD_GENERATION.
June 5, 2026New Feature
Google Ads now supports conversion adjustments via POST /v1/ads/conversions/adjustments.

You can retract, restate value, or enhance an already-uploaded conversion (Google only; other platforms return 405). Partial failure is supported (check adjustmentsFailed / failures[]).

Send:
• accountId (must be a googleads account)
• destinationId (conversion action resource name)
• adjustments[] with adjustmentType = RETRACTION | RESTATEMENT | ENHANCEMENT

Per adjustment:
• Identify the original conversion with orderId (required for ENHANCEMENT) or gclid + conversionTime
• adjustmentTime (unix seconds)
• restatementValue + currency (for RESTATEMENT)
• user.email/user.phone (+ optional userAgent) for ENHANCEMENT

Also: Meta standalone ad creation adds goal = leadconversion on POST /v1/ads/create (and Ad.goal) for website pixel lead optimization (distinct from leadgeneration instant forms).
June 5, 2026Improvement
GET /v1/comment-automations now returns stats.trackedSends for each automation.

Use trackedSends as the CTR denominator for tracked links (divide stats.linkClicks by stats.trackedSends, not stats.dmsSent). It may lag dmsSent for automations that predate click tracking.

New field:
• stats.trackedSends - DMs sent with a trackable (wrapped) link
June 5, 2026New Feature
Webhooks now support external post events for posts created/updated/deleted directly on the platform (not published via Zernio).

Subscribe by including these in events when calling POST /v1/webhooks/settings or PUT /v1/webhooks/settings:
• post.external.created
• post.external.updated
• post.external.deleted

Payload uses WebhookPayloadExternalPost with post = ExternalPostWebhookPost (includes post.id = platform-native ID, post.source = external; on post.external.deleted, post.deletedAt is populated). Events are sync-detected (~hourly), not real-time.
June 5, 2026New Feature
POST /v1/ads/create now supports Meta-specific single-ad naming and ad-level tracking.

You can explicitly set the created ad’s name (instead of inheriting name), and attach pixel measurement + URL tags regardless of optimization goal.

New fields:
• adName (Meta only) — overrides the default ad name (default was name)
• tracking.pixelId (Meta only) — attaches pixel measurement via ad tracking_specs
• tracking.urlTags (Meta only) — sets ad url_tags as [{ key, value }]
June 5, 2026New Feature
New Inbox Analytics endpoints are available to query messaging volume, response times, and conversation-level stats (max date range: 365 days).

New endpoints:
• GET /v1/analytics/inbox/volume — volume + KPIs + per-platform split (key params: fromDate, toDate, profileId, platform, accountId, source)
• GET /v1/analytics/inbox/heatmap — day-of-week × hour heatmap (key params: fromDate, toDate, action=message.received|message.sent|message.read|all)
• GET /v1/analytics/inbox/source-breakdown — breakdown by metadata.source
• GET /v1/analytics/inbox/response-time — time-to-first-response summary + histogram
• GET /v1/analytics/inbox/top-accounts — top accounts by inbox volume (key param: limit 1–50)
• GET /v1/analytics/inbox/conversations — paginated conversation analytics (key params: limit, page, sortBy=lastMessageAt|firstMessageAt|totalMessages|received|sent|read|failed, order=asc|desc)
• GET /v1/analytics/inbox/conversations/{conversationId} — single-conversation analytics ({conversationId} accepts Mongo _id or platformConversationId)

Webhook event enums were expanded in POST /v1/webhooks/settings, PUT /v1/webhooks/settings, and the Webhook schema to include:
• whatsapp.number.suspended
• whatsapp.number.reactivated
• whatsapp.number.released
June 4, 2026New Feature
You can now rename Meta ads objects via update endpoints.

This adds name updates without recreating entities (Meta only; other platforms return 501).

• PUT /v1/ads/campaigns/{campaignId} now accepts name (max 255)
• PUT /v1/ads/ad-sets/{adSetId} now accepts name (max 255)
• PUT /v1/ads/{adId} now supports name (max 255) and propagates it to Meta; non-Meta returns 501

Ad creation on Meta also adds more naming control:
• POST /v1/ads/create now accepts campaignName and adSetName (max 255)
• In Meta multi-creative mode, creatives[].name sets the per-ad name (max 255)
June 4, 2026New Feature
Meta ads created via POST /v1/ads/create now support raw Meta-native targeting via rawTargeting.

This lets you pass a Meta targeting spec through verbatim (snake_case) to clone an existing campaign’s targeting exactly, including advanced fields not covered by the camelCase targeting fields.

Use rawTargeting (Meta only) and do not combine it with:
• camelCase targeting fields (e.g. countries, regions, cities, interests, ageMin, ...)
• audienceId
• savedTargetingId

Sending both will return 422. If targeting EU users, also provide dsaBeneficiary / dsaPayor separately (not inside rawTargeting).
June 4, 2026Improvement
POST /v1/ads/create now supports safe retries via an optional Idempotency-Key header.

This endpoint isn’t idempotent at the ad-platform level (a blind retry can create duplicate campaigns/ad sets/ads). With Idempotency-Key, retries return the original stored response instead of creating duplicates.

Use Idempotency-Key (e.g. a UUID):
• Same key + same body → replays the original response (response header Idempotent-Replayed: true)
• Same key + different body → 422
• Key still processing → 409 (retry after backoff)

Keys are scoped to your credential and expire after 24h.
June 3, 2026Improvement
Google Business Profile location listing now supports server-side narrowing for large accounts via search and advanced filter.

This helps users find a specific location without loading the full list, and responses can now indicate when the list is bounded.

Changes:
• GET /v1/connect/googlebusiness/locations: new search, filter; response adds hasMore and per-location storeCode
• GET /v1/accounts/{accountId}/gmb-locations: new search, filter; response adds hasMore and per-location storeCode
• POST /v1/connect/googlebusiness/select-location: request body adds optional (recommended) accountId to resolve the location directly for large accounts
• PUT /v1/accounts/{accountId}/gmb-locations: request body adds optional (recommended) accountId when switching locations
June 3, 2026Improvement
Meta placement asset customization in POST /v1/ads/create now supports pinning video per placement group (not just images).

You can now run a single ad with different assets by placement (e.g. 9:16 video on Stories/Reels, 4:5 on Feed). A placementAssets block must be all-image or all-video (no mixing).

Use placementAssets in one of these modes:
• Image mode: defaultImageUrl + rules[].imageUrl
• Video mode: defaultVideoUrl + rules[].videoUrl (+ optional defaultThumbnailUrl / rules[].thumbnailUrl)

placementAssets still can’t be combined with dynamicCreative, creatives[], or adSetId.
June 3, 2026New Feature
Ads now support unified read/update of click-URL tracking tags via GET /v1/ads/{adId}/tracking-tags and PATCH /v1/ads/{adId}/tracking-tags.

This lets you audit and set platform-native tracking params across Meta/Google/LinkedIn (platforms without a click-URL tracking surface return 405).

Read returns platform, level (creative | campaign) plus platform fields:
• Meta: urlTags, templateUrlSpec
• Google: trackingUrlTemplate, finalUrlSuffix
• LinkedIn: dynamicValueParameters, customValueParameters

Update expects only the platform fields:
• Meta: urlTags (array of {key,value}) + full creative (headline, body, callToAction, linkUrl, imageUrl); may return 422 if the creative can’t be rebuilt
• Google: trackingUrlTemplate and/or finalUrlSuffix
• LinkedIn: dynamicValueParameters and/or customValueParameters

Meta-only: you can now read Event Match Quality (EMQ) + pixel↔CAPI coverage via GET /v1/ads/conversions/quality with accountId and destinationId (non-Meta returns 405).

Also added Meta advanced matching fields to ConversionEvent.user: city, state, zip, dob, gender (Meta-only; hashed server-side).
June 2, 2026New Feature
POST /v1/ads/create now supports Meta placement asset customization via placementAssets.

This lets you pin a specific image to each placement group (e.g. 9:16 for Stories/Reels and 4:5 for Feed) within a single ad, using Meta’s assetfeedspec + assetcustomizationrules behavior.

Use placementAssets:
• defaultImageUrl (required)
• rules[] (1-10), each with imageUrl + placements

placements supports: publisherPlatforms, facebookPositions, instagramPositions, messengerPositions, audienceNetworkPositions, threadsPositions, whatsappPositions, devicePlatforms.

Constraints: Meta-only; cannot be combined with dynamicCreative, creatives[], or adSetId.
June 2, 2026New Feature
Comment-to-DM automations now support Instagram story reply triggers via a new trigger field.

You can create automations that fire on keyword replies to stories (in addition to post comments). For story replies, platformPostId is the story media id (omit it to target any story).

Use trigger:
• comment
• story_reply

Applies to:
• POST /v1/comment-automations (new request field trigger, default comment)
• GET /v1/comment-automations and GET /v1/comment-automations/{automationId} (responses now include trigger)
June 2, 2026Improvement
GET /v1/comment-automations now returns additional automation stats: stats.delivered and stats.read.

This lets you report delivery/read performance for comment-to-DM automations (where supported) without extra calls.

New fields:
• stats.delivered - DMs confirmed delivered (Messenger; IG emits no delivery receipt)
• stats.read - DMs confirmed read (IG messagingseen / Messenger messagereads)
June 2, 2026New Feature
Comment-to-DM automations now support tagging contacts who click tracked links via clickTag.

When linkTracking is enabled, you can set clickTag so anyone who clicks a tracked link button in the DM gets tagged for segmentation (broadcasts/sequences).

Use:
• POST /v1/comment-automations: clickTag (optional; requires linkTracking)
• PATCH /v1/comment-automations/{automationId}: clickTag (empty string clears it)

Returned in:
• GET /v1/comment-automations: clickTag
• GET /v1/comment-automations/{automationId}: clickTag
June 2, 2026New Feature
Comment-to-DM automations now support link tracking for DM button URLs, and the list endpoint includes click metrics.

When enabled, link buttons are wrapped in a tracked redirect so you can measure clicks and CTR.

New fields:
• Create/update: linkTracking (POST default: true; set false to send links untouched)
• List: automations[].linkTracking
• List stats: automations[].stats.linkClicks, automations[].stats.uniqueClicks
• Details/create responses: automation.linkTracking
June 1, 2026New Feature
X (Twitter) posts now support quote reposting via quoteTweetId in TwitterPlatformData.

Set quoteTweetId to a tweet ID (or full status URL) to publish a quote tweet. For threads, it applies to the first tweet only.

Key property:
• quoteTweetId - mutually exclusive with media and poll; X only allows quoting your own posts or posts you’re mentioned in / part of the conversation thread of; billed at the standard create rate ($0.015).
May 29, 2026Improvement
WhatsApp call billing now includes billableCostUSD in call history, cost estimates, and call.ended webhooks.

This makes it explicit what Zernio bills you (Telnyx + recording) vs what Meta bills directly to your WABA (display-only).

New/updated billing fields:
• billing.billableCostUSD - amount Zernio bills (excludes Meta)
• billing.metaCostUSD - Meta per-minute charge (billed by Meta directly; display only)
• billing.totalCostUSD - full cost including Meta (display only)

Applies to:
• GET /v1/whatsapp/calls
• GET /v1/whatsapp/calls/estimate (in breakdown)
• Webhook schema WebhookPayloadCallEnded (in call.billing)
May 29, 2026Improvement
GET /v1/contacts/{contactId} now returns additional messaging activity fields on the contact and channels.

You can use these to show engagement stats and recent activity without querying message history.

New fields:
• Contact: messagesSentCount, messagesReceivedCount, lastMessageSentAt (nullable), lastMessageReceivedAt (nullable)
• Channel: lastActiveAt (nullable)
May 29, 2026New Feature
New Workflows API endpoints are available to build and run branching conversation workflows (node/edge graphs), including activation/pausing and execution tracking.

Manage workflows:
• GET /v1/workflows (filters: profileId, status = draft|active|paused, limit, skip)
• POST /v1/workflows (create draft; required: profileId, accountId, name; optional: platform = whatsapp|instagram|facebook|telegram|twitter|bluesky|reddit, nodes, edges, entryNodeId)
• GET /v1/workflows/{workflowId}
• PATCH /v1/workflows/{workflowId} (graph edits only when draft or paused)
• DELETE /v1/workflows/{workflowId}

Control lifecycle:
• POST /v1/workflows/{workflowId}/activate (validates runnable graph; idempotent)
• POST /v1/workflows/{workflowId}/pause (stops matching new inbound messages; idempotent)

Track and test runs:
• GET /v1/workflows/{workflowId}/executions (filter status = running|waiting|completed|exited|failed; limit, skip)
• POST /v1/workflows/{workflowId}/executions (manual start; provide conversationId or WhatsApp-only to; optional text seeds lastMessage)
May 29, 2026New Feature
WhatsApp now supports Sandbox session management via new endpoints: GET /v1/whatsapp/sandbox/sessions, POST /v1/whatsapp/sandbox/sessions, DELETE /v1/whatsapp/sandbox/sessions/{sessionId}.

This lets you activate a test phone against the shared sandbox number (reply-to-activate), list non-expired sessions, and revoke access to block future sends.

Key fields:
• Create/refresh with phone (server normalizes to E.164)
• Session object WhatsAppSandboxSession: id, phoneE164, status (pending | active), expiresAt, activatedAt
• Responses include sandboxNumber

Also, GET /v1/whatsapp/phone-numbers now includes an optional sandbox object (or null) with phoneNumber, accountId, and allowed template (name, language), plus isSandbox = true.
May 28, 2026Improvement
WhatsApp conversion events no longer require a Facebook Page ID on the account when calling POST /v1/whatsapp/conversions.

The endpoint now only requires metaCapiDatasetId (provision via POST /v1/whatsapp/dataset) and forwards the WABA ID as userdata.whatsappbusinessaccountid for Meta attribution.

If you were setting account metadata with connectedFacebookPageId, you can stop—missing it will no longer cause 422. Missing metaCapiDatasetId can still return 422.
May 28, 2026Improvement
Inbox webhooks now include contactId on conversation (via InboxWebhookConversation).

This lets you join any inbox webhook event back to the Zernio CRM Contact (best-effort), without relying on the sender identity — useful for outgoing and delivery-status style events.

New field:
• conversation.contactId - Zernio CRM Contact ID for the participant (omitted when no match or participantId is absent)

Applies to:
• message.received
• message.sent
• reaction.received
• conversation.started
May 28, 2026Improvement
CTWA ad creation now supports more precise geo targeting in POST /v1/ads/ctwa.

You can target by city/region/ZIP/metro or point-radius locations (useful for local WhatsApp campaigns) instead of relying only on country targeting.

New optional geo fields:
• cities[] (with optional radius + distance_unit: mile | kilometer)
• regions[]
• zips[]
• metros[]
• customLocations[] (requires latitude, longitude, radius, distanceUnit: mile | kilometer)

Note: countries now defaults to ["US"] only when no other geo targeting (cities/regions/zips/metros/customLocations) is provided.
May 28, 2026New Feature
New webhook event conversation.started is available via WebhookPayloadConversationStarted.

It fires once when a new DM conversation begins (first message exchange) and is platform-agnostic across instagram, facebook, telegram, whatsapp, twitter, reddit, bluesky.

Key fields:
• event=conversation.started
• conversation.id, conversation.platform, conversation.platformConversationId
• conversation.status: active | archived
• account
• startedAt, timestamp
• id (stable webhook event ID)
May 28, 2026New Feature
POST /v1/inbox/conversations/{conversationId}/messages now supports WhatsApp voice messages (PTT) via voiceNote in the JSON body.

When sending an audio attachment, set voiceNote to true to send it as a WhatsApp voice note (waveform/voice-note UI) instead of a regular audio file.

Key params:
• attachmentType: audio
• attachmentUrl: public URL to the audio file
• voiceNote: true (WhatsApp only)

Note: the audio must be .ogg encoded with OPUS (mono); other formats will be rejected by WhatsApp.
May 28, 2026New Feature
New endpoint: GET /v1/whatsapp/conversions.

You can now fetch recent WhatsApp conversion send events (from delivery logs, ~30-day retention) to power a “recent activity” view without maintaining your own persistence.

Required query: accountId
Optional query: limit (1–200, default 50)

Response includes per-event fields like timestamp, eventName (LeadSubmitted, Purchase, AddToCart, InitiateCheckout, ViewContent), conversationId, eventsReceived, eventsFailed, traceId, durationMs.
May 28, 2026Improvement
Inbox webhooks now include a sender contactId when the sender matches a Zernio CRM Contact.

This lets you link inbound messages/reactions directly to a Contact (via the ContactChannel mapping) without an extra Contacts API lookup. The field is omitted when no contact exists (and typically for outgoing/business sender).

New field:
• message.sender.contactId in message.received and message.sent
• reaction.sender.contactId in reaction.received
• InboxWebhookMessage.sender.contactId
May 28, 2026New Feature
WhatsApp now supports managing the Meta CTWA conversions dataset via two new endpoints.

Use these to detect whether Conversions API reporting is configured for a WhatsApp account and to provision the required Meta dataset (idempotent).

Check current dataset: GET /v1/whatsapp/dataset with accountId (query)
Returns datasetId (nullable).

Provision (or fetch existing) dataset: POST /v1/whatsapp/dataset with body accountId
Returns datasetId and created (boolean).

If the account token is missing whatsappbusinessmanage_events, provisioning returns 422 (reconnect required).
May 27, 2026New Feature
New endpoint: GET /v1/whatsapp/template-library.

You can now look up a single pre-approved WhatsApp Template Library template by exact name to inspect its structure before importing—especially the buttons you must mirror when creating a template (e.g., URL/PHONE_NUMBER inputs).

Required query params:
• accountId (WhatsApp social account ID)
• name (exact library template name)

Response (200): template (object or null) with key fields like bodyparams and buttons where buttons[].type can be QUICKREPLY, URL, PHONE_NUMBER, OTP, FLOW, ...
May 27, 2026New Feature
WhatsApp Flows now supports listing submitted flow responses via GET /v1/whatsapp/flow-responses.

This lets you fetch customer responses parsed from nfm_reply webhook messages (newest first), and optionally scope them to a specific flow.

Use:
• accountId (required)
• flowId (optional)
• limit (optional, max 200, default 50)

Response items include id, receivedAt, from, senderName, conversationId, flowToken, data, raw.

Also updated: when sending flows via POST /v1/whatsapp/flows/send, if flow_token is omitted it is now auto-generated as <flowId>:<uuid> so responses can be attributed to that flow in the Flow Responses view.
May 27, 2026New Feature
WhatsApp Flows now support flow previews and version history.

You can fetch Meta’s embeddable web preview URL for a flow (drafts included), and list Zernio-tracked versions for a flow’s clone lineage.

New endpoints:
• GET /v1/whatsapp/flows/{flowId}/preview with accountId and optional invalidate (mint a fresh link)
• GET /v1/whatsapp/flows/{flowId}/versions with accountId

Flow versioning metadata is now returned:
• GET /v1/whatsapp/flows adds version and lineageId per flow
• POST /v1/whatsapp/flows response adds version and lineageId

Cloning can now stay in the same lineage:
• POST /v1/whatsapp/flows adds asVersion (only when using cloneFlowId)
May 26, 2026New Feature
Inbox messages now include platform-specific metadata on each message via messages[].metadata.

This lets you read extra context like quoted replies and WhatsApp interactive/tap details without relying only on webhooks.

New field:
• messages[].metadata (free-form object; commonly includes quotedMessageId, waInteractive, and inbound tap fields like interactiveType / interactiveId)

WhatsApp multipart sends now support voice note uploads via voiceNote.

Set voiceNote to true when uploading an in-browser recorded audio so it’s transcoded to WhatsApp-native ogg/Opus.

New multipart field:
• voiceNote: true (WhatsApp-only)
May 26, 2026New Feature
Inbox messages now include emoji reactions on supported platforms (WhatsApp / Telegram) via reactions on each message.

This lets you display who reacted and when, alongside the message timeline.

New per-message field:
• reactions[]: { emoji, fromMe, reactedAt }

Sending messages now supports WhatsApp location and contact cards.

New request fields for POST /v1/inbox/conversations/{conversationId}/messages:
• location: { latitude, longitude, name?, address? }
• contacts[]: contact card objects (requires name.formatted_name; optional phones[], emails[])
May 26, 2026New Feature
POST /v1/inbox/conversations now supports starting conversations on X/Twitter, Bluesky, Reddit, and WhatsApp (previously X/Twitter only).

You can initiate DMs across these platforms using participantId or participantUsername (platform-dependent).

WhatsApp requires an approved template to start a conversation: set templateName, templateLanguage, and optional templateParams (freeform initial messages aren’t allowed). message is now optional when using a WhatsApp template.

skipDmCheck remains X/Twitter only.
May 25, 2026Improvement
PostAnalytics now includes Instagram Reels watch-time metrics.

You can retrieve average and total watch time for Reels (values are 0 for non-Reels media and other platforms).

New fields:
• igReelsAvgWatchTime (ms)
• igReelsVideoViewTotalTime (ms)
May 25, 2026New Feature
New webhook payload schema added for Meta Lead Gen / Instant Forms: lead.received events now use WebhookPayloadLead.

This lets webhook consumers reliably parse incoming leads, including form metadata, ad attribution (when available), and a flattened field map.

Key properties:
• event: lead.received
• lead.fields: key → value map (multiple-choice values are option keys, e.g. k1)
• lead.isOrganic: boolean
• lead.createdAt: ISO 8601 datetime
• account.platform: facebook
• lead.formName, lead.adId, lead.adsetId, lead.campaignId: nullable when not available
May 25, 2026New Feature
Meta Lead Gen is now supported end-to-end: you can manage Instant Forms and retrieve submitted leads via new Ads endpoints.

New endpoints:
• List all leads (cross-form CRM view): GET /v1/ads/leads with formId, accountId, since, limit, cursor
• Manage forms: GET /v1/ads/lead-forms, POST /v1/ads/lead-forms, GET /v1/ads/lead-forms/{formId}, DELETE /v1/ads/lead-forms/{formId}
• Form leads + testing: GET /v1/ads/lead-forms/{formId}/leads, POST /v1/ads/lead-forms/{formId}/test-leads

Ad creation update (Meta only): POST /v1/ads/create now accepts leadGenFormId. When goal is lead_generation, leadGenFormId is REQUIRED, and linkUrl is NOT required (the ad opens the form).
May 22, 2026New Feature
New endpoint POST /v1/ads/targeting/reach-estimate returns a normalized pre-flight audience reach estimate for a targeting spec before creating a campaign.

Send accountId + spec (same shape as TargetingSpec used by ads create). Optionally pass optimizationGoal.

Response includes available, and when available: lower, upper, optional daily, currency, and Meta-only estimateReady.

GET /v1/ads/interests is now deprecated. Use GET /v1/ads/targeting/search with dimension=interest (the deprecated alias still returns the legacy { interests: [...] } shape).

GET /v1/ads/targeting/search is now cross-platform and normalized. Use dimension = geo | interest | behavior | income, and for geo also geoType = country | region | city | zip | metro. Results return { results: [{ id, name, type, path?, audienceSize? }] }.

POST /v1/ads/create targeting now supports additional fields: zips, metros, customLocations, behaviors, incomeTier (top5 | top10 | top1025 | top2550), languages, savedTargetingId, specialAdCategories (HOUSING | EMPLOYMENT | CREDIT | ISSUESELECTIONSPOLITICS).

Audiences now support reusable targeting presets: POST /v1/ads/audiences accepts type=savedtargeting with spec (no adAccountId), and GET /v1/ads/audiences adds type filter and returns spec when type is savedtargeting.
May 22, 2026Improvement
GET /v1/inbox/comments/{postId} now returns (Reddit only) post metadata alongside comments in a new post object.

This lets you render a post preview in the inbox/comments view without an additional Reddit fetch. post is null/absent for non-Reddit platforms and may be missing if the upstream post listing isn’t available.

Key fields in post: id, fullname, title, selftext, author, subreddit, permalink, url, score, numComments, createdUtc, over18, stickied, flairText, isGallery.
May 21, 2026New Feature
Google Business Profile verification is now supported via new GMB Verifications endpoints.

You can fetch verification status/history, discover eligible methods, start a verification (SMS/call/email/postcard), and complete it by submitting the PIN.

New endpoints:
• GET /v1/accounts/{accountId}/gmb-verifications (optional locationId) returns voiceOfMerchantState.hasVoiceOfMerchant and verifications[].state = PENDING/COMPLETED/FAILED
• POST /v1/accounts/{accountId}/gmb-verifications/options requires languageCode; returns options[].verificationMethod = ADDRESS/EMAIL/PHONECALL/SMS/AUTO/VETTEDPARTNER
• POST /v1/accounts/{accountId}/gmb-verifications starts verification with method (+ method-specific fields like phoneNumber/emailAddress)
• POST /v1/accounts/{accountId}/gmb-verifications/{verificationId}/complete completes with pin

Note: service-area businesses may require context (service address) when fetching options/starting verification.
May 21, 2026New Feature
Webhooks now support message reaction events via the new reaction.received subscription.

Subscribe by including reaction.received in events when calling POST /v1/webhooks/settings or PUT /v1/webhooks/settings.

The reaction payload includes emoji, action (added | removed), platformMessageId, optional messageId, sender, and reactedAt.
May 20, 2026New Feature
GET /v1/ads/audiences now supports fetching custom audiences for LinkedIn and X (Twitter) ad accounts.

Use the platform query param with one of:
• facebook, instagram, googleads, tiktok, tiktokads, pinterest, linkedin, linkedinads, twitter, xads
May 18, 2026Improvement
GET /v1/broadcasts/{broadcastId}/recipients now returns errorCode per recipient.

This provides the Meta WhatsApp error code to help you classify and handle delivery failures (only populated when status=failed).

New field: errorCode (integer, nullable) — e.g. 131049 (antispam), 131021 (invalid phone), 131026 (re-engagement required).
May 18, 2026New Feature
Instagram now supports Stories retrieval + insights.

You can list currently-active stories and fetch metrics for a specific story, including a clear indicator of whether insights are live, cached, or unavailable.

New endpoints:
• GET /v1/accounts/{accountId}/instagram/stories
Returns active stories (24h window). Key fields: id, mediaType, mediaUrl (nullable), permalink, thumbnailUrl, timestamp
• GET /v1/accounts/{accountId}/instagram/stories/{storyId}/insights
Returns data.source = live | cached | unavailable and data.metrics including views, reach, replies, shares, navigation, tapsForward, tapsBack, exits, swipesForward, profileVisits, follows, reposts, totalInteractions.
May 18, 2026New Feature
WhatsApp is now supported as a platform for connecting accounts and checking account health.

You can now use whatsapp with:
• GET /v1/connect/{platform} (path param platform enum now includes whatsapp)
• GET /v1/accounts/health (query param platform enum now includes whatsapp)
May 17, 2026Improvement
GET /v1/ads/tree now supports campaign-level sorting via a new sort query parameter.

This lets you control the order of returned campaigns (by recency or aggregated spend) while keeping pagination at the campaign level.

Use sort:
• newest (default)
• oldest
• spend_desc
• spend_asc
May 15, 2026New Feature
Facebook now supports multi-link carousel posts via facebookSettings on POST /v1/posts.

This lets you publish a 2–5 card carousel where each image has its own click-through link (and optional headline/description).

Set facebookSettings.carouselCards (2–5 items) with:
• link (required)
• name (optional)
• description (optional)

Optionally set facebookSettings.carouselLink for the carousel end-card destination.

Constraints: mediaItems must be images only, and must match carouselCards length/order. Mutually exclusive with facebookSettings.contentType = story or reel.
May 15, 2026Improvement
POST /v1/posts now supports request idempotency via x-request-id.

If you send the same x-request-id again within ~5 minutes (including while the first request is in-flight), the API treats it as a retry and returns HTTP 200 with the original post in existingPost (no duplicate post is created).

Use x-request-id (UUID) per logical create call. If you omit it, each request is treated as new.

Duplicate protection still applies separately: identical content to the same (platform, accountId) within 24h returns HTTP 409 with details.accountId, details.platform, details.existingPostId.
May 15, 2026Improvement
{adId} in GET /v1/ads/{adId} and GET /v1/ads/{adId}/comments now accepts multiple identifier types.

You can call these endpoints using the Zernio internal id (24-char hex), Meta numeric platformAdId (from comment.received as comment.ad.id), or the creative’s effectiveobjectstoryid / effectiveinstagrammedia_id. Any of these resolves to the same ad, so you don’t need a translation step.
May 15, 2026New Feature
Webhooks now support per-platform terminal post events: post.platform.published and post.platform.failed.

These fire once per platform target inside a post when that specific platform reaches a terminal state (published or permanent failure), which helps track multi-platform posts more precisely.

Subscribe via events on POST /v1/webhooks/settings or PUT /v1/webhooks/settings:
• post.platform.published
• post.platform.failed

A new webhook event is also available:
• whatsapp.template.status_updated
May 15, 2026New Feature
GET /v1/ads/campaigns now supports filtering aggregated campaign metrics by a date range.

Use fromDate and toDate (YYYY-MM-DD, inclusive) to control the metrics window returned for each campaign.

New query params:
• fromDate - start date (defaults to 90 days ago when both date params are omitted)
• toDate - end date (defaults to today; max 730-day range)
May 15, 2026New Feature
Webhooks now support WhatsApp template status updates via the new event whatsapp.template.status_updated.

This fires when Meta completes a (re)review of a template on a connected WABA, so you can react to approvals/rejections and other status transitions.

Subscribe by adding whatsapp.template.status_updated to Webhook.events.

Payload: WebhookPayloadWhatsAppTemplateStatusUpdated with template.status ∈ APPROVED, REJECTED, PENDING, PAUSED, DISABLED, INAPPEAL, PENDINGDELETION and template.reason (e.g. "NONE" on approval).
May 15, 2026New Feature
Google Business reviews now support owner replies via new endpoints: POST /v1/accounts/{accountId}/gmb-reviews/{reviewId}/reply and DELETE /v1/accounts/{accountId}/gmb-reviews/{reviewId}/reply.

Use comment to post/update a reply (calling POST again overwrites the previous reply). Deleting removes only the reply, not the review.

Reply body: { "comment": "..." }

POST /v1/ads/ctwa now supports creating multiple CTWA ads under one campaign/ad set by sending a non-empty creatives[] array (each creative has its own headline, body, and exactly one of imageUrl/video). Single-creative requests still work.

New optional bidding controls on CTWA: bidStrategy (LOWESTCOSTWITHOUTCAP, LOWESTCOSTWITHBIDCAP, COSTCAP, LOWESTCOSTWITHMINROAS) plus bidAmount (required for bid-cap/cost-cap) and roasAverageFloor (required for min-ROAS).

CTWA create response is now a tagged union via adType: single or multi.
May 14, 2026New Feature
Webhooks now support ad.status_changed.

Subscribe to get notified when a Meta ads object (campaign/ad set/ad) changes status or enters WITH_ISSUES.

Add ad.status_changed to events when creating/updating a webhook via POST /v1/webhooks/settings or PUT /v1/webhooks/settings.

Payload highlights:
• event: ad.status_changed
• adObject.level: CAMPAIGN | AD_SET | AD
• status.raw: platform-native status (e.g. ACTIVE, PAUSED, WITH_ISSUES)
• error: optional diagnostics on most WITH_ISSUES events

Also, GET /v1/ads/{adId}/analytics now returns ad.currency (ISO 4217) so you can interpret money values in summary and daily.
May 13, 2026Improvement
GET /v1/ads/timeline now supports filtering by a specific platform ad account via adAccountId.

Use this when a single Zernio connection wraps multiple platform ad accounts and you want the timeline chart for just one (e.g. Meta act_…, TikTok advertiser ID).

New query param:
• adAccountId (optional)

Note: rows ingested before 2026-05-13 may not include this dimension yet; the recurring 7-day re-sync will repopulate them over time.
May 13, 2026New Feature
New endpoint GET /v1/ads/timeline returns a daily time series of aggregate ad metrics for a social account (one row per calendar day), useful for spend/conversions charts without calling /v1/ads/tree per day.

Key params:
• accountId (required)
• fromDate, toDate (YYYY-MM-DD; defaults to last 90 days; max 730 days)
• platform (optional): facebook, instagram, tiktok, linkedin, pinterest, google, twitter

Response: rows[] with per-day metrics like spend, impressions, clicks, ctr, conversions, actions, purchaseValue, roas. Returns empty rows if no activity. 403 if Ads add-on is required.
May 13, 2026New Feature
New endpoint: PATCH /v1/accounts/{accountId} to move a connected account to a different profile owned by the same user.

Send profileId in the JSON body to set the target profile.

Request body:
• profileId (required)

Auth/scope note: for profile-restricted API keys, both the account’s current profile and the target profileId must be allowed, otherwise you’ll get 403.

Responses: 200 returns message and profileId; errors include 400, 401, 403, 404.
May 12, 2026Improvement
TikTok conversion ads via POST /v1/ads/create now support/require TikTok pixel optimization settings through promotedObject.

For TikTok, goal=conversions uses promotedObject to configure the ad group’s pixel + optimization event (or inherit them by using adSetId).

Set:
• goal: conversions
• promotedObject.pixelId (required for TikTok conversions)
• promotedObject.customEventType (optional TikTok optimizationevent code, e.g. ONWEBORDER, INITIATEORDER, ONWEBCART, ONWEBREGISTER, FORM, ONWEBDETAIL)
May 12, 2026New Feature
POST /v1/ads/create now supports creating LinkedIn standalone ads.

This creates a LinkedIn Single Image Ad backed by a Direct Sponsored Content ("dark post") authored by a Company Page, with supported goals engagement, traffic, awareness (and traffic requires linkUrl).

Key request fields for LinkedIn:
• organizationId (Company Page org ID or urn:li:organization:N; required unless it can be inferred)
• headline (now required for LinkedIn; max 400)
• body (used as LinkedIn post commentary)
• imageUrl (required for LinkedIn)
• linkUrl (required for goal=traffic)
• longHeadline (optional secondary description on LinkedIn traffic ads)

Also updated:
• callToAction enum expanded with REGISTER, JOIN, ATTEND, REQUESTDEMO, VIEWQUOTE, APPLY, SEEMORE, BUYNOW
• Meta-only: new attributionSpec to control conversion attribution windows (CLICKTHROUGH/VIEWTHROUGH/ENGAGEDVIDEOVIEW with windowDays 1/7/28).
May 12, 2026New Feature
Meta ads (boosted/dark posts) are now included in GET /v1/inbox/comments for users with the Ads add-on (Metronome plans always qualify).

Ad rows are flagged so you can route comment fetching to the ads comments thread.

Use platform:
• metaads - ads only
• facebook/instagram - organic only
• omit platform - both

New fields on inbox rows: isAd (boolean), adId (string). For ad rows, fetch the thread via GET /v1/ads/{adId}/comments.

GET /v1/inbox/comments/{postId} now returns meta.adComments when a boosted post has no organic comments (includes adId and adCommentsUrl), and may return 400 with code USEADCOMMENTS_ENDPOINT when postId is an ad creative/ad ID.

GET /v1/ads adds filters to map Business Manager IDs back to Zernio ads: platformAdId, effectiveObjectStoryId, effectiveInstagramMediaId.
May 12, 2026Improvement
GET /v1/ads/{adId}/comments now returns additional Instagram metadata in meta for Instagram-placed ads.

This makes it easier to identify the boosted IG post and which connected Instagram account was used to read comments (useful for follow-up reply/hide actions via /v1/inbox/comments).

New meta fields (Instagram-only):
• instagramUserId
• instagramPermalink
• instagramAccountId

Also clarified: for Instagram-placed ads, if no connected Instagram account on the profile can read the ad’s media, the endpoint returns 422 with code adsconnectionrequired.
May 11, 2026New Feature
Meta Ads now supports managing Tracking Tags (Meta Pixels) via new endpoints.

You can list/create/get/update pixels, share/unshare them across ad accounts, and fetch aggregated event stats.

New endpoints:
• GET /v1/accounts/{accountId}/tracking-tags (optional adAccountId=act_...)
• POST /v1/accounts/{accountId}/tracking-tags body: adAccountId, name (not idempotent)
• GET /v1/accounts/{accountId}/tracking-tags/{tagId} (includes install code)
• PATCH /v1/accounts/{accountId}/tracking-tags/{tagId} fields: name, enableAutomaticMatching, automaticMatchingFields (em/ph/fn/ln/ge/db/ct/st/zp/country/externalid), firstPartyCookieStatus (empty/firstpartycookiedisabled/firstpartycookieenabled), dataUseSetting (advertisingandanalytics/analyticsonly/empty)
• GET|POST|DELETE /v1/accounts/{accountId}/tracking-tags/{tagId}/shared-accounts (DELETE supports adAccountId query)
• GET /v1/accounts/{accountId}/tracking-tags/{tagId}/stats (optional aggregation, startTime, endTime; aggregation enum includes event, url, devicetype, browsertype, etc.)

Notes: Meta-only (platform=metaads; other platforms return 405). Requires the Meta ads SocialAccount from the Ads add-on connect flow; list view omits code (use get to retrieve it).
May 11, 2026New Feature
Comment-to-DM automations now support inline DM buttons via buttons.

You can attach up to 3 buttons to the auto-DM (URL, postback, or phone on Facebook) to drive clicks or capture postbacks.

Use buttons in:
• POST /v1/comment-automations
• PATCH /v1/comment-automations/{automationId} (pass buttons: [] to clear)

Button schema: { type, title, url|payload|phone } where type is url | postback | phone (phone = Facebook-only), and title is max 20 chars.

Note: when buttons is non-empty, dmMessage must be ≤ 640 characters. Responses from list/get/create/update may now include buttons (omitted when none are set).
May 10, 2026Deprecation
X API pricing/usage reporting now includes a new $0.200 “posts with URL” operation and tier.

This lets you attribute X spend more accurately per action; new clients should rely on per-operation counts rather than tier aggregates.

Key changes:
• New operation in GET /v1/billing/x-pricing: operation=contentcreatewithurl with tier=xapi_200 and pricePerCallUsd=0.2
• contentcreate price moved to pricePerCallUsd=0.015 (now tier=xapi_015)
• In GET /v1/usage-stats, usage.xApiCalls is now deprecated and excludes xapi200 (and future tiers); use usage.xApiCallsByOperation as the source of truth
May 10, 2026Improvement
WhatsApp is now supported on POST /v1/inbox/conversations/{conversationId}/typing.

On WhatsApp, the typing indicator shows typing... for up to 25 seconds. It requires a recent inbound message in the conversation (Meta references the inbound message id) and will also mark that inbound message as read as a side-effect.

Use: { "accountId": "..." }
May 8, 2026New Feature
Message webhooks now include expanded Meta ad-click attribution in metadata.referral (in addition to WhatsApp CTWA).

This lets you attribute the first inbound message to a specific Facebook/Instagram ad click for CTM/CTD flows.

New fields in metadata.referral:
• ad_id
• ref
• source
• type
• adscontextdata (object: adtitle, photourl, videourl, postid, productid, flowid)

metadata.referral is now nullable and is only populated on the first inbound message after the click.
May 8, 2026New Feature
LinkedIn Ads now plugs into Zernio's unified Conversions API. Stream offline conversion events to LinkedIn's /rest/conversionEvents through the same /v1/ads/conversions endpoint you already use for Meta and Google.

New capabilities for linkedinads accounts:
• POST /v1/ads/conversions — stream events (BATCH_CREATE, up to 5,000 per call)
• GET /v1/accounts/{accountId}/conversion-destinations — list rules across every sponsored ad account
• POST /v1/accounts/{accountId}/conversion-destinations — create a new rule (with autoAssociationType=ALL_CAMPAIGNS by default)
• GET|PATCH|DELETE /v1/accounts/{accountId}/conversion-destinations/{destinationId} — single-rule CRUD (DELETE soft-deletes via enabled: false)
• GET|POST|DELETE .../associations — campaign↔conversion mapping
• GET .../metrics?startDate=&endDate=&granularity= — externalWebsiteConversions, conversionValueInLocalCurrency, qualifiedLeads, etc., bucketed by date

Supports the LinkedIn first-party click ID lifatid (added to user.clickIds) for higher match rates. Standard event names map to LinkedIn rule types (Lead → LEAD, Purchase → PURCHASE, ...); LinkedIn enum values pass through unchanged.

Reconnect required for LinkedIn accounts connected before this change — LinkedIn does not silently upgrade existing OAuth grants with the new rwconversions scope. Affected accounts return 403 linkedinreconnect_required until the user re-authorises.
May 8, 2026New Feature
Webhooks now support additional message events you can subscribe to when creating/updating webhook settings.

This lets you receive callbacks for outbound message lifecycle changes (send/edit/delete/delivery/read/failure), not just inbound messages.

Use events in POST /v1/webhooks/settings or PUT /v1/webhooks/settings with any of:
• message.sent, message.edited, message.deleted, message.delivered, message.read, message.failed

Also added to WebhookPayloadMessage.metadata (Instagram only): storyReply and isStoryMention.
May 6, 2026Improvement
Ads analytics endpoints now support up to a 730-day metrics date range (previously 90 days).

You can query longer historical performance, and requests for ranges older than 90 days may trigger a one-time on-demand fetch from the ad platform, then be served from cache on subsequent requests.

Applies to:
• GET /v1/ads (max range 730 days)
• GET /v1/ads/tree (max range 730 days)
• GET /v1/ads/{adId}/analytics (max range 730 days)

Use fromDate / toDate; ranges older than 90 days may require scoping to accountId (where noted) to trigger the on-demand fetch.
May 5, 2026New Feature
POST /v1/inbox/comments/{postId}/{commentId}/private-reply now supports inline buttons on the first private reply.

You can attach 1–3 buttons rendered in the same message bubble (Meta button_template). This is recommended for cold reach because quickReplies don’t render in Instagram Message Requests.

New request field: buttons (min 1, max 3), mutually exclusive with quickReplies.
Button types:
• url: { type, title, url }
• postback: { type, title, payload }
• phone (Facebook only): { type, title, phone }
May 4, 2026New Feature
WhatsApp headless OAuth now supports multi-phone WABA selection via a new two-step flow.

When the OAuth redirect includes step=selectphonenumber, first list available numbers with GET /v1/connect/whatsapp/select-phone-number using profileId + tempToken (optional X-Connect-Token).

Then bind the chosen number with POST /v1/connect/whatsapp/select-phone-number:
• profileId
• tempToken
• wabaId
• phoneNumberId

Note: GET /v1/connect/pending-data is now documented as only for LinkedIn organizations and Snapchat profiles; WhatsApp selection state is passed via redirect params and pending-data may return 404 for WhatsApp flows.
May 4, 2026New Feature
New endpoint GET /v1/billing/x-pricing returns Zernio’s canonical X/Twitter API pricing table (per-operation rates, 0% markup). Use it with GET /v1/usage-stats to compute exact X spend from per-operation call counts.

GET /v1/usage-stats now varies by billingSystem: stripe | metronome.
For metronome accounts it includes:
• usage.connectedAccounts
• usage.xApiCalls (tier buckets: xapi005 | xapi010 | xapi015)
• usage.xApiCallsByOperation (keys resolve via GET /v1/billing/x-pricing)
• spend.currentPeriodCents, spend.xSpendCents, spend.xSpendLimitCents

PUT /v1/accounts/{accountId} (X only, usage-based) accepts xCapabilities to opt into background metered operations:
• xCapabilities.analytics
• xCapabilities.inbox

Also: POST /v1/profiles and GET /v1/connect/{platform} may now return 402 PaymentRequired.
May 4, 2026New Feature
POST /v1/inbox/comments/{postId}/{commentId}/private-reply now supports optional quick reply chips on the first private reply.

You can include up to 13 chips to let users pick an intent in-app (Instagram/Messenger apps only).

New optional field: quickReplies (max 13)
• quickReplies[].title (max 20 chars)
• quickReplies[].payload
• quickReplies[].imageUrl (optional)
May 1, 2026Improvement
Account groups can now include accounts across multiple profiles. For API keys scoped to specific profiles, GET /v1/account-groups only returns groups whose accountIds are all in allowed profiles.

GET /v1/account-groups may now include createdBy and legacy profileId (only on older groups).

POST /v1/account-groups accepts deprecated profileId (ignored) for backward compatibility.

Meta ads connect can now be scoped to specific ad accounts via GET /v1/connect/{platform}/ads (metaads only): adAccountId (format act_<digits>) or adAccountIds (repeat or comma-separated). Response may include scopedAdAccountIds.

Webhook account.ads.initialsynccompleted now includes richer correlation + error details:
• account.platformAdAccountId, account.platformAdAccountIds
• sync.error, sync.errorCode, sync.errorSubcode, sync.errorCategory = tokeninvalid | permissiondenied | noadaccounts | ratelimited | discoveryfailed | unknown
May 1, 2026Breaking Change
POST /v1/account-groups no longer requires profileId in the request body.

You can now create an account group using only a name and the list of social account IDs.

Request body now requires:
• name
• accountIds
May 1, 2026New Feature
POST /v1/comment-automations now supports account-wide (any post) comment-to-DM automations.

You can omit platformPostId (and postId) to evaluate comments across every post on an account. Multiple account-wide automations can be stacked and run independently; per-post automations take priority on their post.

Key request rules:
• Per-post: set platformPostId and include postId
• Account-wide: omit platformPostId (and postId)
• matchMode: exact | contains

Note: 409 now applies only to per-post automations (duplicate active platformPostId).
April 30, 2026New Feature
New Meta geo targeting lookup endpoint: GET /v1/ads/targeting/search.

It lets you resolve a human-readable location name into Meta’s opaque location key for use in ad targeting.

Call with accountId, q, optional type (country/region/city/subcity/neighborhood/zip/metroarea/geomarket, default city), optional countryCode, and optional limit.

POST /v1/ads/create now supports Meta-only geo targeting via cities[] and regions[]:
• cities[]: { key, radius?, distanceunit? } where distanceunit is mile or kilometer
• regions[]: { key }

countries[] is now documented as ISO-2 codes and defaults to ["US"] when no cities/regions are provided.
April 30, 2026Improvement
TikTok ads created via POST /v1/ads/create now honor callToAction (it’s passed through to the Spark Ad creative’s calltoaction).

This lets you set a CTA on TikTok using the same field you already use for Meta.

Set callToAction to one of:
• LEARNMORE, SHOPNOW, SIGNUP, BOOKTRAVEL, CONTACTUS, DOWNLOAD, GETOFFER, GETQUOTE, SUBSCRIBE, WATCHMORE
April 30, 2026Breaking Change
TikTok boost ads now surface clearer requirements and failure modes in POST /v1/ads/boost.

For TikTok boosts, include targeting.countries (ISO country codes). TikTok requires locations on the ad group; other platforms can omit it.

The 422 response can now also indicate the connected TikTok user isn’t authorized as an Identity on the target advertiser (returned with code adsconnectionrequired and an actionable remediation message).

Also note: BusinessCenter.advertiserCount is now nullable (null when the BC asset walk is empty/failed, distinct from 0).
April 30, 2026Improvement
Analytics endpoints now support filtering by social account via accountId.

This lets you scope results to a specific connected account (useful when a profileId contains multiple accounts).

Added optional query param accountId on:
• GET /v1/analytics/best-time
• GET /v1/analytics/content-decay
• GET /v1/analytics/posting-frequency
April 29, 2026New Feature
New Ads endpoints and expanded TikTok support.

You can now list TikTok Business Centers.

• List TikTok BCs: GET /v1/ads/business-centers with accountId → returns businessCenters[] (bcId, name, advertiserCount)

TikTok campaign duplication is now supported via POST /v1/ads/campaigns/{campaignId}/duplicate by setting platform to tiktok (Meta still supported).

Ad updates now support more platforms/fields:
• PUT /v1/ads/{adId} supports targeting + creative on facebook/instagram/tiktok. Other platforms return 501 (unsupportedplatformoperation) when sending targeting or creative.

Ad account listing now supports filtering/limiting:
• GET /v1/ads/accounts adds optional adAccountId and limit

TikTok boost now supports cross-creator Spark Ads:
• POST /v1/ads/boost adds sparkAuthCode

Standalone ad creation “attach” mode now supports TikTok:
• POST /v1/ads/create adSetId is now supported for tiktok (ad group ID)

New response fields:
• Ad adds platformAdAccountName and platformCreatedAt
• AdCampaign and AdTreeCampaign add platformAdAccountName
April 29, 2026New Feature
Ads API now supports setting bid strategy on campaigns, ad sets, and boosted posts.

You can update campaign-level defaults via PUT /v1/ads/campaigns/{campaignId} using bidStrategy (optionally alongside budget). Ad sets can now be updated via PUT /v1/ads/ad-sets/{adSetId} with bidStrategy plus bidAmount or roasAverageFloor when required.

Set bidStrategy to:
• LOWESTCOSTWITHOUT_CAP
• LOWESTCOSTWITHBIDCAP (requires bidAmount)
• COST_CAP (requires bidAmount)
• LOWESTCOSTWITHMINROAS (requires roasAverageFloor)

Boosting posts via POST /v1/ads/boost now accepts bidStrategy, bidAmount, and roasAverageFloor. TikTok boosts also add linkUrl and callToAction.

Ad read models now include bidStrategy (normalized for Meta + TikTok) and may include bidAmount / roasAverageFloor on Ad, AdCampaign, and AdTree* responses.

GBP location details (GET /v1/accounts/{accountId}/gmb-location-details) now always includes title + metadata and always populates the derived location summary; passing location in readMask returns 400.
April 29, 2026New Feature
GET /v1/accounts/{accountId}/gmb-location-details now returns a new location summary object in the 200 response.

This provides a compact, public-facing view derived from GBP metadata, making it easy to surface “leave a review” and Maps URLs without parsing the raw metadata block.

New fields under location:
• name
• placeId
• reviewUrl
• mapsUri
• isVerified

Populated when readMask includes metadata (default). For unverified/new locations, placeId/reviewUrl/mapsUri may be null.
April 28, 2026Improvement
GET /v1/ads/accounts now returns Meta ad account timezone details: timezoneName and timezoneOffsetHoursUtc.

Use these fields to align daily budget reset timing and Insights day boundaries with the ad account’s local timezone.

New fields in each item of accounts[]:
• timezoneName (IANA timezone, Meta only)
• timezoneOffsetHoursUtc (signed UTC offset in hours, reflects current DST, Meta only)
April 28, 2026Improvement
Meta ads now support DSA (EU) advertiser disclosures via two new optional fields on ad creation endpoints.

When your targeting intersects EU member states, Meta may require these values (enforced server-side) to comply with DSA Article 26.

Set:
• dsaBeneficiary — legal entity benefiting from the ad
• dsaPayor — legal entity paying for the ad (Meta spelling: dsa_payor)

Available on:
• POST /v1/ads/boost
• POST /v1/ads/create
• POST /v1/ads/ctwa
April 27, 2026Improvement
GET /v1/inbox/conversations/{conversationId}/messages now supports cursor-based pagination and sort order control.

You can page through long conversations using an opaque cursor, and request oldest-first or newest-first results (with platform-specific limitations).

New query params:
• limit (1–100, default 100)
• cursor (pass prior pagination.nextCursor)
• sortOrder: asc | desc (default asc)

New response fields:
• pagination.hasMore
• pagination.nextCursor
• sortOrderApplied: asc | desc

Also: WebhookPayloadMessage.metadata adds referral (WhatsApp only) for Click-to-WhatsApp ad attribution on the first inbound message after a CTWA ad click (e.g. ctwaclid, sourceurl, imageurl/videourl).
April 27, 2026New Feature
POST /v1/ads/create now supports Meta-only gender targeting via gender.

Use this to restrict the audience by gender when creating Meta (facebook/instagram) ads; non-Meta platforms ignore it.

Set gender to: • all (default) • male • female
April 27, 2026New Feature
New endpoints added for Click-to-WhatsApp ads and WhatsApp conversion attribution.

Create CTWA ads on Meta in one call via POST /v1/ads/ctwa (creates campaign → ad set → creative → ad).
Key fields: accountId, adAccountId, name, headline, body, budgetAmount, budgetType (daily | lifetime), and exactly one of imageUrl or video (url, thumbnailUrl). Optional: objective (OUTCOMEENGAGEMENT | OUTCOMESALES | OUTCOME_LEADS).

Send WhatsApp conversation conversion events to Meta CAPI (business messaging) via POST /v1/whatsapp/conversions.
Key fields: accountId, eventName (LeadSubmitted | Purchase | AddToCart | InitiateCheckout | ViewContent), eventId, optional eventTime, and at least one of conversationId or phoneE164. Note: returns 422 if no captured ctwa_clid (no attribution possible); on 200 check eventsFailed and failures[] for Meta rejections.
April 27, 2026Improvement
GET /v1/posts now supports filtering by social account via the optional query param accountId.

Use it to return only posts published via a specific connected account (useful when multiple accounts exist per platform/profile).

New parameter:
• accountId (string, 24-char hex ObjectId) — filter posts to those published via a specific social account
April 27, 2026New Feature
Webhooks now support the new event account.ads.initialsynccompleted.

This fires once per ads-enabled account when the initial discovery + 90-day ads backfill finishes (including partial success or failure), so you can trigger downstream processing when the initial ads dataset is ready.

Subscribe via events on:
• POST /v1/webhooks/settings
• PUT /v1/webhooks/settings

Event: account.ads.initialsynccompleted
Payload highlights: account.accountId, account.profileId, account.platform, sync.status (success | failure), sync.totalAds, sync.synced, sync.failed, timestamp.
April 26, 2026Improvement
Comment automation logs now include separate results for the DM and the optional public comment reply in GET /v1/comment-automations/{automationId} and GET /v1/comment-automations/{automationId}/logs.

You can now track whether the DM was sent and whether the public reply was attempted/sent, with separate error messages.

New log fields:
• status (sent | failed | skipped) — DM outcome
• error — DM error if status is failed
• commentReplyStatus (sent | failed | skipped) — public reply outcome
• commentReplyError — public-reply error if commentReplyStatus is failed
April 24, 2026New Feature
New analytics endpoints are available for YouTube, LinkedIn org pages, TikTok, Facebook Pages, and Instagram follower history. All new endpoints reuse the same response envelope as /v1/analytics/instagram/account-insights for consistent client handling.

YouTube channel totals (no per-video looping): GET /v1/analytics/youtube/channel-insights
Key params: accountId, metrics, since, until, metricType (totalvalue|timeseries)
Notes: requires yt-analytics.readonly (412 if missing) + Analytics add-on; data is delayed 2–3 days (range clamped).

LinkedIn organization page aggregate analytics: GET /v1/analytics/linkedin/org-aggregate-analytics
Key params: accountId, metrics, since, until, metricType (totalvalue|timeseries)
Notes: requires scopes rorganizationsocial + rorganizationfollowers + rorganizationadmin (412 if missing) + Analytics add-on; page-view metrics are total_value only.

TikTok account-level insights: GET /v1/analytics/tiktok/account-insights
Key params: accountId, metrics, since, until, metricType (totalvalue|timeseries)
Notes: requires user.info.stats (412 if missing) + Analytics add-on.

Facebook Page insights (post-Nov-2025 Meta metric names): GET /v1/analytics/facebook/page-insights
Key params: accountId, metrics, since, until, metricType (totalvalue|timeseries)
Notes: deprecated Meta metrics (pageimpressions, pagefans, pagefanadds, pagefanremoves) are rejected; use pagemediaview, pagefollows, etc. followersgained/followers_lost are synthesized.

Instagram follower history (daily follower count time series): GET /v1/analytics/instagram/follower-history
Key params: accountId, metrics (default followercount,followersgained,followerslost), since, until, metricType (totalvalue|time_series)

Also added for LinkedIn personal analytics:
• GET /v1/accounts/{accountId}/linkedin-aggregate-analytics now supports POSTSAVE and POSTSEND in metrics
• GET /v1/accounts/{accountId}/linkedin-post-analytics now returns saves and sends (personal only; org returns 0)
April 23, 2026New Feature
LinkedIn post analytics now include saves and sends.

LinkedIn added POSTSAVE and POSTSEND to memberCreatorPostAnalytics in their 202604 API version. Both fields are now returned on:
• GET /v1/accounts/{accountId}/linkedin-post-analytics
• GET /v1/accounts/{accountId}/linkedin-aggregate-analytics (pass POSTSAVE and/or POSTSEND via the metrics query param, or omit metrics to get them automatically)

Scope: personal LinkedIn accounts only. LinkedIn did not add these metrics to the organization/company-page analytics endpoint, so organization accounts return 0 for saves and sends. No OAuth reconnect required, the existing rmemberpostAnalytics scope already covers it.
April 22, 2026Breaking Change
POST /v1/ads/create now supports 3 mutually-exclusive request shapes (and has updated required fields).

You can now (Meta-only) create multiple ads in one call via creatives[], or attach a new ad to an existing ad set via adSetId.

Key request options:
• Legacy single-creative: use top-level goal, budgetAmount, budgetType, headline, body, imageUrl, linkUrl, callToAction
• Multi-creative (Meta only): set creatives[] (min 1). Each item requires headline, body, imageUrl, linkUrl, callToAction
• Attach (Meta only): set adSetId + a single creative at top-level; goal/budgetAmount/budgetType are inherited

creatives[] and adSetId are mutually exclusive (400 if both). Non-Meta platforms with creatives[]/adSetId return 400.

Response change: 201 may return either ad (legacy/attach) or ads[] + platformCampaignId + platformAdSetId (multi-creative).
April 22, 2026Improvement
Ad metrics now include revenue and ROAS fields for Meta insights.

You can read monetary action totals via actionValues (mirrors actions, values in ad-account currency), plus convenience rollups:
• purchaseValue — summed purchase-type action values
• roas — purchaseValue / spend

Meta-only; other platforms return {} for actionValues and 0 for purchaseValue/roas.
April 21, 2026New Feature
New Ads API write endpoints are available for managing campaigns and ad sets (budget updates, pause/resume, duplication, deletion). This adds first-class support for CBO vs ABO budget routing and bulk status changes.

Campaign budget updates (CBO): PUT /v1/ads/campaigns/{campaignId}
• Body: platform (facebook|instagram), budget.amount, budget.type (daily|lifetime)
• If the campaign is ABO, returns 409 (BUDGETLEVELMISMATCH) — update the ad set instead

Ad set updates (ABO budget and/or status): PUT /v1/ads/ad-sets/{adSetId}
• Body: platform, optional budget, optional status (active|paused)
• If parent campaign is CBO and you try to update budget here, returns 409 (BUDGETLEVELMISMATCH)

Convenience ad set status toggle: PUT /v1/ads/ad-sets/{adSetId}/status
• Body: platform, status (active|paused)

Bulk pause/resume campaigns (up to 50): POST /v1/ads/campaigns/bulk-status
• Body: status (active|paused), campaigns[] with platformCampaignId + platform
• Returns per-campaign results so one failure doesn’t fail the whole batch

Duplicate a campaign (Meta async copy + optional discovery): POST /v1/ads/campaigns/{campaignId}/duplicate
• Key options: deepCopy (default true), statusOption (ACTIVE|PAUSED|INHERITEDFROMSOURCE), syncAfter (default true)

Delete a campaign (cascades to ad sets/ads): DELETE /v1/ads/campaigns/{campaignId}
• Body: platform (facebook|instagram)

Also added to campaign responses (GET /v1/ads/campaigns and GET /v1/ads/tree):
• reviewStatus (inreview|approved|rejected|withissues)
• budgetLevel (campaign|adset), campaignBudget, and currency to disambiguate CBO vs ABO budgets

Meta-only for now where noted; other platforms may return 501 for unsupported operations.
April 21, 2026Improvement
Ad metrics now include conversion reporting and raw action breakdowns via new fields on AdMetrics.

This lets you read conversion counts/cost directly and also extract any Meta action/conversion type from Insights without relying only on derived metrics.

New fields:
• conversions (integer) — conversion events matching the campaign’s promoted event type (Meta-only for now; others return 0)
• costPerConversion (number) — spend / conversions (0 when conversions is 0)
• actions (object<string, integer>) — per actiontype counts (e.g. linkclick, offsiteconversion.fbpixel_purchase)
April 21, 2026New Feature
Announcing Zernio Ads. Run paid social campaigns across 6 platforms directly through the Zernio API.

You can now boost organic posts, create standalone ads with custom creative, manage Campaign / Ad Set / Ad hierarchies, build custom audiences, target by interests and geography, pull analytics, and relay conversion events, all through one unified API.

Supported platforms: Meta (Facebook, Instagram), Google Ads, TikTok, LinkedIn, Pinterest, X/Twitter.

Endpoints:
• POST /v1/ads/boost: turn an organic post into a paid ad
• POST /v1/ads/create: create standalone ads with custom creative
• GET /v1/ads: list ads with spend, impressions, clicks, CTR, CPC, CPM (up to 90 days)
• GET /v1/ads/tree: nested Campaign > Ad Set > Ad view
• GET /v1/ads/campaigns: aggregated campaign view
• GET /v1/ads/audiences, POST /v1/ads/audiences: list and create custom audiences (Meta lookalike, customer list, website pixel)
• POST /v1/ads/sync: pull external ads from platforms into Zernio
• POST /v1/ads/conversions: unified Conversions API relay (Meta, Google Ads)
• GET /v1/connect/{platform}/ads: OAuth connection flow for ad accounts

Targeting supports age (13 to 65), countries, interests, custom audiences, and Meta's Advantage Audience. Goals include engagement, traffic, awareness, videoviews, leadgeneration, conversions, and app_promotion (availability varies by platform).

Requires the Ads add-on on your account.
April 20, 2026Improvement
Meta ads now support Advantage Audience controls when creating/boosting ads and when updating Meta targeting.

This lets you explicitly enable/disable Meta’s targeting automation during ad set creation, and optionally preserve the current setting on updates.

Use:
• POST /v1/ads/boost: targeting.advantage_audience = 0 | 1
• PUT /v1/ads/{adId}: targeting.advantage_audience = 0 | 1 (omit to keep existing)
• POST /v1/ads/create: advantageAudience = 0 | 1
April 20, 2026Improvement
PUT /v1/inbox/conversations/{conversationId} can now return 404.

This happens when the conversation isn’t found on WhatsApp (other platforms upsert instead of returning not found).

Handle the new response: 404 Conversation not found.
April 20, 2026New Feature
Reddit posting now supports native video submissions via new fields in RedditPlatformData.

You can upload videos to Reddit’s CDN for an embedded player (with automatic fallback to a link post if video posts aren’t allowed), or submit silent looping clips.

New fields:
• nativeVideo (boolean, default true) - use Reddit native video flow; set false to fall back to a legacy link post
• videogif (boolean) - submit as silent videogif when nativeVideo is active
• videoPosterUrl (uri) - optional thumbnail/poster image for native video posts
April 17, 2026New Feature
Webhooks now support review events via events: review.new, review.updated (in POST /v1/webhooks/settings and PUT /v1/webhooks/settings).

Sending inbox messages now supports WhatsApp rich interactive messages via interactive in POST /v1/inbox/conversations/{conversationId}/messages.

Set interactive.type to: list, cta_url, or flow (WhatsApp only). When interactive is set, it takes priority over buttons and quickReplies.

Interactive taps/submissions are delivered in the message.received webhook under metadata: interactiveType (buttonreply | listreply | nfm_reply), interactiveId, flowResponseJson, flowResponseData.
April 16, 2026New Feature
New Google Business Profile (GBP) capabilities are available: manage Services, update Place Action links, and fetch Reviews across multiple locations in one call.

Manage location services:
• GET /v1/accounts/{accountId}/gmb-services (optional locationId)
• PUT /v1/accounts/{accountId}/gmb-services replaces the full list via serviceItems (structured or free-form, optional price)

Update a Place Action link:
• PATCH /v1/accounts/{accountId}/gmb-place-actions with name (required), and optionally uri and/or placeActionType
• placeActionType: APPOINTMENT, ONLINEAPPOINTMENT, DININGRESERVATION, FOODORDERING, FOODDELIVERY, FOODTAKEOUT, SHOPONLINE

Batch fetch reviews for multi-location accounts:
• POST /v1/accounts/{accountId}/gmb-reviews/batch with locationNames, optional pageSize (max 50) and pageToken
April 16, 2026New Feature
Google Business Platform posts now support EVENT and OFFER types via topicType in GoogleBusinessPlatformData.

This lets you publish event and offer posts (in addition to standard updates) to Google Search/Maps/GBP.

Set topicType to: STANDARD | EVENT | OFFER (default: STANDARD)

When topicType is EVENT, include event (required): event.title, event.schedule.startDate, event.schedule.endDate (optional: startTime, endTime).

When topicType is OFFER, include offer (recommended): offer.offerType (OFFER | BUYONEGET_ONE), offer.redeemOnlineUrl, offer.termsConditions, offer.couponCode.
April 16, 2026Breaking Change
Webhook comment.received payloads may now include null for Zernio internal post IDs when the post was not published through Zernio.

Handle missing internal IDs by relying on the platform IDs instead.

Fields that can now be null:
• comment.postId
• post.id

Use comment.platformPostId / post.platformPostId as the stable identifiers in these cases.
April 16, 2026New Feature
Discord support has been added: you can now manage Discord account settings, list available guild channels, and post to Discord with Discord-specific options.

Manage Discord connection settings:
• GET /v1/accounts/{accountId}/discord-settings (read current channel/guild + webhook identity)
• PATCH /v1/accounts/{accountId}/discord-settings (update webhookUsername/webhookAvatarUrl and/or switch channelId)
• GET /v1/accounts/{accountId}/discord-channels (discover channels: type = 0/5/15)

Post to Discord via POST /v1/posts using platformSpecificData = DiscordPlatformData:
• Required: channelId
• Optional: embeds, poll, crosspost, forumThreadName, forumAppliedTags, threadFromMessage, tts, webhookUsername, webhookAvatarUrl

Analytics responses now include platformPostId per platform in GET /v1/analytics.

Ads goals have been expanded in POST /v1/ads/boost and POST /v1/ads/create: goal now supports engagement, traffic, awareness, videoviews, leadgeneration, conversions, app_promotion (availability varies by platform).
April 15, 2026New Feature
New Ads Conversions API endpoints are available: POST /v1/ads/conversions and GET /v1/accounts/{accountId}/conversion-destinations.

You can now relay conversion events to Meta or Google Ads; platform is inferred from accountId. List valid destinations first, then send a batch of events.

Use GET /v1/accounts/{accountId}/conversion-destinations to get destinations[].id (use as destinationId) and destinations[].status (active/inactive). For Google, destinations[].type is present (event type locked to the conversion action).

Send events via POST /v1/ads/conversions with accountId, destinationId, events[] (each requires eventName, eventTime, eventId, user). Optional: testCode (Meta only) and batch-level consent (adUserData/adPersonalization: GRANTED/DENIED, Google only).

Response includes platform (metaads/googleads), eventsReceived, eventsFailed, failures[], and traceId. Requires the Ads add-on.
April 14, 2026Improvement
Meta ads now return additional campaign/ad set details in ad reporting responses.

You can use these fields to display objective/optimization/bidding context and conversion event setup alongside metrics.

New fields:
• On Ad: platformObjective, optimizationGoal, bidStrategy, promotedObject
• On AdTreeCampaign and AdCampaign: platformObjective, optimizationGoal (can be string or string[]), bidStrategy, promotedObject
• On AdTreeAdSet: optimizationGoal, bidStrategy, promotedObject

promotedObject may include customeventtype, pixelid, pageid (and on Ad also applicationid, productset_id). Fields are null when not applicable and are only present for Meta ads.
April 14, 2026New Feature
Discord is now supported as a platform in several endpoints and response schemas.

You can now use platform=discord to connect accounts, validate content, and filter account health.

Updated enums:
• GET /v1/connect/{platform}: {platform} includes discord
• POST /v1/tools/validate/post: platforms[].platform includes discord
• GET /v1/accounts/health: query platform includes discord
• Schemas: SocialAccount.platform, PostLog.platform, ConnectionLog.platform include discord
April 13, 2026Improvement
GET /v1/ads now supports filtering by platform ad account via adAccountId.

Use this to scope results to a specific platform ad account (e.g. Meta act_123), consistent with /v1/ads/campaigns and /v1/ads/tree.

New query param: adAccountId

The Ad schema also adds Meta creative metadata to improve previews for video/moderation states:
• creative.videoId (nullable)
• creative.videoUrl (nullable)
• creative.objectType
April 13, 2026New Feature
Webhooks and Inbox messages now support message lifecycle updates: message.edited, message.deleted, message.delivered, message.read, message.failed.

You can subscribe to these via Webhook.events and filter webhook logs with GET /v1/webhooks/logs using event values: message.edited, message.deleted, message.delivered, message.read, message.failed.

GET /v1/inbox/conversations/{conversationId}/messages now returns additional fields for message state:
• Edit tracking: isEdited, editedAt, editCount, editHistory
• Delete tracking: isDeleted, deletedAt
• Delivery tracking: deliveryStatus (sent/delivered/read/failed/deleted), sentAt, deliveredAt, readAt, deliveryError
April 12, 2026Breaking Change
POST /v1/connect/facebook/select-page now requires userProfile in the request body.

If you were previously sending only profileId, pageId, and tempToken, you must now also include userProfile (decoded from the OAuth callback) to complete the Facebook Page selection flow.

Required fields: profileId, pageId, tempToken, userProfile.
April 9, 2026New Feature
Webhooks now support message sent events via message.sent.

Use this to get notified when a message is sent via the API, including message + conversation context.

Subscribe by adding message.sent to Webhook.events.

You can also filter/view deliveries in logs with GET /v1/webhooks/logs using event=message.sent.

Payload schema: WebhookPayloadMessageSent (includes message, conversation, account, timestamp).
April 8, 2026Breaking Change
Thread publishing behavior changed for X (Twitter), Threads, and Bluesky when using threadItems.

If threadItems is provided, the top-level content is now used only for display/search and is NOT published. You must include the first post as threadItems[0].

Key fields:
• platformSpecificData.twitter.threadItems
• platformSpecificData.threads.threadItems
• platformSpecificData.bluesky.threadItems
April 5, 2026Improvement
Facebook now supports posting an optional first comment for Reels as well as feed posts.

Use platformSpecificData.facebook.firstComment to add a comment immediately after publishing (not supported for contentType=story). Skipped when draft is true.

Applies when contentType is reel (or omitted for feed posts).
April 4, 2026New Feature
You can now edit a published X (Twitter) post via POST /v1/posts/{postId}/edit.

This updates the post on X and stores edit history in Zernio. X constraints apply (Premium required, 1-hour edit window, max 5 edits, text-only).

Request body:
• platform: twitter
• content: new text

Response includes:
• id (new tweet ID after edit)
• url

You can now start/send X DMs via POST /v1/inbox/conversations.

If a thread already exists, the message is appended. If the recipient doesn’t accept DMs, you’ll get 422 with code=DMNOTALLOWED (or bypass the pre-check with skipDmCheck).

Key fields:
• accountId
• participantId or participantUsername
• message (or attachment via multipart/form-data)
• skipDmCheck

Also added:
• Root-level facebookSettings on POST /v1/posts and PUT /v1/posts/{postId} (e.g. facebookSettings.draft=true to create an unpublished draft in Facebook Publishing Tools)
• TwitterPlatformData.longVideo=true to enable long video uploads (requires X Premium; may require allowlisting).
April 4, 2026New Feature
New Google Business Profile analytics endpoints are available.

Fetch daily performance metrics (impressions, clicks, calls, directions, etc.) via GET /v1/analytics/googlebusiness/performance. Requires accountId and the Analytics add-on; data may be delayed 2–3 days; up to 18 months history.

Key params:
• accountId (required)
• metrics (comma-separated; defaults to all): BUSINESSIMPRESSIONSDESKTOPMAPS, BUSINESSIMPRESSIONSDESKTOPSEARCH, BUSINESSIMPRESSIONSMOBILEMAPS, BUSINESSIMPRESSIONSMOBILESEARCH, BUSINESSCONVERSATIONS, BUSINESSDIRECTIONREQUESTS, CALLCLICKS, WEBSITECLICKS, BUSINESSBOOKINGS, BUSINESSFOODORDERS, BUSINESSFOODMENU_CLICKS
• startDate, endDate (YYYY-MM-DD)

Fetch monthly search keywords via GET /v1/analytics/googlebusiness/search-keywords. Requires accountId and the Analytics add-on; up to 18 months history.

Key params:
• accountId (required)
• startMonth, endMonth (YYYY-MM)

Note: keywords below Google’s minimum impression threshold are excluded.
April 3, 2026New Feature
YouTube analytics now supports audience demographics via GET /v1/analytics/youtube/demographics.

Fetch demographic insights for a YouTube channel broken down by age/gender (percentages) and country (view counts). Requires the Analytics add-on and YouTube Analytics scope.

Key params:
• accountId (required)
• breakdown = age, gender, country (comma-separated; defaults to all)
• startDate, endDate

WhatsApp now supports Flows (create/manage/publish and send interactive flow messages).

Manage flows:
• GET /v1/whatsapp/flows (accountId)
• POST /v1/whatsapp/flows (accountId, name, categories, optional cloneFlowId)
• GET /v1/whatsapp/flows/{flowId} (accountId, optional fields)
• PATCH /v1/whatsapp/flows/{flowId} (accountId, name/categories; DRAFT only)
• DELETE /v1/whatsapp/flows/{flowId} (accountId; DRAFT only)

Flow JSON asset:
• GET /v1/whatsapp/flows/{flowId}/json (accountId)
• PUT /v1/whatsapp/flows/{flowId}/json (accountId, flow_json; DRAFT only)

Lifecycle:
• POST /v1/whatsapp/flows/{flowId}/publish (accountId; irreversible)
• POST /v1/whatsapp/flows/{flowId}/deprecate (accountId; irreversible)

Send a flow message:
• POST /v1/whatsapp/flows/send with accountId, to, flowid, flowcta, body
• Optional flowaction = navigate | dataexchange, flowtoken, flowaction_payload, draft
April 1, 2026Improvement
YouTube metadata updates now support additional fields on POST /v1/posts/{postId}/update-metadata.

You can now set COPPA compliance, disclose AI/synthetic media, and add a video to a playlist without re-uploading.

New optional properties:
• madeForKids (boolean)
• containsSyntheticMedia (boolean)
• playlistId (string)
March 31, 2026New Feature
YouTube metadata updates now support setting a custom thumbnail via POST /v1/posts/{postId}/update-metadata.

You can update the thumbnail without re-uploading, including for existing videos not published through Zernio (direct video ID mode).

Use thumbnailUrl (public image URL) with platform youtube. In direct mode also include videoId + accountId.

Key params: platform (youtube), thumbnailUrl, videoId, accountId
March 30, 2026New Feature
YouTube posts now support adding a video to a playlist via playlistId in YouTubePlatformData.

Use the new playlist endpoints to discover playlist IDs and (optionally) store a client-side default for an account.

New endpoints:
• GET /v1/accounts/{accountId}/youtube-playlists → returns playlists[] and defaultPlaylistId
• PUT /v1/accounts/{accountId}/youtube-playlists with defaultPlaylistId (optional defaultPlaylistName)

New field:
• platformSpecificData.youtube.playlistId (string) — optional playlist to add the video to after upload
March 26, 2026New Feature
POST /v1/posts/{postId}/update-metadata now supports updating YouTube video metadata even if the video was uploaded outside Zernio.

Use one of two modes:
• Post-based: call with the real {postId} and platform
• Direct video ID: set {postId} to _ and include videoId + accountId + platform

Key params:
• platform: youtube
• videoId (direct mode)
• accountId (direct mode)

In direct mode, the 200 response may include videoId.
March 26, 2026New Feature
New endpoint: POST /v1/posts/{postId}/update-metadata to update metadata on an already-published post without re-uploading media.

Currently supported for YouTube videos only; the post must be published on the target platform and you must provide at least one updatable field.

Request body: platform (youtube) plus any of title, description, tags, categoryId, privacyStatus (public | private | unlisted).
March 24, 2026New Feature
Threads posts now support an explicit topic tag via platformSpecificData.topic_tag.

Use this to categorize posts for discoverability on Threads; when provided, it overrides auto-extraction from content hashtags.

Set topic_tag (string):
• 1–50 characters
• cannot contain . or &
March 23, 2026Improvement
Analytics endpoints now support filtering by social account via accountId.

This lets you scope results to a specific connected account (useful when a profileId contains multiple accounts).

New query parameter:
• GET /v1/analytics: accountId
• GET /v1/analytics/daily-metrics: accountId
March 23, 2026New Feature
You can now send inbox attachments by URL when calling POST /v1/inbox/conversations/{conversationId}/messages (JSON body), without using multipart upload.

Provide attachmentUrl (publicly accessible) and optionally attachmentType (image, video, audio, file).

Twitter posts now support polls via TwitterPlatformData.poll.

Set poll.options (2-4 items, max 25 chars each) and poll.duration_minutes (5-10080). Polls are mutually exclusive with media attachments and threads.
March 22, 2026New Feature
Instagram Analytics now supports account-level insights and audience demographics.

Fetch account performance totals or daily reach via GET /v1/analytics/instagram/account-insights.
Key params: accountId (required), metrics, since, until, metricType (timeseries | totalvalue), breakdown (totalvalue only). Note: timeseries only works with reach. Max range: 90 days (default last 30). Data may be delayed up to 48 hours. Requires Analytics add-on.

Fetch audience breakdowns via GET /v1/analytics/instagram/demographics.
Key params: accountId (required), metric (followerdemographics | engagedaudiencedemographics), breakdown (age, city, country, gender), timeframe (thisweek | this_month). Requires 100+ followers, returns top 45 per dimension, and may be delayed up to 48 hours. Requires Analytics add-on.
March 21, 2026New Feature
TikTok video posts now support a custom cover image via videoCoverImageUrl in TikTokPlatformData.

Provide a thumbnail image URL (JPG/PNG/WebP, max 20MB). When set, it overrides videoCoverTimestampMs.

Use:
• videoCoverImageUrl - custom thumbnail image URL
• videoCoverTimestampMs - frame timestamp (ms), ignored if videoCoverImageUrl is provided
March 21, 2026New Feature
New endpoint: GET /v1/accounts/{accountId}/tiktok/creator-info.

You can now fetch TikTok creator details plus the allowed posting options for an account (privacy levels, interaction settings, posting limits, and commercial content disclosures). Useful for validating post settings before publishing.

Key params:
• accountId (path)
• mediaType (query, default video): video, photo

Also added to TikTokPlatformData for video posts: videoCoverImageUrl (custom thumbnail URL). When set, it overrides videoCoverTimestampMs.
March 16, 2026Breaking Change
GET /v1/analytics now supports richer analytics sync states and more sorting options.

Single-post lookups can return 202 when analytics sync is still pending, or 424 when analytics are unavailable because the post failed on all platforms.

Key changes:
• Date defaults/limits: fromDate defaults to 90 days ago, toDate defaults to today, max range 366 days
• Sorting: sortBy now supports date, engagement, impressions, reach, likes, comments, shares, saves, clicks, views
• Single post response adds latePostId, syncStatus (synced/pending/partial/unavailable) and message; status can be published/failed/partial
• Per-platform analytics now include syncStatus (synced/pending/unavailable), platformPostUrl, and errorMessage; analytics may be null
• List responses add posts[].latePostId, posts[].profileId, and overview.dataStaleness
March 13, 2026New Feature
Late API now supports X/Twitter engagement actions: retweet, bookmark, and follow (plus undo/remove/unfollow).

Use:
• Retweet: POST /v1/twitter/retweet with accountId, tweetId
• Undo retweet: DELETE /v1/twitter/retweet with accountId, tweetId
• Bookmark: POST /v1/twitter/bookmark with accountId, tweetId
• Remove bookmark: DELETE /v1/twitter/bookmark with accountId, tweetId
• Follow: POST /v1/twitter/follow with accountId, targetUserId (response may include pending_follow for protected accounts)
• Unfollow: DELETE /v1/twitter/follow with accountId, targetUserId

Inbox comment moderation also adds X/Twitter support:
• POST /v1/inbox/comments/{postId}/{commentId}/hide
• DELETE /v1/inbox/comments/{postId}/{commentId}/hide
For X/Twitter, the reply must be in a conversation started by the authenticated user and requires tweet.moderate.write + X API Basic tier (or higher). Bookmark/follow also require bookmark.write / follows.write scopes, and X API Basic tier (or higher).
March 13, 2026Improvement
GET /v1/analytics now returns richer media metadata for analytics results.

You can render previews and handle carousels consistently using the new fields on both single-post and list responses.

New fields:
• thumbnailUrl
• mediaType = image | video | gif | document | carousel | text (single-post also supports image | video | carousel | text)
• mediaItems (array of { type: image|video, url, thumbnail }; carousel posts include one entry per slide)
March 12, 2026Improvement
The GET /v1/usage-stats response now includes billingAnchorDay.

Use billingAnchorDay (1–31) to determine the day of the month when the billing cycle resets, which can help align usage tracking and reset expectations.
March 12, 2026Improvement
GBP endpoints now support overriding the target location via optional locationId.

This lets you fetch/update reviews, media, attributes, place actions, food menus, and location details for a specific GBP location without changing the account’s selected location.

Use locationId (query) on:
• GET /v1/accounts/{accountId}/gmb-reviews
• GET/PUT /v1/accounts/{accountId}/gmb-food-menus
• GET/PUT /v1/accounts/{accountId}/gmb-location-details
• GET/POST/DELETE /v1/accounts/{accountId}/gmb-media
• GET/PUT /v1/accounts/{accountId}/gmb-attributes
• GET/POST/DELETE /v1/accounts/{accountId}/gmb-place-actions
March 11, 2026New Feature
Twitter posts now support replying to an existing tweet via replyToTweetId in TwitterPlatformData.

Set replyToTweetId to publish the first tweet (or the root of a thread) as a reply; subsequent thread items chain normally.

replySettings also adds verified (now: following, mentionedUsers, subscribers, verified). Note: replySettings cannot be combined with replyToTweetId.
March 8, 2026New Feature
New endpoint GET /v1/accounts/{accountId}/linkedin-post-reactions returns who reacted to a LinkedIn post, including reactor profile details.

Currently supported only for LinkedIn organization/company page accounts (personal profile reaction data is restricted by LinkedIn).

Required params: accountId (path, LinkedIn org account), urn (query, LinkedIn post URN)
Optional: limit (1–100, default 25), cursor

Response includes reactions[] with reactionType (LIKE, PRAISE, EMPATHY, INTEREST, APPRECIATION, ENTERTAINMENT), reactedAt, and from (e.g., name, headline, profileUrl, profilePicture), plus pagination.
March 8, 2026Improvement
GET /v1/connect/{platform} redirect behavior now includes accountId in standard mode.

After a successful connect, Zernio’s standard redirect to redirect_url now appends ?connected={platform}&profileId=X&accountId=Y&username=Z (previously no accountId).

In headless mode (headless=true), the redirect includes raw OAuth data only when a post-OAuth selection step is required (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId.
March 6, 2026New Feature
Posts can now be recycled (auto-reposted on a schedule) via recycling on POST /v1/posts and PUT /v1/posts/{postId}.

After the original post is published, Zernio will create new scheduled copies at a weekly/monthly interval until an expiration condition is met.

Configure with recycling:
• gap (int)
• gapFreq: week | month
• startDate (date-time, optional)
• expireCount (int, optional)
• expireDate (date-time, optional)
• contentVariations (string[], optional)

Webhooks: a new event post.recycled is available in POST/PUT /v1/webhooks/settings and filterable in GET /v1/webhooks/logs.

Also: GET /v1/tools/youtube/transcript may now return 503, and DELETE /v1/inbox/comments/{postId} may now return 400 when the platform rejects the delete.
March 3, 2026New Feature
New endpoint GET /v1/analytics/post-timeline returns a daily analytics timeline for a specific post (per platform for multi-platform Zernio posts), showing how metrics evolve day-by-day since publishing. Requires the Analytics add-on.

Query params:
• postId (required) — accepts ExternalPost ID, platformPostId, or Zernio Post ID
• fromDate (optional, ISO 8601; default 90 days ago)
• toDate (optional, ISO 8601; default now)

Response includes timeline[] rows with date, platform, platformPostId, and metrics like impressions, reach, likes, comments, shares, saves, clicks, views.
February 27, 2026New Feature
New validation endpoints are available under /v1/tools/validate to preflight-check content before publishing.

Validate weighted character counts per platform with POST /v1/tools/validate/post-length (body: { text }) and get per-platform { count, limit, valid }.

Dry-run the full post validation pipeline (same body as POST /v1/posts) with POST /v1/tools/validate/post. Target platforms via platforms[].platform: twitter, instagram, tiktok, youtube, facebook, linkedin, bluesky, threads, reddit, pinterest, telegram, snapchat, googlebusiness. Returns valid plus errors[] and/or warnings[].

Validate a public media URL and compare against per-platform size limits with POST /v1/tools/validate/media (body: { url }). Returns contentType, size, type (image/video/unknown), and platformLimits when available.

Check subreddit existence/info with GET /v1/tools/validate/subreddit using query name.
February 26, 2026New Feature
New Analytics endpoints are available for aggregated reporting and scheduling optimization (requires the Analytics add-on).

Daily aggregated metrics: GET /v1/analytics/daily-metrics
• Filters: platform, profileId, fromDate, toDate
• Returns: dailyData + platformBreakdown with summed metrics (impressions, reach, likes, comments, shares, saves, clicks, views)

Best times to post: GET /v1/analytics/best-time
• Filters: platform, profileId
• Returns: slots grouped by dayofweek (0=Monday..6=Sunday) and hour (UTC 0-23) with avg_engagement

Content performance decay: GET /v1/analytics/content-decay
• Filters: platform, profileId
• Returns: buckets with bucketlabel and avgpctoffinal

Posting frequency vs engagement: GET /v1/analytics/posting-frequency
• Filters: platform, profileId
• Returns: frequency rows with postsperweek, avgengagementrate, weeks_count
February 20, 2026Improvement
LinkedIn document (PDF/carousel) posts now let you control the displayed document title via platformSpecificData.linkedin.documentTitle.

LinkedIn requires a title for document posts; if omitted, Zernio will fall back to the media item title, then the filename.

Use:
• platformSpecificData.linkedin.documentTitle
• media[].title (used as the LinkedIn document title if documentTitle is not set; otherwise falls back to filename)
February 18, 2026New Feature
API keys can now be created with restricted access using scope, profileIds, and permission on POST /v1/api-keys.

This lets you issue keys limited to specific profiles and/or make keys read-only for safer analytics/integration use.

New request fields:
• scope: full | profiles (default full)
• profileIds: required when scope=profiles
• permission: read-write | read (default read-write)

Key responses now include scope, profileIds (populated profile objects when scoped), and permission (e.g., in GET /v1/api-keys and create responses).
February 18, 2026New Feature
Facebook publishing now supports Reels via FacebookPlatformData.contentType.

Set contentType to story or reel (omit for a feed post). Reels require a single vertical video (9:16, 3–60s).

New field for reels:
• title — Reel title (only when contentType=reel)

Note: firstComment applies to feed posts only (not stories or reels).
February 16, 2026New Feature
New endpoint: POST /v1/posts/{postId}/unpublish.

You can now delete a previously published post from a specific social platform while keeping the Zernio post record (platform status becomes cancelled).

Request body: platform = threads | facebook | twitter | linkedin | youtube | pinterest | reddit | bluesky | googlebusiness | telegram.

Notes: Instagram/TikTok/Snapchat deletion isn’t supported. Telegram deletions may fail for messages older than 48h. YouTube deletion permanently removes the video.
February 16, 2026Improvement
Instagram photo tagging now supports tagging specific slides in carousel posts via mediaIndex in platformSpecificData.userTags.

This lets you place tags on any image within a carousel. Tags without mediaIndex still default to the first image (index 0).

Use:
• userTags[].mediaIndex (integer, 0-based)

Notes:
• Not supported for stories or videos; tags targeting video items (or out-of-range indices) are ignored.
February 16, 2026New Feature
POST /v1/inbox/comments/{postId}/{commentId}/private-reply now supports Facebook in addition to Instagram.

You can send a private reply to a Facebook post comment, which opens a Messenger conversation with the commenter (text-only; still limited to 1 private reply per comment within 7 days).

Use accountId (Instagram or Facebook) and message in the JSON body. The response platform is now instagram or facebook.
February 15, 2026Improvement
Google Business Profile posts now support explicitly setting the content language via languageCode in GoogleBusinessPlatformData.

If omitted, the language is auto-detected from the post text; set it when auto-detection may be unreliable (short posts, mixed languages, transliteration).

New field: languageCode (BCP 47, e.g. en, de, es, fr).
February 12, 2026New Feature
Reddit posting now supports post flairs via flairId in platformSpecificData.

Some subreddits require a flair; you can now fetch available flairs and set the one you want on the post.

New endpoint: GET /v1/accounts/{accountId}/reddit-flairs
Required query: subreddit (without r/)

New field: platformSpecificData.reddit.flairId (string)
If omitted, the API will attempt to use the first available flair as a fallback.
February 11, 2026Improvement
Instagram conversations now include optional participant profile context via instagramProfile.

This lets you show follower/verification info (and follower count) alongside DMs, and use it in routing/automation.

Added to:
• GET /v1/inbox/conversations → data[].instagramProfile
• GET /v1/inbox/conversations/{conversationId} → data.instagramProfile

Fields:
• isFollower, isFollowing, followerCount, isVerified, fetchedAt

Webhook update:
• WebhookPayloadMessage.message.sender.instagramProfile (Instagram only) with isFollower, isFollowing, followerCount, isVerified.
February 11, 2026New Feature
Interactive messaging support for the Inbox API.

POST /v1/inbox/conversations/{conversationId}/messages now supports:

• Quick replies (quickReplies) - Up to 13 quick reply buttons (Instagram, Facebook, Telegram)
• Buttons (buttons) - Up to 3 action buttons: URL, postback, or phone (Instagram, Facebook, Telegram)
• Carousels (template) - Generic template with up to 10 elements (Instagram, Facebook)
• Telegram keyboards (replyMarkup) - Native inline or reply keyboards
• Message tags (messageTag) - Send outside the 24h window: HUMANAGENT (Instagram), CONFIRMEDEVENTUPDATE, POSTPURCHASEUPDATE, ACCOUNTUPDATE, HUMAN_AGENT (Facebook)
• Reply to (replyTo) - Reply to a specific message (Telegram)

Incoming messages now include metadata with quickReplyPayload, postbackPayload, postbackTitle, and callbackData to identify interactive message taps.

Also added: PATCH /v1/inbox/conversations/{conversationId}/messages/{messageId} to edit sent Telegram messages (text and inline keyboard).
February 11, 2026New Feature
New Account Settings endpoints for managing platform-specific messaging features.

Facebook Persistent Menu (/v1/accounts/{accountId}/messenger-menu)
GET/PUT/DELETE to manage the persistent menu shown in Facebook Messenger conversations. Max 3 top-level items, max 5 nested items.

Instagram Ice Breakers (/v1/accounts/{accountId}/instagram-ice-breakers)
GET/PUT/DELETE to manage ice breaker prompts shown when users start a new Instagram DM. Max 4 ice breakers, question max 80 chars.

Telegram Bot Commands (/v1/accounts/{accountId}/telegram-commands)
GET/PUT/DELETE to manage the bot command menu shown in Telegram chats.
February 10, 2026Minor
Added 2 new endpoints:

GET /v1/posts/logs - Get publishing logs
GET /v1/connections/logs - Get connection logs
February 5, 2026New Feature
New Google Business Profile endpoints are now available for managing your GMB listings programmatically.

Location Details (/v1/accounts/{accountId}/gmb-location-details)
GET/PUT to read and update business hours, special hours, description, phone numbers, and website. Use readMask and updateMask to specify fields.

Media (/v1/accounts/{accountId}/gmb-media)
GET/POST/DELETE to manage photos. Upload via public URL with categories:
COVER, PROFILE, LOGO, EXTERIOR, INTERIOR, FOODANDDRINK, MENU, PRODUCT, TEAMS, ADDITIONAL

Attributes (/v1/accounts/{accountId}/gmb-attributes)
GET/PUT to manage amenities and services like hasdelivery, hastakeout, hasoutdoorseating, has_wifi, payment types, etc.

Place Actions (/v1/accounts/{accountId}/gmb-place-actions)
GET/POST/DELETE to manage booking and ordering buttons. Types include:
APPOINTMENT, DININGRESERVATION, FOODORDERING, FOODDELIVERY, FOODTAKEOUT, SHOP_ONLINE
February 5, 2026New Feature
New endpoints for Google Business Profile food menus:

GET /v1/accounts/{accountId}/gmb-food-menus
Fetch the full menu structure for a connected GBP location.

PUT /v1/accounts/{accountId}/gmb-food-menus
Update food menus with sections, items, pricing, dietary info, and allergens.

Menu items support:
• price with currency code
• dietaryRestriction - VEGETARIAN, VEGAN, GLUTEN_FREE, etc.
• allergen - DAIRY, GLUTEN, SHELLFISH, etc.
• spiciness, servesNumPeople, preparationMethods

Only available for GBP locations with food menu support (restaurants, cafes, etc.).

Also: PostAnalytics now includes saves for tracking bookmarks on Instagram and Pinterest.
February 4, 2026New Feature
The Inbox API now supports Twitter/X for conversations, comments, and comment interactions.

Conversations (GET /v1/inbox/conversations):
Filter by platform=twitter to fetch Twitter/X DMs.

Comments (GET /v1/inbox/comments):
Twitter/X posts with replies are now included. Also added Threads support for comments listing.

Like/Unlike comments:
POST and DELETE /v1/inbox/comments/{postId}/{commentId}/like now work with Twitter/X replies.
February 3, 2026New Feature
The GET /v1/analytics endpoint now supports a source query parameter to filter posts by origin.

Values:
• late - only posts scheduled/published via Zernio API
• external - only posts synced from the platform (not posted via Late)
• all - all posts (default)

This makes it easier to separate analytics for posts you scheduled through Zernio from posts that were published directly on the platform.
February 2, 2026New Feature
The PlatformTarget schema now includes detailed error information when posts fail to publish.

Three new fields are available:

errorMessage - Human-readable explanation of why the publish failed

errorCategory - Programmatic category for handling:
• auth_expired - token expired, reconnect needed
• user_content - content doesn't meet platform requirements
• user_abuse - rate limits or spam detection
• account_issue - account configuration problems
• platform_rejected - policy violation
• platform_error - platform-side issues
• system_error - Zernio infrastructure issues
• unknown - unclassified

errorSource - Who caused the error:
• user - user action required
• platform - platform-side issue
• system - Zernio system issue

These fields are populated when a platform's status is failed. Posts with mixed results now return status: "partial" at the post level.
February 2, 2026New Feature
YouTube uploads now support video categories via the new categoryId field in platformSpecificData.

You can now specify which category your video belongs to. Defaults to 22 (People & Blogs) if not set.

Common category IDs:
• 1 - Film & Animation
• 10 - Music
• 20 - Gaming
• 24 - Entertainment
• 27 - Education
• 28 - Science & Technology
January 30, 2026New Feature
Multi-page/multi-location posting is now supported for Facebook, LinkedIn, and Google Business.

You can now post to multiple pages, organizations, or locations from a single account connection by using the same accountId multiple times with different targets in platformSpecificData:

• Facebook: pageId - post to multiple Pages
• LinkedIn: organizationUrn - post to multiple organizations
• Google Business: locationId - post to multiple locations

List available targets via:
• GET /v1/accounts/{id}/facebook-page
• GET /v1/accounts/{id}/linkedin-organizations
• GET /v1/accounts/{id}/gmb-locations

Also: content is now optional when media is attached or all platforms have customContent set.
January 28, 2026New Feature
New endpoint: POST /v1/inbox/comments/{postId}/{commentId}/private-reply

Send a private DM to the author of a comment on your Instagram post. Useful for handling customer inquiries or sensitive matters privately.

Required body:
• accountId - Instagram social account ID
• message - text to send as DM

Limitations:
• Instagram only (not available on other platforms)
• One private reply per comment
• Must be sent within 7 days of the comment
• Only works for comments on posts you own
• Requires the Inbox addon
January 26, 2026New Feature
New endpoints for managing Facebook Pages and Google Business Profile locations.

GET /v1/accounts/{accountId}/facebook-page
List all Facebook pages the connected account can access, see which one is currently selected.

GET /v1/accounts/{accountId}/gmb-locations
List all Google Business Profile locations available to the account.

PUT /v1/accounts/{accountId}/gmb-locations
Change the selected GBP location by passing selectedLocationId in the request body.

Useful if you manage multiple pages/locations and need to switch between them programmatically.
January 25, 2026Improvement
POST /v1/posts now returns 409 Conflict when duplicate content is detected.

If you post identical content to the same account within 24 hours, you'll get:
{
"error": "This exact content was already posted...",
"details": {
"accountId": "...",
"platform": "...",
"existingPostId": "..."
}
}


Additionally, 429 responses now include detailed context about which limit was hit:
• Velocity limit: 15 posts/hour per account
• Account cooldown: Escalating delays (10min → 24h) after repeated errors
• Daily post limits: Platform-specific (X: 20, Pinterest: 25, Instagram/Facebook: 100, Threads: 250, others: 50)

New headers on 429: Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining

These changes also apply to POST /v1/posts/bulk-upload and POST /v1/posts/{postId}/retry.
January 25, 2026New Feature
YouTube uploads now support madeForKids in platformSpecificData.youtube.

This COPPA compliance field declares whether your video is child-directed content. Defaults to false.

Set to true if your video is made for kids. Note: kid-directed videos have restricted features (no comments, no notifications, limited ad targeting).

Important: YouTube requires this to be explicitly set. If omitted, the video defaults to not made for kids, but may be blocked from views until configured in YouTube Studio.
January 23, 2026Improvement
Analytics responses now include latePostId field.

When an external post originated from a Late-scheduled post, the response now includes the original Zernio Post ID directly. This makes it easier to correlate analytics data with posts created via POST /v1/posts.

The field appears in both single post queries and paginated list responses. It's null for posts that weren't scheduled through Zernio.
January 22, 2026Breaking Change
LinkedIn headless OAuth flow updated

LinkedIn headless mode now uses a token-based approach to retrieve OAuth data, preventing URITOOLONG errors for users with many organizations.

What changed:
After OAuth redirect, you now receive pendingDataToken instead of tempToken, userProfile, and organizations in the URL.

Call the new endpoint to fetch the data:
GET /v1/connect/pending-data?token=PENDINGDATATOKEN

Returns: tempToken, refreshToken, expiresIn, userProfile, organizations, selectionType

Important:
• One-time use: data is deleted after fetching
• Data expires after 10 minutes if not fetched
• No authentication required, just the token

Other platforms (Facebook, Pinterest, Google Business, Snapchat) are unchanged.
January 21, 2026New Feature
Instagram Reels now support thumbnail offset selection via thumbOffset in platformSpecificData.

Specify a millisecond offset from the start of the video to use as the Reel cover image. Defaults to 0 (first frame).

thumbOffset: integer, minimum 0 (milliseconds)

If you provide a custom thumbnail URL via instagramThumbnail in mediaItems, it takes priority and this offset is ignored.
January 18, 2026New Feature
New webhook event: account.connected

You can now subscribe to notifications when a social account is connected to a profile. The webhook payload includes accountId, profileId, platform, username, and displayName.

Add it to your events array when creating or updating webhooks.
January 15, 2026New Feature
YouTube daily views breakdown is now available via GET /v1/analytics/youtube/daily-views. This endpoint returns historical daily view counts for a specific YouTube video, including views, watch time, and subscriber changes. Required parameters include videoId and accountId, with optional date range parameters startDate and endDate.
January 15, 2026Improvement
Zernio now automatically compresses media that exceeds platform limits during publishing, simplifying the upload process.

Image compression thresholds are set for platforms like Twitter/X: >5 MB, Instagram: >8 MB, Facebook: >10 MB, and more.

Video compression thresholds include Twitter/X: >512 MB, Instagram Stories: >100 MB, Facebook: >4 GB, among others.
January 14, 2026Breaking Change
The endpoint POST /v1/profiles/{profileId}/clone-connection has been removed. This endpoint was used to clone an existing connection to a profile, allowing users to manage multiple brands with the same underlying social media account.
January 12, 2026Improvement
YouTube now supports AI-generated content disclosure via containsSyntheticMedia in YouTubePlatformData.

Set containsSyntheticMedia to true if your video contains AI-generated or synthetic content that could be mistaken for real people, places, or events. YouTube may add a label to videos when this is set.
January 9, 2026Improvement
Instagram now supports custom audio names for Reels via audioName in platformSpecificData.

You can set a custom name for the original audio, replacing the default "Original Audio" label. This can only be set once during creation or later from the Instagram audio page in the app.
January 7, 2026Improvement
InstagramPlatformData Schema Update

The InstagramPlatformData schema has been updated to include a new parameter: trialParams. This parameter allows for configuration of trial Reels, which are initially shared only with non-followers.

Here are the details of the new trialParams field:
- trialParams: object (optional)
- graduationStrategy: string (required)
- Possible values: MANUAL, SS_PERFORMANCE
- This field determines how a trial reel transitions to a regular reel:
- MANUAL: Requires manual graduation via the Instagram app.
- SS_PERFORMANCE: Automatically graduates based on performance metrics with non-followers.

The rest of the schema remains unchanged, including the parameters contentType, shareToFeed, collaborators, firstComment, and userTags.

This update enhances your ability to manage Reels effectively, allowing for strategic sharing and performance-based visibility adjustments. Keep this in mind as you plan your content strategy.
January 5, 2026New Feature
New Endpoints Available:

1. GET /v1/logs
- This endpoint allows you to retrieve publishing logs for all posts. You can filter logs by status (status), platform (platform), and action (action). The logs include detailed information about each publishing attempt, such as API requests, responses, and timing data.
- status (string, optional): Filter by log status (options: success, failed, pending, skipped, all).
- platform (string, optional): Filter by platform (options include tiktok, instagram, facebook, etc.).
- action (string, optional): Filter by action type (options: publish, retry, media_upload, etc.).
- days (integer, optional): Number of days to look back (max 7, default 7).
- limit (integer, optional): Maximum number of logs to return (max 100, default 50).
- skip (integer, optional): Number of logs to skip for pagination (default 0).
- The response includes an array of logs and pagination details.

2. GET /v1/logs/{logId}
- Use this endpoint to retrieve detailed information about a specific log entry, including full request and response bodies for debugging purposes.
- logId (string, required): The ID of the log entry you want to retrieve.
- The response contains the log entry details, including the full context of the publishing attempt.

3. GET /v1/posts/{postId}/logs
- This endpoint retrieves all publishing logs for a specific post, showing the complete history of publishing attempts across all platforms.
- postId (string, required): The ID of the post for which you want to retrieve logs.
- limit (integer, optional): Maximum number of logs to return (max 100, default 50).
- The response includes an array of logs specific to the post and the count of logs returned.
January 5, 2026New Feature
New Endpoints for Pinterest Integration

We have added two new endpoints to enhance your integration with Pinterest in headless mode:

1. GET /v1/connect/pinterest/select-board
- Summary: List Pinterest Boards after OAuth (Headless Mode)
- Description: This endpoint retrieves a list of Pinterest boards available for selection after initiating OAuth via /v1/connect/pinterest with headless=true. It allows you to build your own fully-branded board selector instead of using Zernio's hosted UI.
- Parameters:
- X-Connect-Token (header, required): Short-lived connect token from the OAuth redirect.
- profileId (query, required): Your Zernio profile ID.
- tempToken (query, required): Temporary Pinterest access token from the OAuth callback redirect.
- Responses:
- 200: Returns a list of Pinterest boards available for connection, including each board's ID, name, description, and privacy setting.
- 400: Missing required parameters.
- 401: Unauthorized access.
- 403: No access to profile.
- 500: Failed to fetch boards.

2. POST /v1/connect/pinterest/select-board
- Summary: Select a Pinterest Board to complete the connection (Headless Mode)
- Description: Use this endpoint to save the selected board and complete the Pinterest account connection after OAuth.
- Request Body: Must include the following fields:
- profileId (string, required): Your Zernio profile ID.
- boardId (string, required): The Pinterest Board ID selected by the user.
- boardName (string, optional): The board name for display purposes.
- tempToken (string, required): Temporary Pinterest access token from OAuth.
- userProfile (object, optional): User profile data from OAuth redirect.
- refreshToken (string, optional): Pinterest refresh token if available.
- expiresIn (integer, optional): Token expiration time in seconds.
- redirect_url (string, optional): Custom redirect URL after connection completes.
- Responses:
- 200: Pinterest Board connected successfully, returns a message and account details.
- 400: Missing required fields.
- 401: Unauthorized access.
- 403: No access to profile or profile limit exceeded.
- 500: Failed to save Pinterest connection.
January 5, 2026Improvement
GET /v1/accounts/{accountId}/linkedin-mentions

This endpoint resolves a LinkedIn profile URL (for a person) or a company page URL (for an organization) to a URN that can be used to @mention them in posts.

Parameters:
- accountId (path, required): The LinkedIn account ID.
- url (query, required): LinkedIn profile URL, company URL, or vanity name. It supports:
- Person: miquelpalet, linkedin.com/in/miquelpalet
- Organization: company/microsoft, linkedin.com/company/microsoft
- displayName (query, optional): The exact display name as shown on LinkedIn. This is required for clickable person mentions. If not provided, the name is derived from the vanity URL, which may not match exactly.

Response:
On success (HTTP 200), the response contains:
- urn: The LinkedIn URN (either for a person or organization).
- type: The type of entity, which can be either person or organization.
- displayName: The display name (provided, from API, or derived from vanity URL).
- mentionFormat: Ready-to-use mention format for post content.
- vanityName: The vanity name/slug (only for organization mentions).
- warning: A warning about clickable mentions (only present for person mentions if displayName was not provided).

This change allows developers to mention organizations without needing to be an admin of a LinkedIn organization, which simplifies the process of tagging companies in posts. Person mentions still require admin access for the organization but now support a broader range of input formats.
January 1, 2026Improvement
GET /v1/profiles
This endpoint lists profiles visible to the authenticated user. It now includes a new optional query parameter includeOverLimit (type: boolean, default: false). When set to true, it includes profiles that exceed the user's plan limit, which will have isOverLimit: true in the response. This is useful for managing or deleting profiles after a plan downgrade. The profiles are sorted by creation date (oldest first).

Response contains:
- profiles: an array of profile objects, each containing:
- id: string
- name: string
- color: string
- isDefault: boolean
- isOverLimit: boolean (only present when includeOverLimit=true)
- createdAt: string (date-time)

GET /v1/accounts
This endpoint lists connected social accounts. It now also includes the includeOverLimit parameter (type: boolean, default: false). When true, it includes accounts from profiles that exceed the user's plan limit, allowing users to disconnect accounts from over-limit profiles for deletion.

Response contains:
- accounts: an array of account objects, each containing:
-
id: string
- platform: string
- profileId: object containing profile details
- username: string
- displayName: string
- profileUrl: string
- isActive: boolean
- hasAnalyticsAccess: boolean (indicates if the user has analytics add-on access)
December 30, 2025Improvement
The account.disconnected webhook payload now includes additional fields for better context:

- accountId - The account's unique identifier (same as used in /v1/accounts/{accountId})
- profileId - The profile's unique identifier this account belongs to
- disconnectionType - Either intentional (user disconnected manually) or unintentional (token expired or was revoked)

These fields help you understand why an account was disconnected and quickly look up related data.
Was this page helpful?

Billing

When your card is charged, and how daily proration and the graduated price ladder compute your invoice.

Refer & earn

Earn 20% recurring commission for 12 months when you refer new customers to Zernio.