OmniSocialsOmniSocials
DocumentationAPI reference
Help centerSupport
  • Documentation
  • API reference
Developers
  • Quickstart
  • API reference
  • SDKs
  • MCP server
  • CLI
For AI agents
  • Docs for AI agents
  • llms.txt
  • OpenAPI spec
  • Postman collection
Product
  • OmniSocials
  • Pricing
  • Changelog
  • Help center
Company
  • Support
  • Privacy policy
  • Terms of service

© 2026 OmniSocials

OmniSocials on GitHubOmniSocials on XOmniSocials on LinkedIn
Overview
Get started
SDKs
CLI
MCPDocs for AI agentsWebhooks
Guides
Platforms
Integrations
Migrate
    From AyrshareFrom ZernioFrom Upload-PostFrom Postiz
Plans and billing
GlossaryChangelogAPI reference
Migrate

Migrate from Zernio (Late) to the OmniSocials API

This guide is for developers who post through the Zernio API (formerly Late, getlate.dev) and want to move to the OmniSocials API. Most of the work is renaming fields. The create-post body is flatter, accounts are referenced by channel ID, scheduled times go in as UTC, and webhook signatures use a different header. The biggest change is that social accounts are connected in the OmniSocials dashboard, not through an API connect flow. Zernio facts on this page come from docs.zernio.com and were checked on October 21, 2026.

If your code still uses the Late names (LATE_API_KEY, the late Python package), the same steps apply.

Concepts

ZernioOmniSocialsNotes
API key (sk_...)API key (omsk_live_...)Both go in Authorization: Bearer. An OmniSocials key belongs to one workspace.
Restricted key (zrk_...)Key scopesPick scopes such as posts:write or media:read when you create the key. See Authentication.
ProfileWorkspaceA workspace is one set of connected accounts, usually one brand or client. Create workspaces in the dashboard.
Account (_id)Account (id, for example 368204_instagram)You can also pass the bare channel ID, such as instagram. A workspace has one account per channel.
Connect URL (GET /v1/connect/{platform})Dashboard connectionAccounts are connected only in the dashboard, never through the API.
platforms[] on a postaccounts on a postA flat array of account IDs or channel IDs.
customContent per platformcontent object keyed by channeldefault is the fallback text.
Queue and slotsNoneGET /analytics/best-times recommends slots. You pick the time.
Post status publishingposting
Post status partialwarningPublished on some channels. errors names the rest.
Webhook settingsWebhooksRegistered with POST /webhooks.

Authentication

Both APIs use a Bearer token in the Authorization header.

Text
Zernio: Authorization: Bearer sk_... OmniSocials: Authorization: Bearer omsk_live_...

The difference is in what a key can reach. With Zernio, you pass a profileId to pick a profile. An OmniSocials key is bound to one workspace, which you choose when you create the key. If you serve several brands, create one workspace and one key per brand, and store the key next to the brand in your own database. GET /accounts returns workspace_id and workspace_name, so you can check which workspace a key belongs to.

Create keys under Settings → API in app.omnisocials.com. A key's scopes cannot be edited later. If you need more, create a new key.

Endpoint mapping

All OmniSocials paths are relative to https://api.omnisocials.com/v1. Zernio paths are relative to https://zernio.com/api.

