Zernio | Developer News
675 subscribers
1 photo
1 video
475 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
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).

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

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

View documentation
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 (sender_id_taken) conflict response
• Increased active sender ID limit in 403 from 5 to 1,000 (sender_id_limit_reached)

Request remains: senderId

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

View documentation
Updating a post now preserves per-platform settings unless you explicitly send them in platforms[].platformSpecificData.

A Settings namespace (e.g. facebookSettings, tiktokSettings) omitted from the request is kept from the stored post. If you include the key, it replaces the entire namespace (no deep-merge).

Also, port-in now supports Brazil by allowing endUser.countryCode = BR in POST /v1/phone-numbers/port-in (enum now includes US, CA, GB, ES, DE, FR, NL, AU, BR).

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

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

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

View documentation
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.body_text_named_params
components[].example.header_text_named_params

Each named param example uses: { param_name, example }.

View documentation
🔥1
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 mutually_exclusive_fields

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 LOWEST_COST_WITHOUT_CAP or COST_CAP.

View documentation
🔥1
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 OUTCOME_AWARENESS, so values like REACH, IMPRESSIONS, AD_RECALL_LIFT, or THRUPLAY-class are accepted; POST_ENGAGEMENT is rejected under that objective).

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

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

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

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

View documentation
1
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:
request_contact_info

On message.received, check:
metadata.contacts (array)
metadata.contactsOrigin: contact_request | other

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

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

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

View documentation
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: pass Meta mid
• Slack: threads the reply (uses thread_ts)
• Silently ignored on platforms without reply support (incl. Facebook Messenger)

View documentation