Zernio | Developer News
686 subscribers
1 photo
1 video
484 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
Google Search ads created via POST /v1/ads/create now support campaign-level sitelinks.

You can create and attach sitelink assets during ad creation (Google reviews them asynchronously; approval is not immediate).

Use sitelinks (Google Search only):
• 2–20 items
• each item: text (max 25) + linkUrl (URI)
• optional: description1, description2 (each max 35)

Applies when campaignType is search.

View documentation
Pinterest posts now support targeting a board section and optionally disclosing AI-modified content via PinterestPlatformData.

Use boardSectionId to publish into a specific section within boardId (omit it to post to the board). Pinterest rejects the pin if the section doesn’t belong to the board, so send both together.

Set isAiGenerated to true to include Pinterest’s AI_MODIFIED disclosure (shows an “AI modified” label); false (default) omits the disclosure.

View documentation
Threads posts now support an optional first comment via platformSpecificData.threads.firstComment.

This lets you automatically publish a reply immediately after the main post is published; when using threadItems, it replies to the root post.

Key field:
firstComment (string, max 500)

Note: the first comment is a Threads post and counts toward the profile’s 250 posts / 24h limit.

View documentation
Instagram now supports publishing videos muted via muteAudio in platformSpecificData.

When muteAudio=true, we strip the audio track before sending the video to Instagram, so the published video is permanently silent.

Use:
muteAudio (boolean, default false)

Applies to Reels, Stories, and video carousel slides; ignored for images. Videos above 200MB cannot be muted (mute before uploading).

View documentation
You can now fetch a single Google Business Profile review by ID via GET /v1/accounts/{accountId}/gmb-reviews/{reviewId}.

This returns one review in the same shape as the items from GET /v1/accounts/{accountId}/gmb-reviews, which is useful for checking the latest state before replying (replies are overwritten in place).

Key params:
accountId (path)
reviewId (path, ID portion only)
locationId (query, optional override; otherwise uses the account’s selected location)

Response includes review (GoogleBusinessReview).

View documentation
GET /v1/users now returns lastLoginAt for each user.

This lets you track recent workspace sign-ins (stamped at most once per hour). The field may be omitted for users with no recorded sign-in since it was introduced.

New field:
users[].lastLoginAt

The message.sent webhook payload now supports more platforms and can include send context in a new metadata object.

Updated enum:
message.platform: instagram, facebook, telegram, whatsapp, twitter, reddit, bluesky, slack

New optional fields:
metadata.quotedMessageId
metadata.threadTs (Slack only)

View documentation
GET /v1/whatsapp/template-library now supports selecting a specific language variant via an optional language query param.

This lets you request a desired locale (e.g. es, en_US); if it’s not available, the API returns the first available variant and reports the actual variant in template.language.

New:
• Query param: language
• Response field: template.availableLanguages (array of supported language codes)

View documentation
DELETE /v1/ads/campaigns/{campaignId} now accepts platform=google.

You can delete Google campaigns using the same endpoint and request body shape as Facebook/Instagram.

Set platform to one of: facebook, instagram, google

For empty campaigns (0 ads), include accountId to allow deletion: accountId (Zernio SocialAccount id).

View documentation
DELETE /v1/ads/campaigns/{campaignId} now accepts platform=google.

This lets you delete Google campaigns via the same endpoint used for Meta.

Set platform to one of: facebook, instagram, google.

accountId behavior is unchanged (only needed to delete an EMPTY campaign with zero ads).

View documentation
WhatsApp inbox conversation creation now supports sending values for template buttons that carry their own payload (copy-code / flow tokens), via templateButtonParams on POST /v1/inbox/conversations.

Use this when your approved WhatsApp template has buttons that need a per-send value (e.g. Pix copy-and-paste code, coupon code, Flow token). This complements templateParams (text variables + dynamic URL buttons).

Set templateButtonParams as an array of:
index (0-9)
subType: url | copy_code | flow
value

Message lineage is now exposed as sentVia on GET /v1/inbox/conversations/{conversationId}/messages and on the message.sent webhook, with values:
human | api | broadcast | sequence | workflow | comment_automation | bulk-api | null

Note: existing stored messages are not backfilled; treat sentVia: null as “unknown”.

View documentation
Inbox messages now include send attribution via sentVia.

This lets you tell which Zernio surface produced an outgoing message (API vs human vs broadcast/workflow/etc). It’s available on message listing responses and on the message.sent webhook.

New field:
sentVia: human | api | broadcast | sequence | workflow | comment_automation | bulk-api | null

Notes:
• Present on GET /v1/inbox/conversations/{conversationId}/messages message items
• Present on WebhookPayloadMessageSent.message.sentVia
• Not backfilled for older stored messages; treat null as “unknown”

WhatsApp template sends can now pass per-button values via templateButtonParams when creating a conversation.

Use this for buttons that carry a value at send time (e.g. copy-code payloads or flow tokens), which are not covered by templateParams.

New request field on POST /v1/inbox/conversations (JSON body only):
templateButtonParams[] items: { index, subType, value }
- index: 0..9
- subType: url | copy_code | flow
- value: string

Analytics sorting now supports additional Instagram metrics on GET /v1/analytics.

New sortBy values:
ig_reels_avg_watch_time
ig_reels_video_view_total_time
reposts
reels_skip_rate

View documentation
PlatformTarget.platformSpecificData is now returned as a free-form object (with additionalProperties: true) instead of a oneOf union of *PlatformData schemas.

This avoids generated clients failing to deserialize PlatformTarget responses when platformSpecificData doesn’t match exactly one variant. Treat platformSpecificData as an untyped JSON object on responses (it may be omitted rather than returned as an empty object).

