Multi-brand social media publishing: architecture for managing 50+ accounts

How to structure account hierarchies, permissions, and publishing workflows when you're managing many brands. Cover profile groups, per-brand credentials, and isolated failure domains.

Multi-brand social media publishing: architecture for managing 50+ accounts

When “connect your accounts” stops being simple

One brand, four platforms, four connected accounts. Straightforward.

Ten brands, four platforms each, forty connected accounts. Manageable, if you squint.

Fifty brands, each with Instagram, TikTok, Facebook, LinkedIn, and Threads — two hundred and fifty connected accounts, each with its own OAuth tokens, its own rate limits, its own expiration schedule, its own failure modes.

This is the reality for agencies, franchise networks, multi-brand retailers, and SaaS platforms that offer publishing to their customers. The problem is not publishing a post. The problem is organizing two hundred and fifty accounts so that a post intended for Brand A’s Instagram never accidentally reaches Brand B’s audience, so that an expired token on one brand does not block publishing for another, and so that the person managing Brand C cannot see or affect anything belonging to Brand D.

Most publishing tools collapse at this scale. Dashboards that were designed for a single brand with a handful of accounts become unusable when the account list runs off the screen. The account picker becomes a minefield. The permissions model — if one exists — is an afterthought.

Multi-brand publishing is an architecture problem, not a feature problem. It requires isolation, hierarchy, scoped access, and failure containment from the start.

The hierarchy: brands, profiles, and placements

Before writing any code, you need a mental model for how accounts are organized.

Organization (your agency / platform)
├── Brand A
│ ├── Instagram Business Account
│ ├── Facebook (→ Acme Page)
│ ├── LinkedIn (→ Acme Company Page)
│ ├── TikTok Account
│ └── Threads Account
├── Brand B
│ ├── Instagram Business Account
│ ├── Facebook (→ US Page, EU Page) ← placements
│ ├── LinkedIn (→ Company Page)
│ └── X Account
└── Brand C
├── Instagram Business Account
└── TikTok Account

Three levels, plus placements:

  1. Organization — the top level. Your agency, your SaaS platform, your franchise network. Owns the billing relationship and the master API key.
  2. Brand — a logical grouping of social accounts that belong to the same entity. Maps to a client, a franchise location, a product line, a subsidiary. Each brand has at most one profile per platform.
  3. Profile — a single connected social account per platform within a brand. One Instagram profile, one Facebook profile, one LinkedIn profile. Each profile holds its own OAuth credentials and its own platform-specific state.

Some profiles have multiple publishing destinations within the same platform. A Facebook connection gives access to multiple Pages. A LinkedIn connection gives access to a personal profile and company pages. These are placements — specific destinations within a profile. Brand B’s single Facebook profile exposes two placements (US Page and EU Page), not two separate Facebook profiles.

This hierarchy is not optional overhead. It is the foundation that makes everything else work: scoped permissions, isolated failures, targeted publishing, and clean reporting.

Profile groups: the isolation boundary

In Postproxy, the brand maps to a profile group. A profile group is a container that holds a set of connected social profiles. Every profile belongs to exactly one group. Every post targets profiles within a group. Groups are the isolation boundary.

Create a profile group for each brand:

Terminal window
curl -X POST "https://api.postproxy.dev/api/profile_groups" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_group": {
"name": "Acme Corp"
}
}'
{
"id": "grp_abc123",
"name": "Acme Corp",
"profiles_count": 0
}

Then connect social accounts into that group. When a brand manager authenticates with Instagram, the resulting profile lives inside the brand’s group:

Terminal window
curl -X POST "https://api.postproxy.dev/api/profile_groups/grp_abc123/initialize_connection" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "instagram",
"redirect_url": "https://yourapp.com/brands/acme/callback"
}'

The brand manager completes the OAuth flow. A new profile appears in the grp_abc123 group. It is invisible to every other group. Publishing to grp_abc123 can only reach profiles inside that group. A misconfigured API call cannot accidentally publish to another brand’s Instagram.

