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
POST /v1/contacts/bulk now treats missing platformIdentifier as a per-contact failure when importing in channel mode.

If you send a top-level accountId, each contact is expected to include platformIdentifier; rows missing it are rejected individually and returned in errors[] under an HTTP 200 (the whole import no longer fails with HTTP 400 for that case).

Key behavior:
• Channel mode: accountId set + contact missing platformIdentifier → HTTP 200 with errors[]
• Request-level 400 still applies for missing required name, or when a row includes channel data but there is no top-level accountId to attach it to

View documentation
1
GET /v1/ads/accounts now returns additional platform status details for ad accounts.

This lets you distinguish platform-specific states (e.g., Meta disabled vs. billing vs. review) and show better “why can’t I run ads?” messaging instead of relying only on selectable.

New fields in accounts[]:
accountStatus (platform status; type varies by platform)
disableReason (Meta only; integer code when accountStatus indicates disabled)
approvalStatus (X only; observed: ACCEPTED, PENDING, REJECTED)

Note: LinkedIn continues to use status (filtered to active accounts).

View documentation
GET /v1/contacts now supports filtering by multiple tags via a new tags query parameter.

Use tags as a comma-separated list to match contacts that have any of the provided tags.

New parameter: tags (comma-separated, OR match). Example: tags=vip,lead,partner

View documentation
LinkedIn ads now return delivery diagnostics and effective bidding details via new fields on Ad and GET /v1/ads/tree nodes.

This lets you distinguish “configured active” vs “actually serving” and understand why a LinkedIn campaign/creative is not delivering (holds, review, budget, etc.).

New fields:
Ad.costType (LinkedIn only): CPM | CPC | CPV
Ad.servingStatuses (LinkedIn only): array of serving/hold reasons (e.g. RUNNABLE, ACCOUNT_TOTAL_BUDGET_HOLD)
Ad.creative.isServing (LinkedIn only): true | false | null
Ad.creative.servingHoldReasons (LinkedIn only): creative-level hold reasons (e.g. UNDER_REVIEW, REJECTED)

Also added to tree responses:
AdTreeAdSet.costType, AdTreeAdSet.servingStatuses

Note: for LinkedIn, bidAmount represents the campaign’s effective unitCost and can be 0 (a real, delivery-stopping value), independent of bidStrategy.

View documentation
PostAnalytics now includes videoDurationSeconds (nullable integer).

This exposes the video length (currently Instagram Reels only) so you can estimate retention when combined with igReelsAvgWatchTime (ms).

New field:
videoDurationSeconds - video length in seconds; null when unknown (non-video/other platforms, or when Instagram doesn’t expose the media URL, e.g. some copyrighted-audio reels).

View documentation
Sending inbox messages now supports idempotent retries via Idempotency-Key on POST /v1/inbox/conversations/{conversationId}/messages.

This prevents duplicate sends when you retry after timeouts: same key + same body replays the original response (with Idempotent-Replayed: true) instead of sending again. Works for both application/json and multipart/form-data requests; keys are retained for 24 hours.

Use:
Idempotency-Key (header)

New error responses:
409 - Idempotency key still in flight
422 - Idempotency key reused with a different body

View documentation
WhatsApp now supports Meta Direct Send utility messages via category on inbox messaging endpoints.

Eligible WhatsApp Business Accounts can start or send a business-initiated utility text message without an approved template (Meta matches/auto-creates a template asynchronously). If the WABA isn’t eligible, the request fails and you must use an approved template.

Use category:
POST /v1/inbox/conversations: set category=utility with message and no templateName (WhatsApp only)
POST /v1/inbox/conversations/{conversationId}/messages: set category=utility to send as a business-initiated utility message (WhatsApp only)

Notes:
category enum: utility
category cannot be combined with templateName (create-conversation) or template (send-message)
category is accepted on application/json only (not multipart/form-data)

View documentation
POST /v1/sms/sender-ids no longer enforces global uniqueness for alphanumeric sender IDs.

The same senderId can now be registered by multiple workspaces, so creating a sender ID should no longer fail due to another workspace already having it.

Changes:
• Removed 409 (sender_id_taken) conflict response
• Increased active sender ID limit in 403 from 5 to 1,000 (sender_id_limit_reached)

Request remains: senderId

View documentation
In PUT /v1/posts/{postId}, platforms[].platformSpecificData update behavior is now explicitly defined.

A Settings namespace (e.g. facebookSettings, tiktokSettings) that is omitted from the request is preserved from the stored post. If you send the key, it replaces the entire namespace (it is not deep-merged).

Key field: platforms[].platformSpecificData

View documentation
Updating a post now preserves per-platform settings unless you explicitly send them in platforms[].platformSpecificData.

A Settings namespace (e.g. facebookSettings, tiktokSettings) omitted from the request is kept from the stored post. If you include the key, it replaces the entire namespace (no deep-merge).

Also, port-in now supports Brazil by allowing endUser.countryCode = BR in POST /v1/phone-numbers/port-in (enum now includes US, CA, GB, ES, DE, FR, NL, AU, BR).

View documentation
PUT /v1/posts/{postId} now requires platforms when sending root-level tiktokSettings or facebookSettings.

If you include tiktokSettings or facebookSettings in an update request without a platforms array, the API will return 400. Include the target platforms (with platform + accountId) in the same request when updating these settings.

