SaaS CRM Architecture
Synced
Current · v3
Conversation

Claude Sonnet 5 · 12 turns · 34.8k tokens

You 09:02
Help me build a SaaS CRM application with authentication, analytics, invoices and AI automation.
current-schema.sql whiteboard.png Engineering handbook
Studio AI 09:02

Two things change the answer materially, so I assumed both rather than stalling — say the word if either is wrong.

Assumptions 2 open
  1. 01Multi-tenant — shared database with row level security, not a schema per customer.
  2. 02Postgres — you attached a Postgres dump, so the schema is written for it.
Postgres 16 RLS Multi-tenant
Studio AI 09:03
Reasoning Completed in 11.4s
Understanding Request0.4s Four subsystems, one product, one answer
Reading Context1.2s Three attachments and 12 earlier turns
Searching Knowledge2.1s Engineering handbook, 6 matching passages
Planning Response1.6s Architecture first, then each subsystem in build order
Generating Content3.8s Prose, schema, middleware, diagram and chart
Verifying Response1.1s SQL parsed; every table referenced actually exists
Final Review0.9s Trimmed two paragraphs that repeated the summary
Ready— Response rendered with 8 artifacts
Studio AI 09:03

Full build laid out in the response panel — nine sections and eight artifacts. The honest risk is the automation layer; everything else is well-trodden.

You 09:18
Add the invoicing state machine and be specific about where the AI automation can actually fail.
Studio AI 09:19

Invoicing now carries the full state table, and the automation section names the three failure modes rather than promising it just works.

Follow up
Try
0 chars · ~0 tokens
CtrlEnter to send
Confidence 92%
Response 9 parts
Outline
Artifacts
Claude Sonnet 5 11.4s 4,120 tokens 92% confidence 3 sources

Building a multi-tenant SaaS CRM

Four subsystems, one product. Authentication and the data model are load-bearing — get those wrong and everything above them has to be rebuilt. Analytics and invoicing are well understood. The AI automation is the only part where I would plan for it to be wrong some of the time.

Build order matters more than stack choice

Tenancy has to be decided before the first table is created. Retrofitting row level security onto a live schema is the single most expensive mistake in this category of product.

1 · Architecture

A single application tier talking to one Postgres cluster, with the AI work pushed onto a queue so a slow model never holds a request open. Everything below is stateless apart from the database and the object store.

The gateway resolves the tenant once, from the token, and sets it as a session variable. Nothing downstream is trusted to filter by tenant, because eventually something will forget.

2 · Data model

Five core tables. Every tenant-scoped table carries tenant_id and a policy that reads it from the session, so a missing WHERE clause returns nothing rather than everything.

-- every tenant row is invisible unless the session claims it
CREATE TABLE accounts (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id   uuid NOT NULL REFERENCES tenants(id),
  name        text NOT NULL,
  owner_id    uuid REFERENCES users(id),
  mrr_cents   integer NOT NULL DEFAULT 0,
  created_at  timestamptz NOT NULL DEFAULT now()
);

ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON accounts
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

CREATE INDEX accounts_tenant_owner_idx
  ON accounts (tenant_id, owner_id);
Core tables, what they hold and approximate row counts at one thousand tenants
TableHoldsRows at 1k tenants
tenantsBilling entity and plan1,000
usersPeople, scoped to one tenant~14,000
accountsCompanies you sell to~480,000
dealsOpportunities and stage history~1.9m
invoicesIssued documents and state~240,000

At those volumes the composite index on (tenant_id, …) does all the work. An index on tenant_id alone is close to useless once one tenant holds a large share of the rows.

3 · Authentication

Sessions over JWTs unless you need cross-domain access. A short-lived access token with a rotating refresh token, and the tenant resolved server side on every request rather than read from anything the client sends.

// tenant-guard.ts — runs before any handler touches the database
export async function withTenant(req: Request, next: Next) {
  const session = await resolveSession(req);
  if (!session) throw new Unauthorized();

  // local scope — released with the transaction, not the connection
  await db.query("SELECT set_config('app.tenant_id', $1, true)", [
    session.tenantId,
  ]);

  return next();
}
Connection pooling will bite you here