This isolation is not just a safety measure for avoiding embarrassing mistakes — though it prevents those too. It is an architectural constraint that makes the system predictable. When you publish to a profile group, the blast radius is contained. When a token expires in one group, no other group is affected. When you query posts for a brand, you see only that brand’s content.

Scoped API keys: per-brand access control

A master API key can access everything — all groups, all profiles, all posts. That is appropriate for the organization’s internal tooling. It is not appropriate for a brand manager who should only see their own accounts, or for a SaaS customer who should only access their own profiles.

Scoped API keys solve this. A scoped key is bound to a specific profile group. It can list profiles, create posts, check status, and manage content — but only within its group. It cannot see or affect other groups.

Terminal window
curl -X POST "https://api.postproxy.dev/api/api_keys" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"api_key": {
"name": "Acme Corp - Production",
"profile_group_id": "grp_abc123"
}
}'
{
"id": "key_m9x2k",
"name": "Acme Corp - Production",
"key": "pp_live_acme_...",
"profile_group_id": "grp_abc123"
}

Hand this key to the brand manager, embed it in the brand’s automation, or use it in the brand-specific section of your SaaS product. The key cannot see Brand B’s profiles. It cannot publish to Brand C’s accounts. The isolation is enforced at the API level, not by hoping that the caller passes the right group ID.

This also simplifies auditing. Every API call made with a scoped key is traceable to a specific brand. If something unexpected appears on a brand’s Instagram, you know which key was used and when.

Key management at scale

With fifty brands, you have at least fifty scoped keys. Some brands may need multiple keys — one for their automation pipeline, one for their team dashboard, one for a third-party tool.

Patterns that work:

  • One production key per brand. The default. Each brand gets a single key used across all their integrations.
  • Separate keys per integration. A brand has one key for their CMS webhook handler, one for their n8n workflow, and one for the manual dashboard. If one key is compromised, revoke it without affecting the others.
  • Rotating keys. For brands with strict security requirements, rotate keys periodically. Create a new key, update the integration, revoke the old key. The profile group is unaffected — only the key changes.

Store keys in a secrets manager (AWS Secrets Manager, Vault, environment variables in your deployment system), not in code or configuration files. Treat them with the same care as database credentials.

Placements: sub-accounts within platforms

Some profiles have multiple publishing destinations. A Facebook connection gives access to multiple Pages. A LinkedIn connection gives access to a personal profile and company pages.

The Placements endpoint returns available destinations for a profile:

Terminal window
curl "https://api.postproxy.dev/api/profiles/prof_xyz/placements" \
-H "Authorization: Bearer YOUR_API_KEY"
[
{
"id": "plc_001",
"name": "Acme US",
"type": "facebook_page",
"platform_id": "1234567890"
},
{
"id": "plc_002",
"name": "Acme EU",
"type": "facebook_page",
"platform_id": "0987654321"
}
]

A brand with separate Facebook Pages for different regions — US, EU, APAC — connects one Facebook account and gets multiple placements. Each placement is a distinct publishing target. You specify the placement when creating a post, ensuring content reaches the right Page.

For multi-brand setups where a single Meta Business account manages Pages for several brands, placements prevent cross-brand publishing. Brand A’s Page and Brand B’s Page might be accessible from the same Facebook login, but they live in different profile groups with different placements. The hierarchy enforces the separation that the platform’s own account model does not.

Isolated failure domains

When you manage two hundred and fifty accounts, something is always broken. A token expired. A platform revoked access. A rate limit was hit. An image was rejected. A video is still processing.

The question is not whether failures happen. It is whether a failure in one brand affects another.

Token expiration does not cascade

Each profile holds its own OAuth tokens. When Brand A’s Instagram token expires, only Brand A’s Instagram publishing is affected. Brand B’s Instagram — different profile, different tokens, different group — continues to work.

The profile object includes a status field:

{
"id": "prof_xyz",
"name": "acme_instagram",
"platform": "instagram",
"status": "active",
"expires_at": "2026-04-15T00:00:00Z"
}

Monitor profile status across all groups. When a profile transitions to expired, notify the brand manager to reconnect. The reconnection flow is the same Initialize Connection call — the new token replaces the old one within the same profile and group.

