Skip to main content
A workspace is the top-level tenant in Postbreeze. Each workspace owns:
  • Its own set of connected social accounts (one Instagram account, one TikTok handle, etc.)
  • Its own posts, drafts, and publish history
  • Its own media library and folders
  • Its own team (owner, admins, editors, client reviewers)
Workspaces never share data. Every API call resolves to exactly one workspace; the auth layer rejects cross-workspace traffic.

API key access models

When you mint a key in the dashboard you pick between two access modes:
  • Full access (default) — the key can act on every workspace its owner is a member of. A new workspace the owner joins later is automatically reachable.
  • Scoped — the key is restricted to an explicit allow-list of workspaces. Toggle “Full access” off in the create-key sheet and pick the workspaces from the list. Any call targeting a workspace outside the allow-list returns 403.
Both modes work the same way at the call site. The SDK’s resource calls (e.g. postbreeze.posts.get, postbreeze.media.move) derive the workspace from the referenced row server-side — you never have to look it up client-side.

Workspace inference

How the workspace gets resolved on each call:
Call shapeSource of workspaceId
postbreeze.posts.get({ postId })Derived from Post.workspaceId
postbreeze.posts.create({ platforms: [{ accountId }] })Derived from the first account
postbreeze.socialAccounts.disconnect({ accountId })Derived from SocialAccount.workspaceId
postbreeze.media.get({ mediaId })Derived from the media’s owner workspace
postbreeze.posts.list() / postbreeze.posts.list({ workspaceId })Optional — omit to fan out, pass to scope
postbreeze.socialAccounts.list() / .list({ workspaceId })Optional — same
postbreeze.comments.list() / .list({ workspaceId })Optional — same
postbreeze.media.list({ workspaceId })Required (account-global)
postbreeze.media.presign({ workspaceId, … }) / .ingestFromUrl({ … })Required
Fan-out lists (no workspaceId) query every workspace the key can act on and return a single merged list, newest-first. Each row carries its own workspaceId so callers can group or filter client-side. Resource calls don’t need a workspaceId either: the server reads it off the referenced row. The SDK accepts an optional workspaceId on those for client-side defense-in-depth (“assert that this post lives in workspace X”) but it’s purely informational.

Multi-brand example

A single full-access key drives every workspace. The flat list returns every account across every workspace the key can reach:
import Postbreeze from "@postbreeze/node";

const postbreeze = new Postbreeze({ apiKey: process.env.POSTBREEZE_API_KEY! });

// One call — every connected account everywhere the key can act on.
const accounts = await postbreeze.socialAccounts.list();

// Group client-side by workspace if you want.
const byWorkspace = Map.groupBy(accounts, (a) => a.workspaceId);

await postbreeze.posts.create({
  content: "Acme launch",
  platforms: [{ accountId: byWorkspace.get("wsp_acme")![0].id }],
});
await postbreeze.posts.create({
  content: "Globex launch",
  platforms: [{ accountId: byWorkspace.get("wsp_globex")![0].id }],
});

// Or pass `workspaceId` to scope the list to one workspace explicitly:
const acmeOnly = await postbreeze.socialAccounts.list({ workspaceId: "wsp_acme" });
For tighter security in CI / production, mint a scoped key that only sees the workspaces it needs. Leakage of a scoped key is bounded to its allow-list; a leaked full-access key compromises every workspace the owner is in.

Owner vs. member workspaces

If you were invited to a workspace, the owner controls its billing plan and feature access. You can still create posts, schedule, and use the API — but plan-gated features (advanced analytics, white-label, the posts-per-month cap) are evaluated against the owner’s subscription. The dashboard surfaces a “Joined” badge on workspaces you don’t own.