ttushar.

Sole developer · 2026

Social Engine

A multi-tenant social media management platform for agencies: compose once, publish across five networks, draft with AI, and route every post through client approval — with one calendar, one audit trail, and tenant isolation enforced at the ORM layer.

TypeScriptNext.jsExpressPostgreSQLOpenAI
Role
Sole developer
Engagement
Ongoing since Dec 2025
Scope
API + web client, end to end
Status
Live in production

The problem

What was broken

An agency posting on behalf of many clients runs the same loop all day: draft the content, email it to the client, wait, decode a 'looks good' with an edit buried in the reply, then log into each network to publish at the right time. Native schedulers don't help — they are per-account and per-network, so twenty clients across five platforms means a hundred logins and no shared picture of what goes out tomorrow.

Scheduling silos per network

Each platform's native scheduler only sees its own accounts. There was no single calendar answering the one question that matters every morning: what is going out today, for whom, and where.

Approvals lost in email

Sign-off happened in inbox threads, so the version a client approved and the version that actually shipped drifted apart — one more tweak after the 'looks good' and nobody could say what was authorised.

Publish failures nobody saw

When a network rejected a post or a token had silently expired, the failure landed in a server log nobody reads. The client found out when their content simply didn't appear.

No audit trail

When a post went wrong — wrong copy, wrong account, wrong time — there was no record of who wrote it, who approved it, and what exactly was sent to the network.

The product

What I built

Social Engine is the system an agency runs its entire client posting operation on. Each client lives in its own workspace with its own connected social accounts, media gallery, and team members. Content is composed once in a multi-network editor, targeted at any combination of Facebook, Instagram, LinkedIn, X and Google Business, and dropped onto a shared calendar that shows every client's queue in one view.

From there the platform runs the process end to end. Drafts can be written by hand or generated with AI — including bulk generation to fill a whole month — then move through a client approval state before anything is allowed to publish. A scheduler picks up approved posts at their slot and pushes them to each network through platform-specific adapters, retrying where the platform demands it and logging every attempt. Connected accounts are kept alive by proactive token refresh, so expiring credentials surface as a notification to fix, not a failed post to apologise for.

I designed and built the whole thing solo — the Express API, the PostgreSQL schema and its 22 migrations, and the Next.js client — starting December 2025, and it has been my longest-running product engagement since.

Capabilities

Key features

Multi-network composer

Write a post once, target any mix of Facebook, Instagram, LinkedIn, X and Google Business, and preview it per network before it goes into the queue.

Client approval workflow

Every post moves through an explicit approval state per client. What ships is exactly what was approved — the sign-off is in the system, not in an email thread.

Content calendar

One calendar across all clients and networks, filterable by client, showing everything scheduled and pending in a month view a team can actually plan against.

AI drafting & bulk generation

Draft single posts with AI or fill a month's calendar in one pass. Every generation is logged and costed, so AI spend is a number per project instead of a shrug.

Client workspaces & team roles

Each client is a project with its own connected accounts, media gallery, and team access — the agency sees everything, each client sees only their own.

Publish audit log

Every publish attempt writes a log row. When a network rejects a post, the reason lands on the post itself, where the account manager can see and act on it.

Under the hood

How it's engineered

Tenant isolation at the ORM layer

Tenant scope is enforced by Sequelize hooks reading request-scoped context from AsyncLocalStorage — deny by default. Code with no tenant context throws instead of running unscoped, callers can't widen their own scope, and the few system jobs that legitimately cross tenants opt in through two greppable escape hatches.

Five networks, one interface

One publishing interface, four implementations. The Meta adapter is twice the size of any other because Instagram's container flow — create, poll, then publish, with per-image child containers for carousels — is an asynchronous pipeline wearing a synchronous API's clothing.

Proactive token refresh

A fifteen-minute cron checks every connected account against per-platform token lifetimes and refreshes anything approaching expiry. When a token can't be saved, a human gets notified while there's still time to reconnect — not at publish time, when it's too late.

Scheduler with per-post attribution

A one-minute cron scans for due posts across all tenants, then resolves the owning organisation per post before touching anything. Every attempt is logged against the post, so 'why didn't this go out' always has an answer.

Where it landed

Outcomes

Social Engine runs the agency's client posting today. The hundred per-network logins became one queue with one audit trail: every post records who wrote it, who approved it, when it published, and what the network said — so the answer to "why didn't this go out" is on the post, not in a server log.

The honest ledger: the AI drafting is the feature clients ask about first and the least novel part of the system — it produces a starting point and deliberately doesn't remove the approval step. The engineering I'd stand behind in any review is the tenant isolation, which turned "remember the where clause" into a property the ORM enforces. And the thing I'd change next is already written down: the in-process cron loops should become durable jobs, because a restart at the wrong moment shouldn't be able to skip a scheduled window — that's the exact failure this platform exists to prevent.