Zernio | Developer News
685 subscribers
1 photo
1 video
480 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/tools/validate/post now accepts accountId per platform to validate against account-specific limits.

For platform=twitter, providing platforms[].accountId lets validation apply the correct character limit (280 vs 25000 for X Premium). Missing/invalid/foreign IDs fall back to 280 and won’t error.

Use:
platforms[].platform (enum: twitter, instagram, tiktok, youtube, facebook, linkedin, bluesky, threads, reddit, pinterest, telegram, snapchat, googlebusiness, discord)
platforms[].accountId

View documentation
GET /v1/accounts now enforces stricter pagination and query validation.

If you use server-side pagination, you must send page and limit together; sending only one now returns 400. Out-of-range page/limit values are also rejected with 400 (instead of being silently clamped).

Key params:
page (1-based) + limit (1–100) must be supplied together
profileId must be a valid ObjectId

View documentation
KYC submission now enforces stricter validation when requesting multiple numbers with an areaCode.

If you pass areaCode and request quantity above that area’s live inventory, the request is rejected with 400 (instead of best-effort skipping).

Applies to:
POST /v1/phone-numbers/kyc
POST /v1/whatsapp/phone-numbers/kyc

Key params: areaCode, quantity (1–5).

View documentation
New endpoint GET /v1/twitter/search lets you search public tweets from the last 7 days using an X query (passed through unchanged) to discover tweets to reply to.

Use it with:
accountId (required)
query (required, 1–512 chars)
limit (10–100, default 10)
cursor (pagination)
sinceId / untilId (numeric tweet IDs)
startTime / endTime (ISO 8601 UTC, within last 7 days)
sortOrder: recency | relevancy (default recency)

To reply to a found tweet, set platformSpecificData.replyToTweetId to the returned tweets[].id when creating a post.

Rate limit: 300 requests per 15 minutes per connected account.

View documentation
Webhook limits have increased: you can now configure up to 50 webhooks per user (was 10).

This lets you split event subscriptions across more endpoints/environments without hitting the previous cap.

Applies to:
GET /v1/webhooks/settings (list up to 50)
POST /v1/webhooks/settings (create up to 50; 400 if max reached)

View documentation
SMS carrier registrations now support a preflight dry-run and change-request replies.

Use POST /v1/sms/registrations/preflight to validate and preview the exact 10DLC brand/campaign payloads that would be stored, without creating a registration. It returns compliance findings (including AI review when available) so you can fix issues before submitting.

Request: registrationType (standard_10dlc | sole_prop_10dlc), brand, campaign, optional phoneNumbers, optional messagingBrandName
Response: composed, advisories (severity block | warn), verdict (pass | warn | fail | unreviewed), aiUnavailable

Use POST /v1/sms/registrations/{id}/respond to reply to a reviewer change request (when a registration is in changes_requested) with a note, files (hosted URLs), or both—without resubmitting the full registration. On success, status returns to requested.

Also added to POST /v1/sms/registrations: messagingBrandName (2–60 chars) to brand samples/auto-replies when it differs from the legal brand.displayName.

View documentation
Meta conversion goals on POST /v1/ads/create now support optimizing against a Custom Conversion.

This lets you run goal conversions or lead_conversion without providing a standard event type, by pointing the ad set at an existing Custom Conversion.

Use promotedObject.customConversionId (instead of promotedObject.customEventType) for:
goal=conversions
goal=lead_conversion

View documentation
Instagram is now supported for typing indicators via POST /v1/inbox/conversations/{conversationId}/typing.

Calling this endpoint can now show typing... to the Instagram recipient (works for both Instagram Login and Facebook Login accounts; recipient must be signed in to Instagram to see it).

Request: { "accountId": "..." }
Response: { "success": true|false } where success indicates whether the typing indicator was actually sent (false on unsupported platforms or if the platform call failed).

The endpoint may now also return 400 (BadRequest) in addition to existing responses.

View documentation
👏1
Webhooks now support post.platform.deleted in events for POST /v1/webhooks/settings and PUT /v1/webhooks/settings.

This event fires when Zernio’s background sync detects a platform post (published via Zernio) was later deleted on the platform (poll-driven ~hourly, not real-time).

Subscribe by including post.platform.deleted in events.

In WebhookPayloadPostPlatform:
event: post.platform.published | post.platform.failed | post.platform.deleted | post.tiktok.url_resolved
platform.status: published | failed | deleted
• New field on delete events: platform.deletedAt (ISO 8601)

View documentation
POST /v1/sms/registrations no longer requires phoneNumbers in the request body.

For 10DLC registrations, if phoneNumbers is omitted or an empty array, it now defaults to your active SMS-enabled US local numbers that aren’t already covered by another registration.

Key request fields:
registrationType: standard_10dlc | sole_prop_10dlc | toll_free
phoneNumbers: optional (new defaulting behavior for 10DLC)

View documentation
Instagram and Facebook Inbox now include pre-connect DM history replay in GET /v1/inbox/conversations and GET /v1/inbox/conversations/{conversationId}/messages.

When an IG/FB account is connected, Zernio replays existing Meta DM history in the background so older threads can appear in listings; if you sync into your own store, you should re-sweep rather than relying on a single pass at connect time.

