npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@post2all/sdk

v0.3.13

Published

TypeScript SDK for post2all public API

Readme

@post2all/sdk

Type-safe TypeScript client for the post2all REST API.

Full guides and examples: post2all TypeScript SDK documentation.

Install

pnpm add @post2all/sdk

Create a post

import { Post2allClient } from "@post2all/sdk";

const client = new Post2allClient({
  apiKey: process.env.POST2ALL_API_KEY!,
});

const { post } = await client.createPost({
  content: "New release shipping today 🚀",
  targets: [
    {
      platform: "discord",
      accountId: "acc_discord_123",
      settings: {
        channelId: "1234567890",
        autoCrosspost: true,
      },
    },
    {
      platform: "threads",
      accountId: "acc_threads_123",
      settings: {
        caption: "A shorter Threads version",
        topicTag: "buildinpublic",
      },
    },
  ],
  delivery: { mode: "now" },
});

For production creates, pass a stable idempotency key so a timeout can be retried without creating a second post:

await client.createPost(input, { idempotencyKey: "customer-job-123" });

targets is a discriminated union. Once platform is selected, TypeScript and Zod only accept settings supported by that platform.

Platform IDs, settings fields, fixed enums, limits, static account-connection metadata, the publishedDeletion.available capability, and sanitized analytics schema metadata are generated from the private monorepo's public contract. Do not edit the generated contract file in this repository; the release synchronization job regenerates it.

Connect social accounts from your SaaS

post2all can be the social-account backend for your own product. Your server keeps the post2all API key; the end user's browser only visits the social provider authorization page and your own callback URL.

const started = await client.connectAccount("instagram", {
  redirectUrl: "https://app.example.com/settings/social/complete",
});

// Send the user's browser to started.authorizationUrl.
console.log(started.connectionId, started.authorizationUrl);

The user does not need a post2all account, post2all login, or post2all browser session. After the provider callback, post2all returns the browser to your redirectUrl. Verify the authoritative result from your backend:

const connection = await client.getAccountConnection(started.connectionId);

if (connection.status === "connected") {
  const account = await client.getAccount(connection.accountIds[0]);
  console.log(account.account);
}

Never expose your post2all API key in browser code. The browser redirect is a UX handoff; use getAccountConnection() server-side before trusting the result.

Reconnect the exact provider identity later if credentials expire or scopes change:

const reconnect = await client.reconnectAccount("acc_instagram_123", {
  redirectUrl: "https://app.example.com/settings/social/complete",
});

post2all rejects a reconnect if the newly authorized provider identity does not match the existing account.

Profiles for clients and SaaS tenants

Business, Agency, and credit-based workspaces can keep clients, brands, or customers in optional Profiles. Business supports up to 2 profiles, Agency up to 10, and credit workspaces up to 10,000. Profiles are organization/filtering contexts, not separate API credentials or tenant authorization boundaries.

const { profile } = await client.createProfile({
  name: "Acme",
  externalId: "customer_123",
});
const acme = client.forProfile(profile.id);
const accounts = await acme.listAccounts();

The profile client sends x-profile-id automatically. It is an organization/filtering context, not a separate security credential: profile-aware lists are filtered, new connections are assigned to that profile, and new posts are organized under it. Profile management remains organization-scoped.

Move an account with setAccountProfile(accountId, profileId) or pass null to clear its profile assignment. Each account belongs to at most one profile at a time. Moving an account does not move historical posts or break existing drafts, schedules, retries, analytics, or post targets. Deleting a profile preserves its accounts and posts as profile-less resources.

The recommended SaaS pattern is to keep one root client for organization-level profile management, then use one scoped client per customer:

const root = new Post2allClient({
  apiKey: process.env.POST2ALL_API_KEY!,
});

const { profile } = await root.createProfile({
  name: "Acme",
  externalId: "customer_123",
});

const acme = root.forProfile(profile.id);

const started = await acme.connectAccount("instagram", {
  redirectUrl: "https://app.example.com/social/complete",
});

const { accounts } = await acme.listAccounts();
const posts = await acme.listPosts();

