Phone number purchases can now be auto-assigned to a different profile when the requested
Send
Check the assigned profile in:
•
•
View documentation
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 purchaseIntentIdView documentation
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.View documentation
Facebook analytics now supports per-post monetization earnings via
This returns lifetime cumulative earnings for a single post, read live from Meta on each request (no date range supported).
Required params:
•
•
Optional:
•
Notes:
•
• Money metrics include
• Metrics Meta can’t serve are omitted from
Also:
View documentation
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 = content_monetization_earnings, monetization_approximate_earnings (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 (content_monetization_earnings, monetization_approximate_earnings) and may return unavailableMetrics with the same omit-vs-zero behavior.View documentation
Meta ads reporting now supports filtering by Facebook Page via
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:
•
Available on:
•
•
•
Matches against
View documentation
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/treeMatches against
ad.creative.pageId (may be null for non-Meta ads and rare IG-only creatives).View documentation
Meta ad URL tags now preserve Meta dynamic macros in tracking parameters.
When setting Meta
Applies to:
•
•
•
View documentation
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.urlTagsView documentation
Rate limit guidance has been updated: the per-account velocity limit referenced in
•
•
If you throttle based on the error text for
View documentation
429 responses is now 25 posts/hour per account (was 15) for:•
POST /v1/posts•
POST /v1/posts/{postId}/retryIf you throttle based on the error text for
429, update your assumptions to match the new 25 posts/hour limit.View documentation
Facebook posts now support large-text background presets via
This lets you render a text-only feed post as large text on a colored background using Meta’s
Use
• Text-only feed posts only: request is rejected if
• Not allowed with
•
• If a URL is present in
View documentation
textFormatPresetId in FacebookPlatformData.This lets you render a text-only feed post as large text on a colored background using Meta’s
text_format_preset_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 setView documentation
WhatsApp Calling endpoints now return caller ID details for tel: forwards via
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:
•
•
Also clarified:
View documentation
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.View documentation
WhatsApp Calling now supports caller-ID verification for customer-brought (BYO) phone numbers, so
Start (or resend) a verification code via
Confirm the code via
•
•
View documentation
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 | falseView documentation
Bulk contact import (
When
Check your handling of
View documentation
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).View documentation
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:
•
•
Available on:
•
•
•
View documentation
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 | callerAvailable 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)View documentation
Facebook text background posts via
Previously, requests were rejected with
Use
View documentation
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.View documentation
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:
•
• New:
Connect response may include
Inbox comments endpoints now also return platform/upstream error responses you may want to handle:
View documentation
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 pinConnect 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}.View documentation
You can now manage OAuth connected apps for the authenticated user: list authorized clients and revoke their access.
List connected apps with
Revoke an app with
Auth note: requires a session or full-scope API key; profile-scoped API keys and OAuth access tokens get
View documentation
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).View documentation
Create/duplicate ads endpoints now support safe retries via an
This prevents accidental duplicate campaigns/ad sets/ads when your client retries after timeouts or network errors.
Now supported on:
•
•
•
•
Behavior:
• same
• same key + different body →
• key still processing →
Also: metrics
View documentation
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}/duplicateBehavior:
• same
Idempotency-Key + same body → replay original 2xx response• same key + different body →
422• key still processing →
409Also: metrics
conversions is now a number (can be fractional) in GET /v1/ads/timeline and the AdMetrics schema (attribution splitting + Google modeled conversions).View documentation
GET
This lets you distinguish “insufficient LinkedIn org/page role” from invalid input (
New response:
•
View documentation
/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)View documentation
Facebook post options have been reorganized: draft/carousel/colored-background text settings are now grouped under
This makes Facebook-specific options consistent and clarifies which fields belong under
Move these fields under
•
•
•
•
Applies to
View documentation
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•
textFormatPresetIdApplies to
POST /v1/posts and PUT /v1/posts/{postId}. Also note: firstComment is skipped when facebookSettings.draft is true.View documentation
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 foundView documentation
Meta ads campaign create/delete behavior has been tightened and now supports deleting “empty” campaign shells.
On
On
On
View documentation
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 LOWEST_COST_WITH_BID_CAP/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.View documentation
GET
Use
PUT
View documentation
/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.View documentation
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 toView documentation
❤1