Zernio | Developer News
673 subscribers
1 photo
1 video
473 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
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
Meta ads creation now supports optimizing for pixel custom events via promotedObject.customEventStr on POST /v1/ads/create.

Use this when you want goal=conversions (or lead_conversion) to optimize against a custom event name from Events Manager (instead of a standard event or a Custom Conversion).

Set:
promotedObject.customEventType = OTHER
promotedObject.customEventStr = <your_event_name>

customEventStr is case-sensitive, and OTHER requires customEventStr (and vice versa).

View documentation
LinkedIn connected accounts now include additional profile details in SocialAccount.metadata.profileData (refreshed on each daily snapshot).

This lets you read LinkedIn bio/headline and vanity slug info directly from the account object without extra lookups.

Key fields:
metadata.profileData.bio (nullable)
metadata.profileData.extraData.vanityName (personal accounts)
metadata.organizationInfo.vanityName (organization accounts)

View documentation
Comment-to-DM automations now support more precise keyword matching and exclusions via matchMode=word, plus new fields excludeKeywords and typoTolerance.

This lets you avoid accidental triggers (e.g., keyword app no longer firing on happy) and optionally match close misspellings when using word-based matching.

Use matchMode:
containswordexact

New fields:
excludeKeywords: string[] - comments matching these never trigger (compared using the same matchMode)
typoTolerance: boolean - only with matchMode=word

Available on POST /v1/comment-automations, PATCH /v1/comment-automations/{automationId}, and returned by GET endpoints.

View documentation
Ads analytics now includes funnel step counts, an engagement breakdown, and cost per ThruPlay.

This makes it easier to report on Meta ads without parsing/deduping raw actions keys (and avoids double-counting), and adds a direct cost metric for ThruPlays.

New fields in AdMetrics:
costPerThruplay
funnel (ref AdFunnelCounts): landingPageViews, contentViews, searches, wishlistAdds, cartAdds, checkoutsInitiated, paymentInfoAdds, purchases, leads, registrationsCompleted, appInstalls, messagingConversationsStarted, messagingFirstReplies
engagementBreakdown (ref AdEngagementCounts): postEngagement, pageEngagement, reactions, comments, shares, saves, pageLikes, videoViews, linkClicks

Also new in Ad:
creativeType: carousel | video | document | image | null

View documentation
1
PATCH /v1/comment-automations/{automationId} now accepts trigger to control what fires an automation.

You can switch an automation between comment-based and story-reply-based triggers; changing it will detach the automation from its currently bound post/story unless the same request sets a new binding.

Set trigger to: • commentstory_reply (Instagram only)

View documentation
2
GET /v1/comment-automations/{automationId}/logs now returns non-matching comment diagnostics via a new misses object.

Use misses to see how many comments reached the automation but matched none of its keywords (and a few recent samples) to troubleshoot keyword/exclusion setups.

New response fields:
misses.total
misses.retentionDays
misses.samples[]: commentText, commenterName, excludedBy, at

Request validation tightened:
limit now 1..200
skip now >= 0
• Invalid values may return 400 BadRequest

View documentation
1
Instagram follow checks are now available via GET /v1/accounts/{accountId}/follow-status/{userId}.

This resolves the follow relationship (when Meta allows it) and returns isFollower (can be null when unknown) plus basic public counters; use refresh=true to bypass the short cache.

Key fields:
isFollower: true | null (null = unknown, never “no”)
unavailableReason: consent_required | dm_access_disabled | not_messageable | error | null

Comment-to-DM automations now support follower-based targeting + optional follow verification (Instagram only), and delayed sends.

New request fields on POST /v1/comment-automations and PATCH /v1/comment-automations/{automationId}:
dmDelaySeconds (0–86400)
commentReplyDelaySeconds (0–86400)
audience (followerStatus: any | follower | non_follower; whenUnknown: send | skip | verify; optional minFollowerCount)
followGate (message, buttonLabel, optional notFollowingMessage)

Automation logs (GET /v1/comment-automations/{automationId} and .../logs) add new status values pending and gated, plus fields like audienceOutcome (passed | blocked | gate_sent | gate_passed | gate_failed) and nextDueAt while queued.

Webhook comment payloads now may include Instagram-only comment.author.instagramProfile (best-effort) with isFollower/followerCount when available; absence means “unknown”.

View documentation
Message reactions are now supported on Instagram, Facebook Messenger, and Slack via the existing reactions endpoints.

You can add/remove reactions across more inbox channels, and reaction webhooks now cover these platforms too.

Use:
POST /v1/inbox/conversations/{conversationId}/messages/{messageId}/reactions with emoji
DELETE /v1/inbox/conversations/{conversationId}/messages/{messageId}/reactions with accountId (query)

Notes:
• Slack reactions require a Slack emoji name like :thumbsup: (unnamed Unicode emoji returns 400)
• Instagram/Messenger reactions are subject to Meta’s 24h messaging window
reaction.received webhook now includes Slack/Instagram/Messenger (sender can be the business platform id; compare reaction.sender.id with conversation.participantId)