At scale, token expiration monitoring becomes an operational task. Query all profiles periodically and surface any that are expired or approaching expiration:

async function checkExpiringProfiles() {
const response = await fetch("https://api.postproxy.dev/api/profiles", {
headers: { "Authorization": `Bearer ${MASTER_API_KEY}` },
});
const profiles = await response.json();
const now = new Date();
const warningThreshold = 7 * 24 * 60 * 60 * 1000; // 7 days
const expiring = profiles.filter((p) => {
if (p.status === "expired") return true;
if (!p.expires_at) return false;
return new Date(p.expires_at) - now < warningThreshold;
});
for (const profile of expiring) {
await notifyBrandManager(profile);
}
}

Run this daily. Brand managers get proactive notifications instead of discovering the issue when a post fails to publish.

Rate limits are per-profile

Each social platform enforces rate limits on a per-account basis. Brand A’s Instagram rate limit is independent of Brand B’s Instagram rate limit. One brand hitting its posting limit does not affect any other brand.

Postproxy tracks rate limits per profile. If Brand A has exhausted their Instagram posting quota for the day, Brand A’s posts queue or fail with a clear error. Brand B’s posts continue unaffected.

This is one of the advantages of true isolation. In systems where multiple brands share a single platform connection or a single app-level rate limit, one busy brand can starve the others. Profile groups prevent this by keeping every brand’s platform relationships separate.

Partial success is per-brand

When a multi-platform publish call partially succeeds — Instagram published but TikTok failed — the partial success is scoped to that brand’s post. Other brands’ posts are unrelated and unaffected.

The per-platform outcomes in the response tell you exactly what happened:

{
"id": "post_8xk2m",
"profile_group_id": "grp_abc123",
"status": "processed",
"platforms": [
{ "platform": "instagram", "status": "published" },
{ "platform": "tiktok", "status": "failed", "error": "privacy_level_not_available" },
{ "platform": "facebook", "status": "published" }
]
}

The failure on TikTok is Brand A’s problem to resolve. No other brand sees it, is affected by it, or needs to know about it.

Structuring publishing workflows across brands

With fifty brands, you are not hand-crafting individual workflows. You need patterns that scale.

The shared pipeline with per-brand configuration

Build one publishing pipeline. Parameterize it per brand. Each brand gets a configuration object that defines its platforms, its tone, its posting cadence, and its profile group ID.

const brandConfigs = {
acme: {
profileGroupId: "grp_abc123",
profiles: ["instagram", "tiktok", "facebook"],
apiKey: process.env.ACME_API_KEY,
postPrefix: "",
hashtagStrategy: "none",
},
globex: {
profileGroupId: "grp_def456",
profiles: ["instagram", "linkedin", "threads"],
apiKey: process.env.GLOBEX_API_KEY,
postPrefix: "",
hashtagStrategy: "category-based",
},
initech: {
profileGroupId: "grp_ghi789",
profiles: ["instagram", "facebook"],
apiKey: process.env.INITECH_API_KEY,
postPrefix: "",
hashtagStrategy: "none",
},
};
async function publishForBrand(brandKey, content) {
const config = brandConfigs[brandKey];
const response = await fetch("https://api.postproxy.dev/api/posts", {
method: "POST",
headers: {
"Authorization": `Bearer ${config.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
post: { body: content.body },
profiles: config.profiles,
media: content.media || [],
}),
});
return response.json();
}

The pipeline logic — extracting content from a CMS, formatting posts, scheduling — is written once. The brand configuration determines where it publishes and with what credentials. Adding a new brand means adding a configuration object and a scoped API key, not building a new pipeline.

Event-driven publishing per brand

For brands that publish in response to events — product launches, blog posts, CMS updates — each brand’s event source maps to the shared pipeline through the brand configuration.

Brand A: Shopify webhook ──▶ Handler ──▶ publishForBrand("acme", ...)
Brand B: WordPress webhook ──▶ Handler ──▶ publishForBrand("globex", ...)
Brand C: Contentful webhook ──▶ Handler ──▶ publishForBrand("initech", ...)

Different sources, different content, different platforms — same publishing infrastructure. The handler extracts the relevant fields from each source, the configuration selects the target platforms, and the scoped API key ensures the right profile group is used.

Scheduled publishing across brands

Some brands publish on a schedule — daily product highlights, weekly roundups, campaign sequences. At scale, scheduling means hundreds of posts across dozens of brands, all timed to go out at different hours.

Use Postproxy’s scheduled_at parameter to handle timing server-side:

async function scheduleWeeklyPosts(brandKey, posts) {
const config = brandConfigs[brandKey];
for (const post of posts) {
await fetch("https://api.postproxy.dev/api/posts", {
method: "POST",
headers: {
"Authorization": `Bearer ${config.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
post: { body: post.body },
profiles: config.profiles,
media: post.media || [],
scheduled_at: post.scheduledAt,
}),
});
}
}