View documentation
Instagram is now supported (limited release) for liking/unliking posts and comments via:
POST /v1/inbox/posts/{postId}/like, DELETE /v1/inbox/posts/{postId}/like
POST /v1/inbox/comments/{postId}/{commentId}/like, DELETE /v1/inbox/comments/{postId}/{commentId}/like

This enables engagement actions on Instagram feed media (posts/reels/carousels) and their comments/replies, but access is currently restricted by Meta permissions.

Instagram requirements/behavior:
• Requires permission instagram_manage_engagement
• Only works for accounts connected via Facebook Login; Instagram Login returns 400 with code instagram_likes_require_facebook_login
• Accounts whose token predates the permission return 403 with code reconnect_required
• Limited release: non-allowed accounts return 403 with code PLATFORM_BETA_RESTRICTED
• Burst limit: 50 like/unlike calls per 5s per Instagram account; exceeding can lock likes for 1 hour

Also note: these endpoints’ 403 may now indicate missing platform permission (not only Inbox add-on).

View documentation
New endpoint: DELETE /v1/ads/ad-sets/{adSetId} deletes an ad set on the platform and cancels its ads locally (it never deletes the campaign).

Provide the ad set ID via adSetId (path).

Response includes deleted and adCount (number of local Ad documents marked status: cancelled). Returns 501 if deletion isn’t supported on the target platform.

View documentation
New endpoint: GET /v1/accounts/{accountId}/youtube-captions to fetch a YouTube video transcript (plain text plus timed cues), including auto-generated (asr) and uploaded (standard) tracks.

Supports cached reads to avoid repeated YouTube calls; response includes source = cache | youtube and fetchedAt. Use refresh=true only when captions changed (re-downloads from YouTube).

Key params:
accountId (path)
videoId (query, required)
language (query, optional; BCP-47)
format (query) = json | srt (default json)
refresh (query, boolean)

Response highlights: trackKind = asr | standard, cues (when format=json), srt (when format=srt), and availableTracks for language selection.

View documentation
GET /v1/usage metering mode now supports spend attribution by profile/account, plus optional projection onto a single profile or account.

This lets you break down billed spend (USD) across profiles/accounts for a given window, or fetch the metering payload scoped to one group for chargeback/reporting.

New query params (metering mode):
groupBy: profile | account (adds attribution)
profileId: project results onto one profile (pair with range)
accountId: project results onto one account (pair with range)

New response fields in UsageMetering:
attribution (with groupBy, groups, unattributed, totals, restricted)
scope (present with profileId/accountId)

Note: when profileId or accountId is set, peaks, callUsage, and tax return null (workspace-level only).

View documentation
Phone number stock watches are now available via new endpoints: GET /v1/phone-numbers/stock-watches, POST /v1/phone-numbers/stock-watches, DELETE /v1/phone-numbers/stock-watches/{id}.

You can watch an out-of-stock country and get notified the first time deliverable numbers are available again (email + webhook). Watches are checked every 6h, consumed when they fire, and you can watch up to 20 countries.

Create a watch with country (ISO 3166-1 alpha-2). Responses: 201 created, 200 if it already existed, 409 if the country is currently in stock or the 20-country limit is reached.

Webhooks now support the new event phone_number.stock_available in events (for POST /v1/webhooks/settings and PUT /v1/webhooks/settings). Payload includes stock.country and stock.types[] with numberType (local/mobile/national/toll_free) and availableCount.

View documentation
WhatsApp sends via POST /v1/inbox/conversations/{conversationId}/messages now document a per-recipient rate limit.

If you send bursts to the same recipient, WhatsApp may reject excess messages with Meta error code 131056 ("Too many messages sent to this recipient"). Pace sends to a single recipient to ~10/min; sends to other recipients are unaffected.

If you see 400 responses with platformError.code = 131056, throttle per recipient and retry later.

View documentation
WhatsApp templates now support working with a specific Meta template variant via its Meta template id.

New endpoints:
GET /v1/whatsapp/templates/id/{templateId} (requires accountId)
PATCH /v1/whatsapp/templates/id/{templateId} (body: accountId, components)
DELETE /v1/whatsapp/templates/id/{templateId} (requires accountId)

Template listing and name-based operations are now language-aware (Meta stores templates per name + language):
GET /v1/whatsapp/templates adds filters: name, language, status (APPROVED/REJECTED/PENDING/PAUSED/DISABLED/IN_APPEAL/PENDING_DELETION)
GET /v1/whatsapp/templates/{templateName} adds optional language; if multiple variants exist and no language is provided, it can return 409 ambiguous_template with details.languages
PATCH /v1/whatsapp/templates/{templateName} now takes language in the request body (required when multiple variants exist); missing language can return 409 ambiguous_template

Delete behavior change:
DELETE /v1/whatsapp/templates/{templateName} without language deletes all language variants; with language it deletes only that variant and returns scope = all_languages or language.

View documentation
PostAnalytics.follows can now be null in GET /v1/analytics responses.

This affects Instagram where Meta doesn’t expose follows for Reels and non-Reels video; in those cases Zernio will return null instead of 0. Update your parsing/aggregation to handle follows: null (treat as “unknown/not available”, not zero).

View documentation
Some endpoints now return additional billing/capability errors you may need to handle.

Posts listing may now fail with 403 when X analytics isn’t enabled for the account (code X_ANALYTICS_NOT_ENABLED) on GET /v1/accounts/{accountId}/posts.

OpenAI Ads connect may now return 402 PaymentRequired on POST /v1/connect/openai-ads/credentials.

SMS sender ID creation now has explicit billing-related failures on POST /v1/sms/sender-ids:
402 (code payment_required) when no payment method is on file
409 (code billing_setup_incomplete) when billing setup is incomplete

View documentation