Comment-to-DM automations now support sending a Meta generic template (product card) as the first DM.

Set template on POST /v1/comment-automations / PATCH /v1/comment-automations/{automationId}:
template.type: generic
template.elements: 1–10 cards (each supports title, subtitle, imageUrl, buttons)

Constraints:
template is mutually exclusive with buttons (sending both is a 400)
• For PATCH, pass template: null to clear the card

Also: for POST /v1/inbox/conversations/{conversationId}/messages on Instagram/Facebook, top-level buttons are now explicitly mutually exclusive with template (sending both returns 400).

View documentation
PUT /v1/ads/campaigns/{campaignId} now supports Google campaigns (in addition to Meta) and enforces platform-level field support (unsupported fields return 400, not silently ignored).

For Google, you can set campaign bidding via:
platform: google
bidStrategy (Meta + Google)
bidAmount (Google only; required for LOWEST_COST_WITH_BID_CAP and COST_CAP)
roasAverageFloor (Google only; required for LOWEST_COST_WITH_MIN_ROAS)

The 200 response now echoes back only the fields you sent, plus updated (and may include bidAmount/roasAverageFloor when provided).

View documentation
New endpoint: PATCH /v1/whatsapp/phone-numbers/{id}/profile to move a provisioned number to another profile.

This re-homes the number and any associated accounts together (so you don’t split the number across profiles by moving only the social account).

Send { "profileId": "..." } where id is the phone number record id from GET /v1/phone-numbers (not an account id).

Response may include movedPlatforms with values phone, sms, whatsapp. Conflicts return 409 if the destination profile already has an account on any of those platforms.

View documentation
POST /v1/ads/boost now supports attaching a boost to an existing Meta ad set via adSetId, and adds Meta support for CTA destinations via linkUrl + callToAction.

This lets you refresh/boost an existing post while keeping the ad set’s learning phase (attach to an existing ad set instead of provisioning a new campaign/ad set).

Key changes:
• Meta-only attach: send adSetId to attach under an existing ad set
• When adSetId is set, budget/schedule/targeting are owned by the ad set and sending any of them is a 400
budget is now required only when adSetId is NOT set
• Meta-only additions: instagramAccountId, destinationType (INSTAGRAM_PROFILE, WEBSITE, ON_AD, MESSENGER, WHATSAPP)
linkUrl is now supported on Meta boosts (still supported on TikTok); callToAction must be sent together with linkUrl (sending either alone is a 400)

POST /v1/ads/ctwa (deprecated) also adds an attach flow:
• Send adSetId to add creatives to an existing messaging ad set; then budgetAmount/budgetType/endDate/objective/countries/interests/audienceId are rejected with 400

Schema note (breaking for generated clients): CtwaAdRequestBody no longer lists budgetAmount/budgetType as always-required (they’re required unless adSetId is set).

View documentation
Google Business location selection/updates now validate the provided Google account resource name and will reject mismatches.

If you pass an account identifier to speed up location resolution for large GBP accounts, the API will now return 400 when the provided account is not one of the accounts the connection manages.

Applies to:
POST /v1/connect/googlebusiness/select-location with accountId
PUT /v1/accounts/{accountId}/gmb-locations with googleAccountId (legacy body field accountId still accepted)
POST /v1/accounts/{accountId}/gmb-locations/assign with googleAccountId

View documentation
WhatsApp connect now rejects Zernio-provisioned numbers that are pinned to a different profile with a 409 conflict.

This prevents accidentally connecting a provisioned WhatsApp number from the wrong profileId. If you need to connect it on another profile, move the number first, then retry the connect.

New 409 responses:
GET /v1/connect/whatsapp (when profileId doesn’t match the provisioned number’s profile)
POST /v1/connect/whatsapp/credentials
POST /v1/connect/whatsapp/select-phone-number

Remediation:
• Move the number with PATCH /v1/whatsapp/phone-numbers/{id}/profile, then connect again.

View documentation
Instagram connect now supports headless mode for loginMethod=facebook_login on GET /v1/connect/instagram.

This lets you run the Facebook Login-based Instagram flow with your own UI: after OAuth, the callback redirects to your redirect_url with selection-step params, and you finish by calling GET /v1/connect/instagram/select-account.

Use:
GET /v1/connect/instagram?loginMethod=facebook_login&headless=true
Callback includes: platform=instagram, step=select_account, profileId, tempToken, connect_token

View documentation
Comment-to-DM automations can now also trigger from inbound DMs via alsoMatchInDms.

Set alsoMatchInDms: true on a trigger=comment automation to answer users who DM the keyword instead of commenting it. Deduplication is per source (comment vs DM).

Key details:
alsoMatchInDms (boolean, default false) on POST /v1/comment-automations and PATCH /v1/comment-automations/{automationId}
• Requires at least one keywords entry (empty keyword lists are rejected when enabling)
• Not allowed for trigger=story_reply

Responses now include:
alsoMatchInDms on automation objects (list/get/create/update)
source on log entries: comment | story_reply | dm

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