Omitting profile context means the complete workspace. externalId is optional and is intended for mapping a post2all profile to your own customer or tenant ID. Keep the workspace API key on your trusted backend; the workspace remains the authorization boundary.

Telegram uses the same API with a bot-code response, and Wircle accepts credentials directly:

const telegram = await client.connectAccount("telegram");

const wircle = await client.connectAccount("wircle", {
  credentials: {
    apiKey: process.env.WIRCLE_API_KEY!,
    profileHandle: "@maker",
  },
});

Delivery modes

await client.createPost({
  content: "Work in progress",
  delivery: { mode: "draft" },
});

await client.createPost({
  content: "Scheduled announcement",
  targets: [
    {
      platform: "linkedin",
      accountId: "acc_linkedin_123",
      settings: {},
    },
  ],
  delivery: {
    mode: "scheduled",
    scheduledAt: "2026-07-20T09:00:00+05:30",
  },
});

Drafts may omit targets and incomplete publishing settings. Immediate and scheduled delivery require valid targets, media, and all required platform settings.

Account publishing options

Load the selected-account schema once before composing:

const schema = await client.getPublishingSchema([accountId]);
console.log(schema.accounts); // Fixed choices and account-specific limits, including X tiers

Use account publishing options before rendering or submitting dynamic settings such as Discord channels or TikTok privacy choices:

const options = await client.getPublishingOptions(["acc_discord_123"]);
console.log(options.accounts[0]?.destinations);
console.log(options.accounts[0]?.boards);

capability is the authoritative, account-specific constraint set. Read it before composing or validating a post instead of hard-coding platform limits. For example, an X account's capability.text.maxLength reflects whether that account is Free, Basic, Premium, or Premium+.

Do not send a fixed post type. Composition is inferred from attached media. Mixed image/video is allowed only when platform media.allowMixedMedia is true. When capability.media.altText is present, each attached media item may include its own altText; use the returned media types and maximum length. X intentionally does not expose media alt text.

Credit billing and publishing capacity

Organizations manually onboarded onto credit billing can inspect their prepaid access and selected-account rolling publishing capacity:

const billing = await client.getBilling();
const limits = await client.getPublishingLimits(["acc_instagram_123"]);

getBilling() is organization-wide. getPublishingLimits() preserves forProfile() scope and returns an advisory snapshot; publish-time enforcement remains authoritative and automatically defers targets that have exhausted capacity.

Credit-organization webhooks

Credit workspace owners and admins manage webhook endpoints in the post2all dashboard. The SDK verifies incoming deliveries:

import { verifyWebhookSignature } from "@post2all/sdk";

const valid = verifyWebhookSignature({
  rawBody,
  secret: process.env.POST2ALL_WEBHOOK_SECRET!,
  eventId: headers.get("x-post2all-event-id") ?? "",
  timestamp: headers.get("x-post2all-timestamp") ?? "",
  signature: headers.get("x-post2all-signature") ?? "",
});

The dashboard shows the signing secret only once after endpoint creation. Deliveries are at least once; store the secret server-side and deduplicate on the stable event ID.

Use onResponse to collect request IDs and request-quota metadata without changing any method's return shape:

const client = new Post2allClient({
  apiKey: process.env.POST2ALL_API_KEY!,
  onResponse: ({ requestId, rateLimit }) => console.log(requestId, rateLimit),
});

Analytics

Read normalized account metrics, time series, provider metric definitions, and previous-period comparisons:

const analytics = await client.getAccountAnalytics("acc_instagram_123", {
  startDate: "2026-08-01",
  endDate: "2026-08-31",
});

console.log(analytics.metrics);
console.log(analytics.comparisons);

The default range is the latest 30 inclusive UTC days and the maximum range is 90 days. Pass refresh: true only when you explicitly need fresh provider data instead of the normal analytics cache.

Compare provider-wide post performance:

const posts = await client.listAccountAnalyticsPosts("acc_instagram_123", {
  sortBy: "views",
  sortDirection: "desc",
  limit: 20,
});

