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
Inbox messages now support refreshable attachment URLs for Instagram/Facebook DMs.

Meta DM media URLs expire; attachments now include a stable refreshUrl you can store and call later to get a working media URL.

New endpoint: GET /v1/inbox/conversations/{conversationId}/messages/{messageId}/attachments/{index}
Key params: accountId (required), format (redirect | json, default redirect)

Response behavior:
• Default format=redirect returns 302 to the live media URL
format=json returns { url, refreshed }

Also added on GET /v1/inbox/conversations/{conversationId}/messages attachments:
attachments[].refreshUrl (Instagram/Facebook only)
attachments[].url is now documented as expiring on Instagram/Facebook (use immediately; don’t store).

View documentation
Workflow AI nodes now support openrouter as an ai.provider value.

This lets you route AI steps through OpenRouter using your BYOK setup (set provider + model).

Set ai.provider to one of: anthropic, openai, google, mistral, groq, openrouter.

View documentation
🔥2
You can now like/unlike posts via the Inbox API.

This enables executive engagement (like/react as any connected accountId on a brand post). postId accepts a Zernio post ID or the platform native post ID.

New endpoints:
POST /v1/inbox/posts/{postId}/like with accountId (+ optional reactionType for LinkedIn: LIKE, PRAISE, EMPATHY, INTEREST, APPRECIATION, ENTERTAINMENT; optional cid for Bluesky)
DELETE /v1/inbox/posts/{postId}/like with accountId (+ likeUri required for Bluesky)

Also updated: comment like/unlike now supports LinkedIn on POST /v1/inbox/comments/{postId}/{commentId}/like and DELETE /v1/inbox/comments/{postId}/{commentId}/like. For LinkedIn likes, pass the composite comment URN as commentId and you can set reactionType (same enum as above).

View documentation
👍1
Meta ads now support scheduled budget increases via POST /v1/ads/high-demand-periods.

This lets you pre-schedule temporary budget bumps (e.g. Black Friday) instead of editing budgets manually on the day.

Send:
accountId
• exactly one of campaignId / adSetId
budgetValueType: ABSOLUTE | MULTIPLIER
budgetValue
timeStart, timeEnd (Unix seconds, 15-min boundaries)
• optional recurrenceType: ONE_TIME | WEEKLY | MONTHLY

Meta custom conversions are now manageable via the API:

• List: GET /v1/accounts/{accountId}/custom-conversions (requires adAccountId)
• Create or reuse: POST /v1/accounts/{accountId}/custom-conversions

Creating supports reuse (to avoid duplicates) by matching name + pixelId; reused conversions return 200 with reused: true, otherwise 201. The response includes customConversionId for promotedObject.customConversionId on POST /v1/ads/create.

Meta creatives now accept multiAdvertiser: OPT_IN | OPT_OUT on:
POST /v1/ads/creatives
POST /v1/ads/create

Use OPT_OUT to disable Meta’s multi-advertiser ads behavior (Meta auto-enrolls by default).

View documentation
GET /v1/accounts/{accountId}/instagram/stories/{storyId}/insights can now return 502 when Instagram rejects the upstream request.

This makes upstream failures explicit so clients can distinguish Instagram-side rejection from successful-but-empty expired story results.

Handle these responses:
200 with data.source = live | cached | unavailable
502 - Instagram rejected the request

View documentation
GET /v1/broadcasts/{broadcastId}/recipients now returns errorTraceId for failed recipients.

Use errorTraceId (Meta fbtrace_id) to correlate a failed send and quote it when escalating to Meta Direct Support. It’s only populated when status is failed (Meta platforms).

New field: errorTraceId (string | null)

View documentation
Instagram now supports attaching catalog audio to Reels via platformSpecificData.audioConfiguration.

You can search/trend audio with GET /v1/accounts/{accountId}/instagram/audio (omit q for trending) and fetch metadata with GET /v1/accounts/{accountId}/instagram/audio/{audioId}. Use the returned audioId when creating a Reel.

Search params:
audioType: music | original_sound
q: optional keywords (omit for trending)

To publish with audio, set:
platformSpecificData.audioConfiguration.audioId (required)
platformSpecificData.audioConfiguration.audioVolume (0-100)
platformSpecificData.audioConfiguration.videoVolume (0-100)