ZernioOmniSocialsNotes
POST /v1/postsPOST /posts/createPOST /posts is an alias. POST /posts/create-and-publish publishes at once.
GET /v1/postsGET /postsFilter with status. Page with limit (max 100) and offset.
GET /v1/posts/{postId}GET /posts/{id}
PUT /v1/posts/{postId}PATCH /posts/{id}Drafts and scheduled posts only. Send only the fields you change.
DELETE /v1/posts/{postId}DELETE /posts/{id}Removes the post from OmniSocials and cancels a scheduled publish. It does not delete a live post on the platform.
POST /v1/posts/{postId}/retryPOST /posts/{id}/retryRetries only the failed channels, up to 3 times per channel.
POST /v1/posts/{postId}/unpublishNoneDelete live posts from the dashboard or in the platform's app.
POST /v1/posts/{postId}/editNonePublished posts cannot be changed.
POST /v1/posts/bulk-uploadNoneCall POST /posts/create once per post.
POST /v1/media/presignPOST /media/create-upload-urlOr skip uploads and pass media_urls on the post. See below.
GET /v1/profiles and profile CRUDNoneManage workspaces in the dashboard.
GET /v1/accountsGET /accountsReturns the key's workspace and its connected accounts.
GET /v1/accounts/healthGET /accountsRead status and needs_reconnect on each account.
GET /v1/accounts/follower-statsGET /analytics/accounts
GET /v1/connect/{platform}NoneConnect accounts in the dashboard.
GET /v1/queue/slots, GET /v1/queue/next-slotGET /analytics/best-timesRecommendations from your own history, not a queue.
GET /v1/analyticsGET /analytics/overview, GET /analytics/posts/{id}, GET /analytics/posts?ids=Bulk reads take up to 100 post IDs.
POST /v1/webhooks/settings and CRUDPOST /webhooks, GET, PATCH, DELETE /webhooks/{id}POST /webhooks/{id}/rotate-secret rotates the secret.
GET /v1/webhooks/logsGET /webhooks/{id}Returns failure_count and last_failure. There is no full delivery log.
POST /v1/webhooks/testNoneSubscribe to post.scheduled and schedule a post through the API. That sends a real event.
POST /v1/tools/validate/postPOST /media/checkChecks media against your connected channels. There is no full post validation call.
GET /v1/inbox/conversations, GET /v1/inbox/commentsGET /inbox/conversationsDMs, comments, and mentions in one list. Filter with type.
POST /v1/inbox/conversations/{conversationId}/messagesPOST /inbox/conversations/{conversationId}/replyNeeds the opt-in inbox:write scope. See Social inbox.

Create a post

The same post in both APIs: one image to LinkedIn and Instagram, scheduled for 09:00 in Amsterdam on November 2, 2026.

Zernio:

TerminalTerminal
curl -X POST https://zernio.com/api/v1/posts \ -H "Authorization: Bearer $ZERNIO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Golden hour at the pier.", "mediaItems": [ { "url": "https://example.com/pier.jpg", "type": "image" } ], "platforms": [ { "platform": "linkedin", "accountId": "66b2e19d8c3f5a7e9d0b1c2d" }, { "platform": "instagram", "accountId": "66b2e19d8c3f5a7e9d0b1c2e" } ], "scheduledFor": "2026-11-02T09:00:00", "timezone": "Europe/Amsterdam" }'

OmniSocials:

TerminalTerminal
curl -X POST https://api.omnisocials.com/v1/posts/create \ -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Golden hour at the pier.", "media_urls": ["https://example.com/pier.jpg"], "accounts": ["linkedin", "instagram"], "schedule_at": "2026-11-02T08:00:00Z" }'

OmniSocials has no timezone field, so the local time is converted to UTC first (Amsterdam is UTC+1 in November). The response is a 201 with the post under data and "status": "scheduled".

Per-platform text works the same way, but it lives in one content object instead of on each platform entry:

JSON
{ "content": { "default": "Golden hour at the pier.", "linkedin": "Golden hour at the pier. Shot on our team walk this week." }, "accounts": ["linkedin", "instagram"], "media_urls": ["https://example.com/pier.jpg"], "publish_now": true }

Field mapping

