← blog/posteptrack-one-instance-per-clientPT
    // blog/posteptrack-one-instance-per-client.md

    PostepTrack: Why Each Client Gets Their Own System

    We run the same architecture across 4 clients. Each one isolated. The decision NOT to go multi-tenant — and when we do.

    2026-06-02·8 min read·architecture·POSTEP Digital

    Isolated Meta App · Own Edge · Own table · Blast radius = 1 client · Multi-tenant where it fits

    // 01

    What PostepTrack is

    PostepTrack is the system that stitches Meta Ads to the client's CRM. When someone clicks a Facebook or Instagram ad and lands on WhatsApp or a form, PostepTrack grabs the identifiers Meta exposes in that window and injects them into the lead that shows up in the CRM minutes later. Without it, the CRM just shows “source: unknown”.

    It's a Supabase Edge Function running on Deno, listening to Meta webhooks, waiting for the lead to appear in the CRM, and enriching it with campaign, adset, ad — all tied together by ctwa_clid for WhatsApp or leadgen_id for Lead Ads. Small code, short logic. Everything around it is what's complicated.

    We run this in four clients in production today. Each one with its own isolated instance: its own Meta App, its own Edge, its own table. The question that always comes up is the same — why not unify it.

    // 02

    The multi-tenant temptation

    On paper, multi-tenant solves it. One Edge, one table with an org_id column, one agency Meta App subscribed to every client's WABA. Touch one client, all of them get it. Bug in one client? You fix it once. Single deploy.

    // two models for the same problem
    Theoretical multi-tenant
    ·1 agency Meta App
    ·1 single Edge Function
    ·1 table with org_id
    ·single deploy covers everyone
    ·leaner code
    One instance per client
    1 Meta App per client
    1 Edge per client
    1 table per client
    Kommo field IDs isolated
    blast radius = 1 client

    The version on the left is what every dev wants to build. The version on the right is what survives in production.

    // 03

    Why we split: 4 real reasons

    Each reason below showed up in practice, not on a whiteboard. When you operate four clients at the same time, they show up early.

    #01 — Every client's CRM is unique

    Client A uses Kommo, B also uses Kommo but with a different pipeline, C uses Clint. Even between two Kommos, the field IDs for custom fields are completely different numbers — ctwa_clid is 802793 on one, 1084178 on the other. Mapping that by org in a single codebase is pretending it works until someone renames a field.

    #02 — Meta effectively requires one App per subscriber

    Technically one App can listen to multiple WABAs. In practice, each client wants the App registered in their name, with their Business Manager, with tokens they control. A single agency App centralizes a huge single point of failure: if Meta suspends it, everyone goes down together.

    #03 — Tiny blast radius

    When Vexo had 83.6% silent fail on the leadgen branch, only Vexo was affected. The other three kept running untouched while we debugged. In multi-tenant, that same bug takes down four companies' operations at the same time.

    #04 — Onboarding without renegotiating everything

    New client comes in by copying a folder, swapping four variables, and running a deploy. No need to open a conversation about “your org_id column lives here” or rename things to avoid conflicts. The existing client doesn't even know another one joined.

    None of these reasons are theoretical. Each one cost us time and money before we learned to actually split.

    // 04

    The cost of splitting (and why it's worth it)

    Splitting has a price. We pay it in code duplication and in discipline to keep fixes from staying in one client only. The trick is to keep a canonical template and copy from it.

    // how a fix travels between clients
    client A
    finds bug · fixes it in their index.ts
    canonical template
    pattern emerges · becomes a commit in posteptrack_kommo/
    clients B, C, D
    replicate on next touch · not all at once
    ✓ doc updates · pending fixes stay visible

    Pending fixes stay visible because the doc lists what each client already has and what still needs to be replicated. It's the opposite of “we hid the complexity inside an if (org === X)”. Here the divergence is explicit — and at least we know what's running in each place.

    The cost of copying is lower than the cost of a wrong abstraction. And much lower than the cost of taking down four clients with one bad deploy.

    // 05

    When multi-tenant makes sense (and we do it)

    To be honest: we also run two multi-tenant SaaS in production, with real org_id and RLS. They exist because the problem is different.

    product YOU sellyour own SaaS with billing, self-service signup, and a single UI → multi-tenant is mandatory, the client must only see their own data
    integration the client already pays for separatelyPostepTrack lives inside the client's Meta account and CRM, with their tokens → an isolated instance honors that boundary
    blast radius kills the caseif one bug in one instance takes down all of them, multi-tenant is the worst possible model for critical pipelines like this

    Multi-tenant is a product decision, not an architecture decision. PostepTrack isn't a product — it's per-client infrastructure.

    // 06

    The practical rule

    at the start

    Ask whether the system lives INSIDE the client's account (their token, their CRM, their Meta). If yes, isolated instance beats multi-tenant before you even think about code.

    as you grow

    Keep a canonical template. When a pattern emerges in 2+ clients, promote it to the template — copying becomes disciplined, not lazy.

    when fixing

    Fix is born in one client. Becomes a commit in the template. Replicates to the others on demand. Pending fixes live in the doc, not inside a hidden if.

    always

    Blast radius is the metric that matters most in critical pipelines. A model that takes down four clients at once is not worth the single deploy.

    // main rule

    Multi-tenant is for the product you sell. Isolated instance is for infrastructure that lives inside the client. Confuse the two and you pay in downtime — and the client pays alongside you.

    written by
    POSTEP Digital
    ← all posts