Requires an Instagram account connected via Facebook Login. Classic Instagram Login accounts will get a 400 instagram_audio_requires_facebook_login and must be reconnected using the Facebook option.

View documentation
TikTok ad reporting now populates additional video metrics in AdMetrics (previously documented as Meta-only).

This lets you use the same video KPI fields across Meta and TikTok when reading ad/campaign metrics.

TikTok sources now apply to:
videoPlayActions
videoP25WatchedActions
videoP50WatchedActions
videoP75WatchedActions
videoP100WatchedActions
videoAvgTimeWatchedActions

Meta-only remains:
video30SecWatchedActions
videoThruplayWatchedActions
videoP95WatchedActions
costPerThruplay

View documentation
Google Ads now supports keyword edits on existing ads via PUT /v1/ads/{adId}.

You can update Google Search ad group keywords by sending targeting.keywords and/or targeting.negativeKeywords. Each list you send becomes the FULL new set of that kind (items not included are removed); omit a kind to leave it unchanged.

Use:
targeting.keywords (Google only) — items are "string" (defaults to broad) or { text, matchType } where matchType is exact | phrase | broad
targeting.negativeKeywords (Google only) — same shape as above

Also added for Google Search creation via POST /v1/ads/create:
negativeKeywords — broad-match negative keywords on the new ad group (editable later via PUT /v1/ads/{adId} targeting.negativeKeywords).

View documentation
Ad analytics now include additional Meta click + cost breakdown fields in AdMetrics.

This adds per-action cost metrics and separates different click definitions (attributed vs in-session vs outbound), making it easier to build accurate dashboards and optimize campaigns.

New fields:
costPerAction (object) — derived spend / actions[type] per action type
outboundClicks, outboundClicksCtr
inlineLinkClicks, inlineLinkClickCtr
uniqueClicks, uniqueCtr

Note: these click fields are Meta-only; other platforms return 0 (and costPerAction may be {} when not applicable).

View documentation
LinkedIn posts now support polls via poll in LinkedInPlatformData.

Use this to publish a LinkedIn poll with your post. Polls cannot be combined with media or reshareUrl, cannot be edited after publishing, and API-created polls are non-sponsored only.

Set poll with:
question (1-140 chars)
options (2-4 items, 1-30 chars each)
duration (optional): ONE_DAY | THREE_DAYS | SEVEN_DAYS | FOURTEEN_DAYS (default SEVEN_DAYS)

View documentation
POST /v1/ads/create now supports TikTok Smart+ campaigns via smartPlus.

When smartPlus: true, Zernio creates an Upgraded Smart+ campaign where TikTok automates targeting/bidding/delivery (within your provided geo targeting).

Key fields:
• Enable: smartPlus (boolean)
• Supported goal: conversions, lead_generation, app_promotion
• Web Smart+ requires promotedObject.pixelId + promotedObject.customEventType
• App Smart+ requires promotedObject.applicationId
• Budget is campaign-level; budgetType=lifetime requires endDate
• Cannot be combined with adSetId

View documentation
1
TikTok ads now populate engagement breakdown in AdEngagementCounts (previously Meta-only).

You can now read paid engagement interactions for TikTok directly from engagementBreakdown (while TikTok follows remain in actions).

Key fields:
AdMetrics.engagementBreakdown.reactions (TikTok paid likes)
AdMetrics.engagementBreakdown.comments
AdMetrics.engagementBreakdown.shares
AdMetrics.actions.follow (TikTok followers objective result)

Other platforms are unchanged (still 0 where not supported).

View documentation
1
Google Local Services Ads lead insights are now available via two new endpoints.

You can fetch LSA leads live from Google (newest first) and then pull the conversation history (calls/messages) for a specific lead. Note: data is not persisted (Google is the source of truth), and requests draw from the shared Google Ads operations budget.

Get leads with GET /v1/ads/local-services/leads
Required: accountId
Optional filters: customerId, fromDate, toDate, leadType (PHONE_CALL/MESSAGE/BOOKING), leadStatus (e.g. NEW, BOOKED, WIPED_OUT), chargedOnly, pageToken
Key fields: id, contact (null when WIPED_OUT), charged, creditState, createdTime