No cron jobs to manage per brand. No timers that need to stay running. The posts are created with future timestamps and published by Postproxy at the specified time. This scales to any number of brands without adding infrastructure.

Onboarding new brands

Adding a new brand to the system should take minutes, not days. Here is the operational checklist:

1. Create the profile group.

Terminal window
curl -X POST "https://api.postproxy.dev/api/profile_groups" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_group": {
"name": "New Brand Inc"
}
}'

2. Create a scoped API key.

Terminal window
curl -X POST "https://api.postproxy.dev/api/api_keys" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"api_key": {
"name": "New Brand Inc - Production",
"profile_group_id": "grp_new123"
}
}'

3. Connect social accounts. Send the brand manager the connection flow links for each platform. They authenticate with their own credentials. Profiles appear in their group.

4. Add the brand configuration. Add an entry to your brand config with the profile group ID, scoped API key, target platforms, and any brand-specific settings.

5. Wire up event sources. Point the brand’s e-commerce webhooks, CMS webhooks, or RSS feed at the shared handler with the brand identifier.

6. Test with a draft post. Publish a test post as a draft, verify it targets the right accounts, then move to auto-publish.

That is the entire onboarding flow. The pipeline already exists. The publishing infrastructure already works. The new brand plugs into both through configuration, not code.

Offboarding and account cleanup

Brands leave. Contracts end. The offboarding should be as clean as the onboarding.

Revoke the scoped API key. This immediately prevents any further publishing for that brand. Any automation using the key starts failing with 401 errors. No posts can be created.

Disconnect profiles. Profiles within the group can be disconnected, which revokes the stored OAuth tokens. The brand’s social accounts are no longer accessible through your system.

Archive or delete the profile group. The group and its post history can be archived for record-keeping or deleted entirely.

Clean offboarding prevents stale credentials from sitting in your system. A brand that left six months ago should not have active OAuth tokens in your infrastructure.

Monitoring at scale

With fifty brands and two hundred and fifty profiles, you need monitoring that surfaces problems before they reach the brand manager.

Profile health dashboard

Query all profiles across all groups and aggregate by status:

async function getProfileHealth() {
const response = await fetch("https://api.postproxy.dev/api/profiles", {
headers: { "Authorization": `Bearer ${MASTER_API_KEY}` },
});
const profiles = await response.json();
const summary = {
total: profiles.length,
active: profiles.filter((p) => p.status === "active").length,
expired: profiles.filter((p) => p.status === "expired").length,
expiringSoon: profiles.filter((p) => {
if (!p.expires_at) return false;
const daysLeft = (new Date(p.expires_at) - new Date()) / (1000 * 60 * 60 * 24);
return daysLeft > 0 && daysLeft < 7;
}).length,
};
return summary;
}

Run this on a schedule. Alert when expired profiles exceed a threshold. Alert when a brand has zero active profiles (meaning all their accounts have disconnected or expired).

Publishing success rates per brand

Track per-platform outcomes over time. A brand whose Instagram publish success rate drops from 100% to 60% has a problem — maybe a token issue, maybe a content format issue, maybe a rate limit.

