This week we shipped Phase C6 of the Prozari roadmap: a built-in social media scheduler for Facebook, Instagram, and LinkedIn. The full code is now live on staging and rolling to production this Friday.

Instead of writing the usual "feature is shipped" announcement, we want to walk through why we built this — and how it fits into the broader thesis that the Work OS that wins is the one that doesn't make you switch tools.

The problem we kept hearing

We talked to about 30 agencies and content teams in Lagos, Nairobi, and Cape Town over the last quarter. The pattern was identical:

"We plan campaigns in Notion (or Trello). We schedule social posts in Buffer. We share assets in Slack DMs. We track results in a Google Sheet. Every Monday someone has to copy the planned posts from Notion into Buffer manually. It's a half-day job."

Four tools to ship one social post. The handoff between Notion and Buffer is where most of the chaos lives — wrong copy, wrong account, wrong time, missed posts.

The thesis

The fix isn't a better Buffer. It's eliminating the handoff. If the social post lives in the same workspace as the campaign that spawned it, the same task that requires it, and the same asset library that holds the media — you don't need to copy anything anywhere.

That's what shipped this week. From any card in Prozari, you can hit "+ Schedule a post" in the Hierarchy rail, pick a connected platform, write the body, drop in media URLs, set the time. The post is queued for our publisher cron worker.

What's actually under the hood

Three pieces:

1. Real OAuth, real platform adapters

We didn't reskin a Buffer SDK. We built native integrations:

  • Facebook Pages: /feed for text, /photos for image, /videos for video.
  • Instagram Business: the two-step container flow (create container, poll status, publish), with status polling up to 20s for Reels.
  • LinkedIn: /v2/ugcPosts with shareCommentary, organisation pages and personal share.

OAuth tokens are encrypted at rest with AES-256-GCM (same scheme as our Google Workspace integration). Tokens are decrypted in-memory only at publish time. Your DB never sees plaintext credentials.

2. A publisher cron that doesn't double-publish

Every minute, a worker scans for posts where status = SCHEDULED and scheduledAt <= now(). Each post is claimed atomically with a conditional updateMany WHERE status = SCHEDULED — so two replicas can't both grab the same row. Capped at 25 posts per tick to drain backlogs gracefully.

3. Self-healing on auth failure

FB returns error code 190 when a page token expires. LinkedIn returns 401 when the bearer dies. Instead of retrying forever, the publisher classifies the error, marks the linked account EXPIRED with the platform's own error message, and surfaces a "Reconnect" chip in Settings → Integrations. The post moves to FAILED with a clear failureReason.

What we deliberately didn't ship in v1

Rule of thumb: ship the 80% that solves the real pain, defer the 20% that adds surface area. So:

  • No carousel posts. FB and IG carousels are a separate API flow. We post the first media item and log a warning. ~5% of posts use multiple images; the trade-off bought us 3 weeks of engineering time.
  • No native LinkedIn image upload. We use ARTICLE link share for media URLs. Real /assets?action=registerUpload takes ~2 weeks to do right; deferred.
  • No analytics polling. Reach/impressions/engagement polling lands in T3. For now you click through to the platform to see numbers.

Try it

It's live for everyone on the staging environment now and rolls to production this Friday. Open any card → Hierarchy rail → "+ Schedule a post". Connect your accounts in Settings → Integrations → Social Media.

And if your social posts have been living in Buffer or Hootsuite — try moving the next launch into Prozari. The whole pitch is one workspace, and this is the feature that closes the loop.