GET
This lets you request a desired locale (e.g.
New:
• Query param:
• Response field:
View documentation
/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)View documentation
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, googleFor empty campaigns (0 ads), include
accountId to allow deletion: accountId (Zernio SocialAccount id).View documentation
DELETE /v1/ads/campaigns/{campaignId} now accepts platform=google.This lets you delete Google campaigns via the same endpoint used for Meta.
Set
platform to one of: facebook, instagram, google.accountId behavior is unchanged (only needed to delete an EMPTY campaign with zero ads).View documentation
WhatsApp inbox conversation creation now supports sending values for template buttons that carry their own payload (copy-code / flow tokens), via
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
Set
•
•
•
Message lineage is now exposed as
Note: existing stored messages are not backfilled; treat
View documentation
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•
valueMessage 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 | nullNote: existing stored messages are not backfilled; treat
sentVia: null as “unknown”.View documentation
Inbox messages now include send attribution via
This lets you tell which Zernio surface produced an outgoing message (API vs human vs broadcast/workflow/etc). It’s available on message listing responses and on the
New field:
•
Notes:
• Present on
• Present on
• Not backfilled for older stored messages; treat
WhatsApp template sends can now pass per-button values via
Use this for buttons that carry a value at send time (e.g. copy-code payloads or flow tokens), which are not covered by
New request field on
•
-
-
-
Analytics sorting now supports additional Instagram metrics on
New
•
•
•
•
View documentation
sentVia.This lets you tell which Zernio surface produced an outgoing message (API vs human vs broadcast/workflow/etc). It’s available on message listing responses and on the
message.sent webhook.New field:
•
sentVia: human | api | broadcast | sequence | workflow | comment_automation | bulk-api | nullNotes:
• Present on
GET /v1/inbox/conversations/{conversationId}/messages message items• Present on
WebhookPayloadMessageSent.message.sentVia• Not backfilled for older stored messages; treat
null as “unknown”WhatsApp template sends can now pass per-button values via
templateButtonParams when creating a conversation.Use this for buttons that carry a value at send time (e.g. copy-code payloads or flow tokens), which are not covered by
templateParams.New request field on
POST /v1/inbox/conversations (JSON body only):•
templateButtonParams[] items: { index, subType, value }-
index: 0..9-
subType: url | copy_code | flow-
value: stringAnalytics sorting now supports additional Instagram metrics on
GET /v1/analytics.New
sortBy values:•
ig_reels_avg_watch_time•
ig_reels_video_view_total_time•
reposts•
reels_skip_rateView documentation
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).View documentation
Instagram is now supported (limited release) for liking/unliking posts and comments via:
•
•
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
• Only works for accounts connected via Facebook Login; Instagram Login returns
• Accounts whose token predates the permission return
• Limited release: non-allowed accounts return
• Burst limit: 50 like/unlike calls per 5s per Instagram account; exceeding can lock likes for 1 hour
Also note: these endpoints’
View documentation
•
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}/likeThis 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
instagram_manage_engagement• Only works for accounts connected via Facebook Login; Instagram Login returns
400 with code instagram_likes_require_facebook_login• Accounts whose token predates the permission return
403 with code reconnect_required• Limited release: non-allowed accounts return
403 with code PLATFORM_BETA_RESTRICTED• 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).View documentation
New endpoint:
Provide the ad set ID via
Response includes
View documentation
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.View documentation
New endpoint:
Supports cached reads to avoid repeated YouTube calls; response includes
Key params:
•
•
•
•
•
Response highlights:
View documentation
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.View documentation
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).View documentation
Phone number stock watches are now available via new endpoints:
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
Webhooks now support the new event
View documentation
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
phone_number.stock_available 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.View documentation
WhatsApp sends via
If you send bursts to the same recipient, WhatsApp may reject excess messages with Meta error code
If you see
View documentation
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.View documentation
WhatsApp templates now support working with a specific Meta template variant via its Meta template id.
New endpoints:
•
•
•
Template listing and name-based operations are now language-aware (Meta stores templates per
•
•
•
Delete behavior change:
•
View documentation
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/IN_APPEAL/PENDING_DELETION)•
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_templateDelete 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.View documentation
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).View documentation
Some endpoints now return additional billing/capability errors you may need to handle.
Posts listing may now fail with
OpenAI Ads connect may now return
SMS sender ID creation now has explicit billing-related failures on
•
•
View documentation
Posts listing may now fail with
403 when X analytics isn’t enabled for the account (code X_ANALYTICS_NOT_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 billing_setup_incomplete) when billing setup is incompleteView documentation
Some endpoints now return additional error statuses you may need to handle.
Posts listing can now return
•
Billing-gated connect / SMS setup now surfaces payment/setup issues explicitly.
•
•
View documentation
Posts listing can now return
403 when X analytics isn’t enabled for the account.•
GET /v1/accounts/{accountId}/posts → 403 (code X_ANALYTICS_NOT_ENABLED)Billing-gated connect / SMS setup now surfaces payment/setup issues explicitly.
•
POST /v1/connect/openai-ads/credentials → 402 (PaymentRequired)•
POST /v1/sms/sender-ids → 402 (code payment_required), 409 (code billing_setup_incomplete)View documentation
Creating or retrying posts can now return 403 with
This happens when the target
Applies to:
•
•
View documentation
code=ACCOUNT_NOT_ENABLED_FOR_POSTING.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 ACCOUNT_NOT_ENABLED_FOR_POSTING)•
POST /v1/posts/{postId}/retry (403 may include ACCOUNT_NOT_ENABLED_FOR_POSTING, PROFILE_OVER_LIMIT, or insufficient_permissions)View documentation
WhatsApp template sends via
If you provide fewer values than the template’s required header + body + dynamic URL-button slots, the request is rejected with
Key fields:
•
•
View documentation
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 (INVALID_TEMPLATE_PARAMS) 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 followView documentation
GET
This makes it easier to build adset-centric dashboards by listing only ads within a specific platform ad set.
Use
PUT
Meta creative updates are now patch-style: send any subset of
Key Meta-only options in
•
•
•
GET
These ids can be used in
•
•
•
Also,
View documentation
/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•
workIndustriesAlso,
TargetingSpec audience lists are now supported: audienceInclude and audienceExclude accept platform audience IDs (not supported on OpenAI Ads; returns 400).View documentation
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.
View documentation
Several Ads endpoints can now return
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:
•
•
•
•
•
•
•
•
•
•
•
•
•
If you have retry logic, treat
View documentation
403 with code ads_allowance_exceeded.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}/assetsIf you have retry logic, treat
403 ads_allowance_exceeded as non-retryable until billing is set up.View documentation