Get lead conversations with GET /v1/ads/local-services/leads/{leadId}/conversations
Params: leadId, accountId (+ optional customerId, pageToken)
Conversation fields include channel (PHONE_CALL/MESSAGE/SMS/EMAIL/WHATSAPP/ADS_API), participantType (ADVERTISER/CONSUMER), plus phoneCall.callRecordingUrl / message.attachmentUrls when applicable.

View documentation
1
Campaign listing and tree endpoints now support filtering by delivery/spend within a date window.

On GET /v1/ads/campaigns, you can now return only campaigns that actually delivered in fromDatetoDate, or that reached a spend threshold.

New query params:
hasDelivery (boolean) — only campaigns with delivery in the window (spend > 0 or impressions at zero spend)
minSpend (number, min 0) — only campaigns whose spend in the window reaches this amount (in the campaign’s own currency); implies hasDelivery

On GET /v1/ads/tree, the same filters were added, and fromDate is clarified: it affects metrics only unless you also pass hasDelivery or minSpend. Pagination totals reflect the filtered campaign set.

View documentation
1
GET /v1/inbox/comments/{postId} now supports passing a Facebook/Instagram comment ID as postId to fetch that comment’s replies (instead of the post’s top-level comments).

This lets you page deeper reply threads without a separate endpoint; on YouTube, postId must still be a video id.

Use:
GET /v1/inbox/comments/{postId}?accountId=... where {postId} can be a post ID or (FB/IG only) a comment ID
• New response signal (Facebook only): repliesHasMore (when true, fetch remaining replies by calling the same endpoint with that comment’s id as postId)

Also updated for GET /v1/inbox/comments pagination:
nextCursor pagination can continue past the first page on facebook, instagram, threads, linkedin, youtube
• Cursor paging is only coherent for sortBy=date + sortOrder=desc; other sort combinations can make page 2 unreliable
• Treat nextCursor as opaque and dedupe results by id

View documentation
GET /v1/ads/targeting/search expands geo targeting with a new geoType option all and additional geo types.

You can now search across multiple geo levels in one call (relevance-ranked) instead of picking a single level.

Updated geoType enum:
all, country, region, city, subcity, neighborhood, place, zip, metro_area, geo_market

Pinterest notes: city is treated as an alias for all (Pinterest has no city-level catalog), and some geo types (e.g. zip) return 400 because Pinterest doesn’t expose those catalogs.

View documentation
Workflow nodes now support an optional display label via WorkflowNode.label.

This lets you set a human-friendly name shown in the builder canvas/inspector (falls back to the node type when omitted). If you update a workflow, remember the nodes array is replaced wholesale—so resend label to keep it.

Use label (string, 1–80 chars) on WorkflowNode.

View documentation
POST /v1/posts now supports LinkedIn plain reposts (no commentary).

If every platform entry is a LinkedIn repost using platformSpecificData.reshareUrl, you can omit top-level content (and omit customContent) to publish LinkedIn’s one-click “Repost” with no text.

Use platformSpecificData.reshareUrl (LinkedIn post URL or urn:li:share/urn:li:ugcPost/urn:li:groupPost). If you include content, it becomes a quote-reshare (repost with your thoughts).

View documentation
You can now attach a Meta multi-language ad when adding an ad to an existing ad set via POST /v1/ads/create.

In adSetId (attach) mode, you can provide a per-locale asset feed so one ad carries localized copy (and optional localized media) while keeping social proof on a single post.

Use:
adSetId (attach mode)
defaultLocale (e.g. en, pt_BR)
translations[] with required fields: locale, headline, body, description (optional per-locale: linkUrl, imageUrl, videoUrl, thumbnailUrl)

Note: translations is mutually exclusive with dynamicCreative, placementAssets, carouselCards, existingCreativeId and creatives[].

View documentation
Webhooks now support referral click events via the new referral.received event.

This lets you capture Meta referral payloads even when a click opens an existing thread without generating a message (previously you could only see referrals on message.received when Meta attached them).

To subscribe, include referral.received in events when creating/updating a webhook:
POST /v1/webhooks/settings events
PUT /v1/webhooks/settings events

The webhook payload is WebhookPayloadReferral with:
event = referral.received
referral (Meta object forwarded verbatim; may include ref, source, type, referer_uri, ad_id, ads_context_data)
sender, conversation, account, timestamp

View documentation