Key behavior:
GET /v1/inbox/conversations: up to 500 conversations per account are replayed; replayed threads keep their original lastMessageAt/updatedTime so they sort into date order (not bumped to the top)
• Replayed history emits no webhooks and is stored as read (does not affect unreadCount)
GET /v1/inbox/conversations/{conversationId}/messages: replay includes the 500 most recent messages per conversation; older messages beyond that are not retrievable

View documentation
New Google Ads keyword research endpoints are available, and GET /v1/ads/insights now supports Google Ads queries.

You can now pull Keyword Planner data via:
POST /v1/ads/keywords/ideas (generate ideas from seedKeywords and/or seedUrl)
POST /v1/ads/keywords/historical-metrics (metrics for up to keywords = 1000)

Key request fields:
accountId (required)
customerId (optional; needed if the connection has several Google Ads accounts)
countries (optional; omitted = worldwide)
languageConstantId (default 1000)
network: GOOGLE_SEARCH | GOOGLE_SEARCH_AND_PARTNERS

GET /v1/ads/insights now routes by platform:
• Meta: use objectId (+ fields/breakdowns/filtering as before)
• Google Ads: use query (GAQL SELECT), with paging via pageToken and paging.nextPageToken (fixed 10,000 rows/page)

Note: Google responses return raw rows (camelCase) with int64 counters encoded as strings; bid/CPC values are in micros.

View documentation
POST /v1/inbox/conversations/{conversationId}/messages now returns more detailed send failures in 400 responses.

You can distinguish missing-recipient cases and, for Instagram/Facebook, get Meta’s diagnostic fields to debug rejected sends.

New 400 fields:
code: PLATFORM_LIMITATION | MISSING_PARTICIPANT
platformError (Instagram/Facebook only): code, subcode, fbtraceId, type

View documentation
POST /v1/posts/sync-external now supports LinkedIn personal profiles.

Because LinkedIn personal profiles don’t have a listing API, you must provide url to import a single post authored by the connected member (works even for posts published before the account was connected).

Use accountId + url (required for LinkedIn personal), where url can be:
linkedin.com/posts/…
linkedin.com/feed/update/urn:li:activity:…
urn:li:share:… / urn:li:ugcPost:…

Imported LinkedIn personal posts return analytics (impressions, reach, reactions, comments, reshares, saves) but no content/media.

View documentation
Instagram user tagging behavior has changed for platformSpecificData.instagram.userTags.

userTags items now require only username (previously username + x + y were required). Tag handling now depends on media type:
• Photos: x/y are required; photo tags without valid coordinates are skipped
• Reels/videos: username-only tags; any x/y provided are ignored
• Stories: x/y are optional

Carousel targeting still uses mediaIndex (defaults to 0); out-of-range indices are ignored.

View documentation
Phone number purchases can now be auto-assigned to a different profile when the requested profileId already has a number.

Send profileId as the preferred profile, but the API may assign the number to the next free profile (or create one) and will return the actual assignment.

Check the assigned profile in:
phoneNumber.profileId (when a number is provisioned)
profileId in the { status: "already_purchased" } response when using purchaseIntentId

View documentation
POST /v1/media/presign now supports generating presigned upload URLs for audio files.

You can upload audio by setting contentType to one of: audio/mpeg, audio/mp4, audio/aac, audio/ogg, audio/wav, audio/webm, audio/x-m4a.

View documentation
Facebook analytics now supports per-post monetization earnings via GET /v1/analytics/facebook/post-earnings.

This returns lifetime cumulative earnings for a single post, read live from Meta on each request (no date range supported).

Required params:
accountId
postId (e.g. {pageId}_{postId} or bare Reels video ID)

Optional:
metrics = content_monetization_earnings, monetization_approximate_earnings (defaults to both)

Notes:
since/until/period/metricType/breakdown are rejected with 400 (lifetime only).
• Money metrics include unit (micro_amount | unspecified) and currency (string | null), and are never rescaled.
• Metrics Meta can’t serve are omitted from metrics and listed in unavailableMetrics (HTTP 200).

Also: GET /v1/analytics/facebook/page-insights now accepts monetization metrics (content_monetization_earnings, monetization_approximate_earnings) and may return unavailableMetrics with the same omit-vs-zero behavior.

View documentation
Meta ads reporting now supports filtering by Facebook Page via pageId on ads, campaigns, and the campaign tree.

This lets you scope results and rolled-up metrics to ads whose creative is backed by a specific Page (useful when one Meta ad account serves multiple Pages).

New query param:
pageId (Meta only)

Available on:
GET /v1/ads
GET /v1/ads/campaigns
GET /v1/ads/tree

Matches against ad.creative.pageId (may be null for non-Meta ads and rare IG-only creatives).

View documentation
Meta ad URL tags now preserve Meta dynamic macros in tracking parameters.

When setting Meta urlTags, macros like {{ad.id}}, {{campaign.id}}, {{placement}} are sent through unescaped so Meta can expand them; all other characters are percent-encoded.

Applies to:
PATCH /v1/ads/{adId}/tracking-tags via urlTags
POST /v1/ads/boost via tracking.urlTags
POST /v1/ads/create via tracking.urlTags

View documentation
Rate limit guidance has been updated: the per-account velocity limit referenced in 429 responses is now 25 posts/hour per account (was 15) for:
POST /v1/posts
POST /v1/posts/{postId}/retry

If you throttle based on the error text for 429, update your assumptions to match the new 25 posts/hour limit.

View documentation