Zernio fieldOmniSocials fieldNotes
contentcontentA string, or an object with default and channel keys.
platforms[].platform and platforms[].accountIdaccountsAccount id from GET /accounts, or the channel ID. Alias: channels.
platforms[].customContentcontent.linkedin, content.x, and so onOne key per channel ID.
platforms[].customMediamedia_urls or media_ids as an object keyed by channelAn empty array opts a channel out of media.
platforms[].platformSpecificDataA top-level object named after the channelFor example tiktok.privacy_level, pinterest.board_id, youtube.privacy_status. See Platforms.
tiktokSettingstiktok
facebookSettingsfacebook
titleyoutube.title, tiktok.title, pinterest.titleTitles are set per channel.
mediaItems[].urlmedia_urlsUp to 10 URLs, 100 MB per file.
mediaItems[].typeNoneThe type is read from the file.
scheduledForschedule_atISO 8601 in UTC, for example 2026-11-02T08:00:00Z. Alias: scheduled_at.
timezoneNoneConvert to UTC before you send.
publishNowpublish_now
isDraftLeave out schedule_at and publish_nowA post without either is a draft. draft: true with schedule_at keeps the time on the calendar without publishing.
queuedFromProfile, queueIdNoneChoose the time yourself, or use GET /analytics/best-times.
recyclingNone
dryRunNone
Idempotency-Key headerNoneStore the returned post id and check it before you retry a create.

Media and scheduling

Media. Zernio uploads take three calls: presign, PUT the file, then use the publicUrl. OmniSocials offers three shorter routes:

  • media_urls on the post. Pass public URLs and OmniSocials downloads them. Up to 10 files, each up to 100 MB.
  • POST /media/upload. A multipart upload with a file field, up to 100 MB. Use the returned data.id in media_ids.
  • POST /media/upload-from-url. OmniSocials fetches a public URL of up to 1 GB. Files above about 100 MB come back with status: "processing". Poll GET /media/{id} until it is ready.

For a client that cannot send your API key, POST /media/create-upload-url returns a single-use upload_url that expires after 10 minutes. POST a multipart file to it with no auth header.

The largest file OmniSocials takes is 1 GB, through upload-from-url. Zernio's presign limit is 5 GB. A PDF is turned into one image per page. On LinkedIn it publishes as a document. See Media.

Scheduling. Send schedule_at as an ISO 8601 time in UTC. OmniSocials publishes within one minute of that time. Two behaviors differ from Zernio:

  • A time in the past returns 400 validation_error. Zernio publishes a past scheduledFor at once. To publish now, send publish_now: true.
  • There is no queue. Store your own slots, or read GET /analytics/best-times?platform=instagram for recommended times.

Webhooks

Register a webhook with POST /webhooks and a list of events. The response returns a secret once. Store it.

TerminalTerminal
curl -X POST https://api.omnisocials.com/v1/webhooks \ -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/webhooks/omnisocials", "events": ["post.published", "post.failed"] }'
Zernio eventOmniSocials eventNotes
post.scheduledpost.scheduledSent only for posts scheduled through the API.
post.publishedpost.publishedFires when at least one channel succeeds.
post.partialpost.publisheddata.status is warning. Check each entry in data.targets.
post.failedpost.failedEvery channel failed.
post.platform.published, post.platform.failedNoneRead the per-channel results in data.targets.
post.cancelled, post.recycled, post.platform.deleted, post.external.*None
account.connected, account.disconnectedNonePoll GET /accounts and check needs_reconnect.
message.received, comment.receivedNonePoll GET /inbox/conversations or GET /inbox/next.
Nonepost.approved, post.rejectedApproval workflow outcomes.

Signatures. Zernio sends X-Zernio-Signature, an HMAC-SHA256 of the raw body. OmniSocials sends X-OmniSocials-Signature: t=<timestamp>,v1=<hex>. The HMAC-SHA256 covers the timestamp, a dot, and the raw body, keyed with your webhook secret. Deduplicate on X-OmniSocials-Delivery. See Webhooks for verification code.

Delivery. Answer with a 2xx within 10 seconds. Failed deliveries are retried twice, after 2 seconds and then 5 more. After that the event is dropped. Webhook URLs must use HTTPS.

Platform coverage

As of October 21, 2026, from docs.zernio.com and the OmniSocials platforms page.

