Zernio | Developer News
689 subscribers
1 photo
1 video
484 links
This channel is for recurrent updates and announcements on https://zernio.com.

Zernio is a Social API for Posting and Analytics on 14 platforms.
Download Telegram
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.

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: 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 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:
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_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.

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 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 incomplete

View documentation
Some endpoints now return additional error statuses you may need to handle.

Posts listing can now return 403 when X analytics isn’t enabled for the account.
GET /v1/accounts/{accountId}/posts403 (code X_ANALYTICS_NOT_ENABLED)

Billing-gated connect / SMS setup now surfaces payment/setup issues explicitly.
POST /v1/connect/openai-ads/credentials402 (PaymentRequired)
POST /v1/sms/sender-ids402 (code payment_required), 409 (code billing_setup_incomplete)

View documentation
Creating or retrying posts can now return 403 with 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 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 follow

View documentation
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).

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 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}/assets

If you have retry logic, treat 403 ads_allowance_exceeded as non-retryable until billing is set up.

View documentation
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 SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV.

View documentation
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.)

View documentation
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.

View documentation
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. BRAZIL_REGULATION, SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV), you can now pass the required Meta-verified beneficiary/payer entity IDs in regionalRegulationIdentities.

Set:
regionalRegulatedCategories: [...]
regionalRegulationIdentities: { universal_beneficiary: 123, universal_payer: 456, ... }

If omitted, Meta may use Ads Manager defaults when configured.

View documentation
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).

View documentation
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 (object_story_spec), and this mode is mutually exclusive with dynamicCreative, placementAssets, carouselCards, and creatives[].

View documentation
🔥1
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 (flow_action: data_exchange).

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.

View documentation