With a transaction-mode pooler, set_config must use the local flag and run inside the same transaction as the query. Session-scoped settings leak between tenants. This is the one bug in this design that is genuinely hard to catch in review.

  • Access token 15 minutes, refresh token 30 days, rotated on every use.
  • SSO via SAML for enterprise tenants — plan the table now, build it later.
  • Audit every permission change; it is the first thing a security questionnaire asks for.

4 · Analytics

Do not query the operational tables for dashboards. A nightly rollup into a summary table keeps the CRM responsive and makes the numbers reproducible, which matters more than freshness for revenue reporting.

Fig 1 · Projected MRR at the current close rate. June assumes the enterprise tier ships on time — treat it as the optimistic bound, not the plan.

Build the dashboard around net revenue retention. It is the one metric that only moves when the product is genuinely working:

NRR = (start + expansion − contraction − churn) ÷ start

Compute it monthly from the rollup, never on the fly. Two people computing it two ways is how a board meeting goes badly.

5 · Invoicing

Invoices are a state machine, not a table of rows you edit. Once a document is issued it is immutable — corrections happen by issuing a credit note, because tax authorities and customers both expect the original to still exist.

Invoice states, the transitions allowed from each and whether the document can still be edited
StateCan move toEditable
draftissued, voidYes
issuedpaid, overdue, voidNo
overduepaid, written offNo
paidrefundedNo
void—No
Store money as integer minor units with an explicit currency column. Floating point currency is a bug you will ship and then spend a week finding.

6 · AI automation

The useful version of this is narrow: summarise a thread, draft a follow-up, score a lead, flag a deal that has gone quiet. All of it runs asynchronously, and all of it is a suggestion a human confirms.

Where this actually fails
  • Lead scoring drifts. A model fitted on last year's closed-won learns last year's market. Re-fit quarterly against a held-out set, or it quietly gets worse while still looking confident.
  • Drafted replies get sent unread. If the draft lands in the send box, someone will send a hallucinated commitment to a customer. Keep drafts one click away from sending, not zero.
  • Summaries lose the exception. Summarisation is lossy in a specific direction — it drops the one unusual sentence that mattered. Always link back to the source thread.

None of these are reasons not to build it. They are reasons to build it as a suggestion layer rather than an automation layer, at least until you have the data to know it is right.

Queue every job, cap the spend per tenant per day, and log the prompt with its response. Without that log you cannot answer the first question a customer asks when it gets something wrong.

7 · Interface

One dense workspace beats five thin pages. Salespeople live in a list and a record, so make those two excellent and treat everything else as secondary.

Mock Pipeline workspace · dashboard.png 1440 × 900 · generated concept
Fig 2 · Deal list on the left, record detail on the right, AI suggestions inline rather than in a separate panel nobody opens.

8 · Build order

  1. Tenancy and auth — weeks 1–3. Nothing else can start safely.
  2. Core CRM objects — weeks 3–7. Accounts, contacts, deals, activity.
  3. Invoicing — weeks 7–10. State machine first, PDF rendering last.
  4. Analytics rollups — weeks 9–12. Runs alongside invoicing.
  5. AI suggestions — weeks 12–16. Last, deliberately, because it needs real data to be any good.

Roughly sixteen engineer-weeks for a credible first version with two engineers. If you have to ship in one quarter, cut the analytics rollups and the SSO work — both can be added later without touching the schema.

Artifacts

Everything generated with this answer. Open one to jump to the section it came from.

  • 09:03:02read3 attachments parsed; Postgres dump detected
  • 09:03:04searchEngineering handbook — 6 passages on tenancy
  • 09:03:06checkPooler mode unknown — flagged as a risk rather than assumed
  • 09:03:09writeSections drafted in build order, not importance order
  • 09:03:13doneSQL parsed; every referenced table exists
  • 09:19:41doneInvoice state machine and failure modes added
AI Response