Use webhooks to collect outcomes in real time. Each platform_post.published and platform_post.failed event carries the post ID, platform, and profile group. Aggregate these into per-brand, per-platform success rates.

A simple alert: if any brand’s success rate on any platform drops below 90% over the last 24 hours, investigate. The per-platform error details in the post status response will tell you exactly what is failing.

Anomaly detection

At fifty brands, manual monitoring does not scale. Automate the detection:

  • Token expiration warnings — notify brand managers 7 days before expiration
  • Publishing failures — alert on any brand with more than 3 consecutive failures on a single platform
  • Silent brands — flag brands that have not published in longer than their expected cadence
  • Rate limit proximity — warn when a brand is approaching its daily posting limit on any platform

These do not require sophisticated infrastructure. A daily cron job that queries profiles and recent post status, runs the checks, and sends notifications covers most cases.

Architecture patterns for different use cases

Agency managing client brands

The agency holds the master API key. Each client gets a profile group and a scoped API key. The agency’s internal tools use the master key for cross-brand reporting and monitoring. Client-facing dashboards use scoped keys for isolation.

Some agencies give clients direct access to connect and manage their own profiles. Others handle all account connections centrally. Both models work — the profile group provides the isolation regardless of who manages the connection flow.

SaaS platform with publishing features

The SaaS platform creates a profile group for each customer when they enable social publishing. Scoped API keys are generated per customer and stored in the platform’s database. The platform’s publishing UI uses the scoped key for all API calls, ensuring tenant isolation at the API level.

This is the pattern described in detail in our SaaS integration guide. Profile groups are the multi-tenancy layer. Scoped keys are the access control layer. The platform’s code never needs to check “does this customer have access to this profile?” — the scoped key enforces it automatically.

Franchise network

A corporate parent manages brand guidelines and publishing templates. Individual franchise locations connect their own social accounts. The corporate team can publish across all locations (using the master key) or provide each location with their own scoped key for local publishing.

This maps naturally to the hierarchy: the corporate entity is the organization, each franchise location is a profile group, and each location’s social accounts are profiles within that group. Corporate can push a national campaign to all locations simultaneously. Locations can publish local content independently. Neither interferes with the other.

Multi-brand retailer

Each brand within the retailer’s portfolio gets a profile group. The marketing team manages all brands through a shared pipeline. Brand-specific configurations control which platforms each brand publishes to, what the post formatting looks like, and what events trigger automated posts.

A brand that only has Instagram and TikTok does not receive posts intended for LinkedIn. A brand with regional Facebook Pages uses placements to target the right markets. The shared pipeline handles the logic once; the configuration handles the variation.

What Postproxy handles

The multi-brand architecture you build handles the hierarchy, the configurations, and the workflow logic. Postproxy handles the publishing infrastructure:

  • Profile groups — isolation boundaries for brands, with scoped API keys for access control
  • OAuth for every platformseven different flows with token refresh and re-authentication, per profile
  • Media uploadsper-platform upload protocols handled transparently, from a single URL
  • Per-platform outcomesexplicit results for every platform on every post, scoped to the brand that published it
  • Placements — sub-account targeting for Facebook Pages and LinkedIn organizations
  • Scheduling — server-side scheduling with scheduled_at, no client-side timers needed
  • Webhooksstatus notifications for real-time monitoring across all brands

You organize the brands. You define the workflows. You manage the configurations. Postproxy ensures that when a brand publishes, the content reaches the right accounts on the right platforms — and you know exactly what happened.

Getting started

  1. Sign up at Postproxy and create your first profile groups — one per brand
  2. Generate scoped API keys for each brand
  3. Connect social accounts through the Initialize Connection flow, grouped by brand
  4. Build the shared pipeline — one publishing function parameterized by brand configuration
  5. Wire up event sources — webhooks, cron jobs, or manual triggers per brand
  6. Set up monitoring — profile health checks, success rate tracking, token expiration alerts

The infrastructure scales linearly. Adding the fifty-first brand is the same process as adding the first. The pipeline, the monitoring, and the isolation model are already in place.

Ready to get started?

Start with our free plan and scale as your needs grow. No credit card required.