PlatformZernioOmniSocials
FacebookYes (facebook)Yes (facebook), Pages
InstagramYes (instagram)Yes (instagram), Business and Creator accounts
ThreadsYes (threads)Yes (threads)
LinkedInYes (linkedin)Yes, profile (linkedin) and company page (linkedin_page)
YouTubeYes (youtube)Shorts only (youtube)
TikTokYes (tiktok)Yes (tiktok)
PinterestYes (pinterest)Yes (pinterest)
BlueskyYes (bluesky)Yes (bluesky)
XYes (twitter)Yes (x)
Google Business ProfileYes (googlebusiness)Yes (google_business)
TelegramYes, as a messaging channel (telegram)Yes (telegram), a channel or group
MastodonNot listedYes (mastodon)
RedditYes (reddit)Coming soon
SnapchatClosed beta (snapchat)Coming soon
WhatsApp, Discord, SlackYes, as messaging channelsNo

What is different

  • Account connection. OmniSocials has no connect-URL endpoint. Accounts are connected in the dashboard. If your product lets end users connect their own accounts through your app, OmniSocials does not support that today.
  • No profiles API. Workspaces are created in the dashboard, and each API key reaches one workspace.
  • One account per channel. A workspace holds one account per channel ID. Two Instagram accounts need two workspaces.
  • Rate limit. OmniSocials allows 100 requests per minute per key. Zernio's documented limit runs from 60 to 1,200 requests per minute, by the number of connected accounts. Limits are per key on OmniSocials, so you can split heavy work across keys. See Rate limits and errors.
  • Fewer post operations. There is no queue, recycling, bulk upload, idempotency key, dry run, edit of a published post, or unpublish through the API.
  • Times are UTC. There is no timezone field, and past times are refused.
  • X links. X posts whose text contains a link use prepaid credits. Posts without a link do not. See X.
  • Fewer webhook events. There are no account, inbox, or per-platform events, no test endpoint, and no full delivery log.
  • Error shape. Zernio errors are flat, with error, type, and code at the top level. OmniSocials errors nest under error, with code, message, and optional details. Branch on error.code.
  • No messaging or ads channels. WhatsApp, Discord, Slack, SMS, and ads APIs are not part of OmniSocials. YouTube is Shorts only, and Reddit is not live yet.
  • No test mode. Neither API documents a general sandbox. On OmniSocials, use drafts or a workspace with test accounts.
  • What OmniSocials adds. Approval workflows with post.approved and post.rejected events, alt text per image, PDF to carousel and LinkedIn documents, automatic first comments, hashtag sets, Instagram comment-to-DM automations, and Mastodon.

Migration steps

  1. Create an OmniSocials account. Add one workspace for each Zernio profile.
  2. Connect each social account in the dashboard, under Settings → Organisation → Workspaces.
  3. Create one API key per workspace under Settings → API. Start with posts:write, posts:read, media:write, accounts:read, and webhooks:manage.
  4. Call GET /accounts with each key. Map every Zernio accountId to an OmniSocials channel ID or account id.
  5. Change the base URL and the key. Rename the post fields with the field mapping table. Convert scheduledFor and timezone to a UTC schedule_at.
  6. Replace the presign upload with media_urls, or with POST /media/upload and media_ids.
  7. Register your webhooks with POST /webhooks. Swap the signature check and map the events.
  8. Recreate the posts that are still scheduled in Zernio with schedule_at. Then delete them in Zernio with DELETE /v1/posts/{postId}, so nothing publishes twice.
  9. Test with drafts first, since there is no test mode. Then publish one post with publish_now: true and check published_urls on GET /posts/{id}.
  10. Handle 429 responses. Wait for the Retry-After header, then retry.

Posts without schedule_at or publish_now are drafts and never publish on their own. Use them to check your field mapping against real accounts.

Last modified on October 21, 2026
From AyrshareFrom Upload-Post
On this page
  • Concepts
  • Authentication
  • Endpoint mapping
  • Create a post
  • Field mapping
  • Media and scheduling
  • Webhooks
  • Platform coverage
  • What is different
  • Migration steps
JSON