Zernio | Developer News
677 subscribers
1 photo
1 video
477 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
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
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