import { OmniSocials } from "@omnisocials/sdk";const client = new OmniSocials({ apiKey: "omsk_live_..." });// or set OMNISOCIALS_API_KEY and call new OmniSocials()
const { data: post } = await client.posts.create({ content: "New drop this Friday", channels: ["instagram", "facebook", "linkedin"], scheduled_at: "2026-08-01T09:00:00Z", media_urls: ["https://example.com/teaser.jpg"],});console.log(post.id, post.status);
Any media_urls (or media_ids) entry can also be an object carrying per-media
alt text (max 1500 chars), delivered to Mastodon, Bluesky, X (photos),
Pinterest, Instagram (images), and LinkedIn (images):
TypeScript
media_urls: [{ url: "https://example.com/teaser.jpg", alt: "Red sneaker on a white background" }],
Publish immediately:
TypeScript
await client.posts.createAndPublish({ content: "Going live right now", channels: ["x", "bluesky"],});
// From a pathconst res = await client.media.upload({ file: "./photos/product.jpg", name: "product-hero" });// Or from bytes / a Blobimport { readFile } from "node:fs/promises";const bytes = await readFile("./photos/product.jpg");await client.media.upload({ file: bytes, filename: "product.jpg" });
Read and answer DMs, comments and mentions from your connected accounts. The API key
needs the opt-in inbox:read / inbox:write scopes. The lists use cursor pagination
(cursor in, pagination.next_cursor out).
inbox.next() hands out the next conversation that still needs an answer, with the
thread and the post it belongs to. On a comment thread, pass message_id so the reply
goes under the comment you answer and not under the newest comment on the post.
TypeScript
let { data: item, remaining } = await client.inbox.next();while (item) { if (!item.reply_window.open) { // An Instagram/Facebook DM past Meta's 24-hour window: answer it in the app await client.inbox.markRead(item.message.conversation_id); ({ data: item, remaining } = await client.inbox.next()); continue; } const reply = await client.inbox.reply(item.message.conversation_id, { text: "Thanks! DM sent.", message_id: item.message.id, include_next: true, }); item = reply.next ?? null; remaining = reply.remaining ?? 0;}
See the Social Inbox guide for the platforms, the reply rules and the error
codes.
Pinterest product tags
Tag products on a Pin so people can shop the items in the image. List the product Pins
of the connected Pinterest account, then pass up to 24 of their Pin ids as
product_tags in the pinterest options of the post. Only product Pins of your own
account can be tagged; products of other merchants cannot.
The list answers with HTTP 200 in both cases: products on success, or an error
object (pinterest_not_connected, pinterest_catalog_access_required,
platform_error) when it could not be read. It reads the Pinterest catalog when the
connection has catalog access (given one time in the OmniSocials composer: Pinterest
options, Add products, Connect catalog), else the account's own Pins. The tags are added
right after the Pin is published. A product that Pinterest refuses never fails the post:
the post then carries pinterest.product_tags_result with the tagged and the skipped
Pin ids. client.pinterest.validateProduct(id) checks one Pin id or Pin link before you
post. On posts.update() the pinterest object replaces the stored one, so leave
product_tags out to remove the tags.
Verify webhooks
TypeScript
import express from "express";import { verifyWebhookSignature, WebhookVerificationError } from "@omnisocials/sdk";const app = express();app.post( "/omnisocials/webhook", express.raw({ type: "application/json" }), // keep the raw body! (req, res) => { try { const event = verifyWebhookSignature({ payload: req.body, // Buffer of the raw body signature: req.get("X-OmniSocials-Signature") ?? "", secret: process.env.OMNISOCIALS_WEBHOOK_SECRET!, tolerance: 300, // seconds (default) }); switch (event.type) { case "post.published": console.log("Published:", event.data.post_id, event.data.targets); break; case "post.failed": console.error("Failed:", event.data.post_id); break; case "post.rejected": console.warn("Rejected:", event.data.post_id, event.data.approval?.reason); break; } res.sendStatus(200); } catch (err) { if (err instanceof WebhookVerificationError) { return res.sendStatus(400); } throw err; } });
Events: post.scheduled, post.published, post.failed, post.approved and post.rejected.
The two approval events carry data.approval with status, decided_by (the approver's
user id) and reason (null on post.approved). To read the full review of a post (steps,
approvers, the rejection reason and the comment thread), call client.posts.getApproval(id).
Errors
The base class is OmniSocialsError. API errors are APIError subclasses carrying
status, code, message, and body: ValidationError (400/422),
AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404),
RateLimitError (429, with .retryAfter), ServerError (5xx), plus
APIConnectionError and WebhookVerificationError.