View documentation
Connect flow redirects now include the created social account id in the success redirect_url for these endpoints:
POST /v1/connect/facebook/select-page
POST /v1/connect/googlebusiness/select-location
POST /v1/connect/pinterest/select-board
POST /v1/connect/snapchat/select-profile

This lets you reliably capture the connected account immediately after OAuth without extra lookup.

Look for accountId in the returned redirect_url query params (e.g. ...&accountId=...).

View documentation
Ad.creative.thumbnailUrl can now be null (was always a string).

This affects ad preview/rendering: don’t assume a thumbnail URL is always available; fall back to other creative fields when it’s missing.

Key change: creative.thumbnailUrl: string | null

View documentation
WhatsApp template creation now supports NAMED variables via parameter_format on POST /v1/whatsapp/templates.

This lets you use {{customer_name}}-style tokens (in addition to the existing positional {{1}} style) and provide per-variable examples for Meta review.

Set parameter_format to POSITIONAL | NAMED (also accepts positional | named).

For NAMED templates, include examples in:
components[].example.body_text_named_params
components[].example.header_text_named_params

Each named param example uses: { param_name, example }.

View documentation
🔥1
Meta Ads now supports Value Rule Sets (bid adjustments by audience segment) via new endpoints under /v1/ads/value-rule-sets.

You can create/list/read/replace/delete value rule sets, then attach one to an ad set to increase/decrease bids for matching segments (rule order matters: first match wins).

New endpoints:
GET /v1/ads/value-rule-sets
POST /v1/ads/value-rule-sets
GET /v1/ads/value-rule-sets/{valueRuleSetId}
PUT /v1/ads/value-rule-sets/{valueRuleSetId} (full replace, not patch)
DELETE /v1/ads/value-rule-sets/{valueRuleSetId}

Attach/detach on ad sets (Meta only) via PUT /v1/ads/ad-sets/{adSetId}:
• Attach/replace: send valueRuleSetId (optionally valueRulesApplied: true)
• Detach: send valueRulesApplied: false and omit valueRuleSetId
• Sending valueRulesApplied: false with valueRuleSetId returns 400 mutually_exclusive_fields

Also supported on create (Meta only) via POST /v1/ads/create:
• Attach to the new ad set with valueRuleSetId (optional valueRulesApplied: true)
valueRulesApplied: false is rejected on create

Eligibility (Meta enforced): only ad sets on LOWEST_COST_WITHOUT_CAP or COST_CAP.

View documentation
🔥1
POST /v1/ads/boost now supports Meta-only optimization goal overrides via optimizationGoal.

This lets you explicitly set the ad set optimization_goal instead of relying on the default derived from goal, which can be useful when Meta requires a different optimization goal for the boost objective.

Set optimizationGoal to a Meta-compatible value for the objective derived from your goal (e.g. boost goal engagement maps to Meta objective OUTCOME_AWARENESS, so values like REACH, IMPRESSIONS, AD_RECALL_LIFT, or THRUPLAY-class are accepted; POST_ENGAGEMENT is rejected under that objective).

View documentation
New endpoint GET /v1/auth/verify was added to validate a bearer credential (API key or OAuth access token) without calling a data endpoint.

Use it to confirm a credential is valid before proceeding (e.g., when verifying an incoming token).

On 200 it returns valid, userId, authType (api_key | oauth | session), and scope (space-separated OAuth scopes; null for API keys). 401 if invalid.

View documentation
Contacts listing now supports filtering by SocialAccount via accountId on GET /v1/contacts.

Use accountId to list contacts through their channel; when accountId is set, the profileId contact filter is not applied (a provided profileId is still access-checked and still scopes filters.tags).

New query param: accountId

Also, POST /v1/contacts 409 is now explicitly documented as: Duplicate channel. The platformIdentifier is already bound to a channel on this accountId.

View documentation
OAuth connect now surfaces upstream token-exchange failures via a new 502 response on POST /v1/connect/{platform}.

This helps distinguish platform-side rejections from internal errors; handle 502 separately from 500 (the upstream 4xx is forwarded as platform_error).

Key response codes:
502 - platform rejected token exchange (platform_error)
500 - internal error while connecting the account

Lead form creation can now return 422 on POST /v1/ads/lead-forms when Meta rejects the form (error code 3), typically because the Facebook Page hasn’t accepted the Lead Ads Terms.

Key response code:
422 - Meta rejected lead form (often requires a Page admin to accept Lead Ads Terms)

View documentation
Slack DMs are now supported in the Inbox API.

You can list eligible Slack workspace members (for a recipient picker) via GET /v1/accounts/{accountId}/slack-members, then start a DM by passing the member id as participantId to POST /v1/inbox/conversations.

Key params:
GET /v1/accounts/{accountId}/slack-members: query, limit
POST /v1/inbox/conversations: participantId (Slack member id, e.g. U01ABCDEF)

Also updated: POST /v1/inbox/conversations now lists supported platforms as X/Twitter, Bluesky, Reddit, WhatsApp, SMS, and Slack.

View documentation
1
WhatsApp now supports requesting a user’s contact info via interactive messages.

You can send a consent-based “share contact” prompt, and inbound replies include contact cards plus an origin flag so you can distinguish consented self-share vs address-book shares.

Send with interactive.type:
request_contact_info

On message.received, check:
metadata.contacts (array)
metadata.contactsOrigin: contact_request | other

View documentation
🔥1