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

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

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

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

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

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

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

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

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

View documentation
PlatformTarget.errorCategory now includes more granular failure types: platform_rate_limit 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: auth_expired, user_content, user_abuse, account_issue, platform_rejected, platform_error, platform_rate_limit, quota_exhausted, system_error, unknown.

View documentation
POST /v1/ads/boost now accepts a Meta-only CTA value VIEW_INSTAGRAM_PROFILE 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: VIEW_INSTAGRAM_PROFILE
linkUrl: (Instagram profile URL)

Applies to Meta boosts only; other platforms will reject Meta-only options.

View documentation
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 article_draft and article_publish (both in tier x_api_010, $0.010/call).

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

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

View documentation
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:
WHATSAPP_NUMBER_PINNED_TO_PROFILE — move it first with PATCH /v1/whatsapp/phone-numbers/{id}/profile
WHATSAPP_NUMBER_ALREADY_CONNECTED — disconnect it from the other profile/workspace first (a number can only be live on one profile)

View documentation
🔥2
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. template_approved, template_rejected, account_restricted, account_disconnected), severity (info/success/warning/critical), title, detail, createdAt.

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

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

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

View documentation
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: PARTICIPANT_NOT_FOUND when the u/username doesn’t exist
422 now also applies to Reddit when the recipient doesn’t accept private messages (still code: DM_NOT_ALLOWED)
429 may now be returned for Reddit rate limits with code: rate_limited

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

View documentation
2