Supported platforms can return posts that were published outside post2all. Use origin to distinguish matched post2all posts from external provider posts; unmatched external posts intentionally omit postId and postAccountId.

Read analytics for every target of a post2all post:

const result = await client.getPostAnalytics("post_abc");

Inspect each target's analyticsStatus before interpreting an empty metrics array. Unavailable provider data, reconnect requirements, unpublished targets, and provider errors are not zero engagement.

Retry failed post targets

Retry a failed or partially failed post without republishing successful targets:

await client.retryPost("post_abc");

Schedule only the failed-target retry for later when needed:

await client.retryPost("post_abc", {
  scheduledAt: "2026-09-21T14:00:00+05:30",
});

For example, if Instagram and YouTube succeeded but TikTok failed, retryPost() publishes only the failed TikTok target.

Media

const { media } = await client.uploadMedia("./video.mp4");

await client.createPost({
  content: "Product walkthrough",
  media: [
    {
      id: media.id,
      altText: "Product walkthrough showing the publishing workflow",
    },
  ],
  targets: [
    {
      platform: "youtube",
      accountId: "acc_youtube_123",
      settings: {
        title: "Product walkthrough",
        privacyStatus: "unlisted",
      },
    },
  ],
  delivery: { mode: "now" },
});

If your application already hosts stable public HTTPS media, you can skip uploading and attach the URL directly:

await client.createPost({
  content: "Launch",
  media: [
    {
      url: "https://cdn.example.com/launch.mp4",
      altText: "Launch walkthrough",
    },
  ],
  targets,
  delivery: { mode: "scheduled", scheduledAt: "2026-10-20T09:00:00Z" },
});

Direct URL media stays caller-managed and must remain publicly reachable until every scheduled publish/retry completes. If the URL is temporary or signed, securely ingest a managed copy first:

const { media } = await client.uploadMediaFromUrl(
  "https://temporary.example.com/launch.mp4",
  "launch.mp4",
);

mediaIds remains accepted for compatibility when you only need to attach managed media IDs. New integrations should prefer media, which accepts either { id } or { url } entries and optional per-media alt text. Do not send both media and mediaIds in one request.

API

  • listProfiles(input?)
  • getProfile(profileId)
  • createProfile(input)
  • updateProfile(profileId, input)
  • deleteProfile(profileId)
  • forProfile(profileId)
  • listAccounts()
  • listAccountPlatforms()
  • connectAccount(platform, input?)
  • getAccountConnection(connectionId)
  • getAccount(accountId)
  • reconnectAccount(accountId, input?)
  • disconnectAccount(accountId)
  • setAccountProfile(accountId, profileId)
  • getAccountAnalytics(accountId, input?)
  • listAccountAnalyticsPosts(accountId, input?)
  • getPublishingSchema(accountIds)
  • getPublishingOptions(accountIds)
  • getAccountPublishingOptions(accountId) (compatibility)
  • getBilling() (credit-based organizations only)
  • getPublishingLimits(accountIds) (credit-based organizations only)
  • uploadMedia(path)
  • uploadMediaFromUrl(url, filename?) — securely imports a public HTTPS URL into post2all-managed storage
  • createMediaUpload(input)
  • confirmMediaUpload(mediaId)
  • createPost(input, options?)
  • listPosts(input?)
  • getPost(postId)
  • getPostAnalytics(postId, input?)
  • updatePost(postId, input)
  • retryPost(postId, input?) — retries only failed targets and can optionally schedule that retry
  • deletePublishedPost(postId, postAccountId) — removes one published social post on a public deletion platform while keeping the post2all post
  • deletePost(postId) — removes the post from post2all; already-published social content remains live
  • cancelPost(postId)

Use getPost(postId) first and check target.deletion.available. When it is false, target.deletion.reason explains why. This runtime state already includes platform rollout, account state, provider IDs, and time limits. Private rollout platforms are never unlocked through API keys.

Errors

All API and response-validation failures throw Post2allApiError. Validation error responses may include field-level issues such as targets.0.settings.channelId.

Changelog

See the repository changelog for versioned SDK and CLI changes, deprecations, and compatibility notes.