Six workspaces. 54 families. One database.

What each workspace stores, which modules sit inside it, which permission opens them and where the reference lives. Enable modules per application; a module that is off has no navigation entry, no storage cost and no upsell.

Workspaces

Analytics

Where do visitors come from, and what do they do?

  • Web and product analytics
  • Saved reports and custom dashboards
  • Funnels with diagnostics
  • User paths
  • Flexible retention cohorts
  • Lifecycle and frequency
  • Click, scroll and friction heatmaps
  • Private session replay
  • Event catalog and tracking plans
  • Release and campaign annotations
  • Anonymous cross-domain journeys

Web & product analytics

One events table for the marketing site and the product.

Pageviews, sessions, sources, UTM and ad click ids, geo and devices, plus every custom event you fire — written to one partitioned table and resolved to one person. Funnels, retention, goals, a live view and a raw event explorer read from it.

Events are partitioned by month and indexed by application, environment and time, so a thirty-day breakdown is a range scan rather than a full table read. Sessions close after thirty minutes of inactivity or on $session_end, bounce and duration are derived from them, and daily rollups keep the dashboards fast without discarding the raw events behind them — every chart can still open the feed that produced it.

Module key
analytics
Replaces
a web analytics subscription and a product analytics subscription
Permission
analytics.read
Funnel · Pricing to first eventDemonstration data
Viewed pricing: 4,127Started signup: 1,286Created organization: 742Sent first event: 5114,1271,286742511Viewed pricingStarted signupCreated organizationSent first event100.0%31.2%18.0%12.4%
  • Viewed pricing4,127
  • Started signup1,286
  • Created organization742
  • Sent first event511
Signup funnel steps
StepReachedStep conversionNot continuedShare of entry
Viewed pricing4,127100.0%0100.0%
Started signup1,28631.2%2,84131.2%
Created organization74257.7%54418.0%
Sent first event51168.9%23112.4%

Steps mix a path (/pricing) with events (signup started, organization created) because both live in the same events table.

Revenue

Which channels and customers create value?

  • Revenue by channel and landing page
  • Ad spend, ROAS and acquisition cost
  • Search keywords with revenue estimates
  • Social mentions and post attribution
  • AI crawler intelligence
  • Stripe, Paddle and Lemon Squeezy adapters

Revenue

MRR next to the traffic that produced it.

Read-only connectors to Stripe, Polar or RevenueCat, normalized into subscriptions, payments and a daily MRR movement series, with customers linked to the person record by email. SaaS Pro Max never charges anything — it only reads.

Webhooks are signature-verified per provider and an hourly backfill catches anything a webhook missed. Amounts are stored in minor units with their currency, MRR is normalized across intervals, and the daily series separates new, expansion, contraction and churn so a flat MRR line still tells you what happened underneath it.

Module key
revenue
Replaces
three billing dashboards and a spreadsheet
Permission
revenue.read
Revenue · last 30 daysDemonstration data
MRR (EUR)
18,420
ARR (EUR)
221,040
Net revenue churn
2.1%
EUR 0EUR 20,000Aug 5Aug 11Aug 17Aug 22Aug 28Sep 3
Monthly recurring revenue by day
DateMRR
Aug 5, 2026EUR 14,820
Aug 6, 2026EUR 14,960
Aug 7, 2026EUR 15,040
Aug 8, 2026EUR 15,040
Aug 9, 2026EUR 15,180
Aug 10, 2026EUR 15,180
Aug 11, 2026EUR 15,310
Aug 12, 2026EUR 15,460
Aug 13, 2026EUR 15,610
Aug 14, 2026EUR 15,610
Aug 15, 2026EUR 15,880
Aug 16, 2026EUR 16,020
Aug 17, 2026EUR 16,020
Aug 18, 2026EUR 16,240
Aug 19, 2026EUR 16,390
Aug 20, 2026EUR 16,540
Aug 21, 2026EUR 16,540
Aug 22, 2026EUR 16,810
Aug 23, 2026EUR 16,960
Aug 24, 2026EUR 16,960
Aug 25, 2026EUR 17,180
Aug 26, 2026EUR 17,340
Aug 27, 2026EUR 17,560
Aug 28, 2026EUR 17,560
Aug 29, 2026EUR 17,820
Aug 30, 2026EUR 17,980
Aug 31, 2026EUR 17,980
Sep 1, 2026EUR 18,140
Sep 2, 2026EUR 18,290
Sep 3, 2026EUR 18,420

People and communications

Which accounts need attention, and how do we reach them?

  • Person profiles, segments and notes
  • B2B accounts and groups
  • Targeted surveys
  • Feedback with screenshot and failure context
  • Behavior-triggered lifecycle journeys
  • Campaigns with approvals

People (CRM)

The person behind the number, with the timeline that produced it.

identify() merges the anonymous visitor into a person, keeping the earliest first-touch attribution and re-pointing their past events. Profiles carry traits, lifecycle stage, tags, notes and segments, and personal fields stay hidden unless the role carries people.pii.read.

Identity resolution is explicit: identify() moves the anonymous person's identities onto the distinct person, sums the counters, keeps the earliest first-seen date and first-touch attribution, and re-points the last ninety days of events and sessions. alias() links a second id to the same person. Nothing is guessed from an IP, because the IP is a hash.

Module key
people
Replaces
a CRM bolted onto analytics
Permission
people.read
PersonDemonstration data
Ada Okaforada@northwind.dev
paying
Plan
Team
First touch
news.ycombinator.com
Sessions
47
Events
1,284
  1. 09:14subscription upgradedTeam, 49.00 EUR / month
  2. 09:11$pageview/settings/billing
  3. 08:52invite sent2 seats
  4. 08:47$identifydistinct id user_8841

Communications

Campaigns to a segment, with a second pair of eyes before send.

Draft, submit, approve, then send or schedule. The audience is snapshotted at approval so the record names how many people were authorised. Email, in-app and Slack each write a delivery row; pause stops at the next batch, cancel skips what is still queued.

Submitting a campaign writes an approval request with action communications.send. The campaign page and the organization's operations page decide the same row. Emails and phone numbers stay behind people.pii.read; a delivery is a row you can open, not a number that vanished into a vendor.

Module key
communications
Replaces
a marketing-automation subscription and a spreadsheet of who was emailed
Permission
communications.read
Campaign · Win-back · 14 day quietDemonstration data
Audience
Trial ended, last seen > 14d
Channel
Email
Snapshotted
1,284 people

Awaiting approval A second operator has to approve before anything is sent. Delivery rows record each message.

Errors and metrics

What failed, who is affected, and what changed?

  • Error tracking with source maps
  • Release health and adoption
  • Issue grouping, ownership and tracker sync
  • OpenTelemetry traces and request correlation
  • Structured logs with trace links
  • Slow-query and performance findings
  • CPU and memory profiling
  • Browser and multi-region synthetic checks
  • Cron and background-job check-ins
  • SLOs and error-budget alerts
  • On-call routing and incidents
  • Infrastructure and Kubernetes monitoring
  • Load tests and performance budgets
  • Real-user monitoring diagnostics

Error tracking

Exceptions from the browser and the server, grouped into issues.

The SDKs capture uncaught errors, unhandled rejections, React error boundaries and framework request errors, parse the stack, mark in-app frames and attach breadcrumbs. Issues carry a release, a status and the people affected — which is a link back to the person, not a number.

Grouping is by fingerprint, and you can set the fingerprint yourself when the default grouping is wrong. Frames are marked in-app or vendor so the culprit line is the one you wrote, breadcrumbs carry the navigation, clicks, requests and console lines that preceded the throw, and an issue moves between unresolved, investigating, resolved and ignored with the change recorded in the audit ledger.

Module key
errors
Replaces
an error tracking subscription
Permission
errors.read
Issues · productionDemonstration data
IssueEventsUsersStatus
TypeError Cannot read properties of undefined (reading 'plan')app/checkout/summary.tsx in PlanSummary8461unresolved
FetchError POST /api/billing/session returned 502lib/billing/session.ts in createSession3127investigating
ChunkLoadError Loading chunk 42 failednext/dist/client/route-loader.js1212resolved

Metrics and reliability

Counters, gauges, web vitals and uptime, with alert rules attached.

Send a counter from a job, a timer around a request, or let the browser SDK report LCP, CLS, INP, FCP and TTFB. Uptime monitors run on the same worker as everything else, and alert rules resolve to email, Slack or a webhook.

Four instrument kinds — counter, gauge, histogram and timer — with up to twenty label keys each, plus web vitals rated against the Core Web Vitals thresholds rather than an invented scale. Uptime monitors and alert rules run as jobs on the worker, and an alert names its inputs so an operator can see why it fired before deciding what to do.

Module key
metrics
Replaces
a metrics and dashboard subscription
Permission
metrics.read
Uptime and latency · last 30 daysDemonstration data
0 ms200 ms400 msAug 5Aug 11Aug 17Aug 22Aug 28Sep 3
p95 response time by day
Datep95 ms
Aug 5, 2026184 ms
Aug 6, 2026179 ms
Aug 7, 2026188 ms
Aug 8, 2026176 ms
Aug 9, 2026168 ms
Aug 10, 2026165 ms
Aug 11, 2026191 ms
Aug 12, 2026186 ms
Aug 13, 2026194 ms
Aug 14, 2026205 ms
Aug 15, 2026231 ms
Aug 16, 2026212 ms
Aug 17, 2026188 ms
Aug 18, 2026183 ms
Aug 19, 2026179 ms
Aug 20, 2026176 ms
Aug 21, 2026182 ms
Aug 22, 2026188 ms
Aug 23, 2026174 ms
Aug 24, 2026169 ms
Aug 25, 2026181 ms
Aug 26, 2026186 ms
Aug 27, 2026192 ms
Aug 28, 2026187 ms
Aug 29, 2026183 ms
Aug 30, 2026172 ms
Aug 31, 2026168 ms
Sep 1, 2026179 ms
Sep 2, 2026184 ms
Sep 3, 2026181 ms
Uptime99.97%Healthy60s checks from the worker
  • healthysaaspro.devGET /142 ms
  • healthyIngestPOST /api/v1/ingest38 ms
  • warningWorker heartbeatspm.worker.job.durationqueue 41

Flags and evaluations

Is a change safe, and is it useful?

  • Feature flags with local evaluation
  • Schedules, cohort targets and config
  • Statistical A/B experiments
  • Evaluation runs with graders and gates
  • Production AI traces and conversation costs
  • Sampled production AI quality
  • Proactive investigations and proposed fixes

Feature flags

Boolean and multivariate flags the client and the server agree on.

Percentage rollouts and property rules, evaluated in the browser from a cached payload and on the server through the same endpoint. Both use the shared hash, so a person bucketed on in the browser is bucketed on in your API too.

The browser SDK loads a flag payload once and re-evaluates locally, so a flag check is not a network call; the server evaluates through POST /api/v1/flags/evaluate. Both use rolloutBucket from @saaspro/shared, whose reference vectors are pinned in that package's tests — the same person cannot land in different buckets on the two sides of your app.

Module key
flags
Replaces
a feature flag subscription
Permission
flags.read
Flag · new-onboardingDemonstration data
  1. ifplan is one of team, businesson
  2. else ifcountry is DEoff
  3. elseeveryone else25% rollout, hashed on distinct id
ts
const enabled = await spm.flags.isEnabled("new-onboarding");

The browser and the server bucket a person identically: both hash the distinct id with the same murmurhash3_32 from @saaspro/shared.

Evaluations lab

Prompt and model changes with a gate in front of them.

Datasets, graders, baseline against candidate, and a quality gate that a deploy can consult. Runs happen on the job worker, artifacts are kept, and a human review is recorded next to the automated score instead of replacing it.

A run pins the prompt, the model configuration, the retrieval settings and the dataset checksum, so a score is reproducible and a regression can be traced to the thing that changed. Graders can be deterministic checks or a judge model; either way the gate policy — minimum score, maximum failure rate — is data, and a human review sits beside the number instead of overwriting it.

Module key
evals
Replaces
a spreadsheet and a hopeful eyeball
Permission
evals.read
Comparison · support-replies · 120 casesDemonstration data
GraderBaselineCandidate
Answer contains the policy0.780.91+0.13
No invented order number0.940.97+0.03
Tone matches the brand guide0.710.83+0.12
Under 120 words0.990.96-0.03

Quality gate: score ≥ 0.85, failures ≤ 5% — passed

AI assistant

A question about this application, answered from its own data.

Ask lives in the console, not as a general chatbot. It reads the same events, people, errors and revenue the operator can already see, and it cannot see another tenant. The MCP server and the Management API are the same permission-filtered layer for agents.

The assistant is a page, a top-bar button and an MCP tool over the same reads. It cannot be enabled on its own: at least one data module has to be on, otherwise it would have nothing to cite. Hidden sidebar entries still answer from search; a disabled module does not.

Module key
ai
Replaces
a chat window pasted onto a dashboard
Permission
ai.use
Ask · this applicationDemonstration data

Which release dropped checkout conversion?

web@2026.9.1. The pricing funnel fell 4.1 points; 38 people hit ERR_CHECKOUT_TIMEOUT. Flag checkout.edge is at 25%.

Answers only from this application's data, with the same permissions the operator holds. Enabling the module requires at least one data module to read.

Data and integrations

How do we connect, control and move our data?

  • Custom transformations and destination templates
  • Telemetry budgets and cardinality controls
  • Short-lived privacy mode
  • External data sources and business joins
  • Historical import and migration
  • Scheduled exports to warehouses and storage
  • Reusable panels and installable extensions
  • Backend SDKs, React Native and native crashes
  • WordPress, WooCommerce and no-code installs
  • Enterprise identity provisioning

Ad destinations

Server-side conversions, forwarded from events you already send.

Map one of your events onto an ad platform's event once, and every matching event is dispatched from the worker with retries and a delivery record. Click ids captured at landing (gclid, fbclid, ttclid, li_fat_id) ride along.

Every dispatch writes a delivery row with a redacted request, the response and the error, so a conversion that did not arrive has an answer rather than a shrug. Deduplication uses the SaaS Pro Max event id as the destination's event id, which is what lets a browser pixel and a server-side conversion describe the same purchase without counting it twice.

Module key
destinations
Replaces
four pixel integrations and a Zapier bill
Permission
destinations.read
Destination · Meta Conversions APIDemonstration data
Your eventSent asValue
subscription startedPurchaseproperties.mrr EUR
trial startedStartTrial
$pageviewPageView

Six adapters: Meta CAPI, Google Ads (click conversions or GA4), TikTok Events, LinkedIn Conversions, a signed webhook, and Slack. Emails and phone numbers are SHA-256 hashed before they leave; the raw IP never existed to send.

App user management

Your users, in the console, without sharing a database.

You expose one signed route. The console calls it with an Ed25519 JWT that lives 60 seconds, names the operator, and carries only the permissions that operator actually holds. Support can read a dossier and apply a reversible, audited control without a shell.

The token is an EdDSA JWT with iss, aud, nbf, exp at most sixty seconds out, and a jti your handler should cache for two minutes to reject replays. The private key is generated per application and lives in the vault; the public key is yours to pin. Rotation is supported, and every control applied through the connector is written to the audit ledger with the reason the operator typed.

Module key
connector
Replaces
a bespoke admin app per product
Permission
people.read
Your application, tenant side
ts
import { createConnectorHandler } from "@saaspro/node";

export const POST = createConnectorHandler({
  publicKey: process.env.SPM_CONNECTOR_PUBLIC_KEY!,
  audience: process.env.SPM_APP_ID!,
  handlers: {
    listUsers, getUser, applyControl,
  },
});
  • GET /health capabilities the console may use
  • GET /users paged, filtered, PII redacted unless the caller holds people.pii.read
  • GET /users/{id} dossier: facts, activity, available controls
  • POST /users/{id}/controls a reversible action, with a reason

What every module shares.

A module is a set of files and one navigation entry, not a product bolted on. Feature families are sections inside those modules, which is why the sidebar stays short.

Modules
11 switches, 54 families
Retention
per application, 365 days by default
Environments
production, staging, development

Scoped the same way

Every module table carries org_id, and app_id and env_id when it is application-scoped. Isolation is enforced in the query layer, resolved server-side from membership — a client cannot hand itself a tenant.

Gated the same way

One frozen permission list, seven seeded roles and custom roles per organization. A page and its API handler each resolve the caller's permission independently, so a hidden button is a courtesy, not the control.

Recorded the same way

Mutations write to an append-only, hash-chained audit ledger that a database trigger protects from updates and deletes. High-risk actions additionally need a reason, a passkey verified within ten minutes, and sometimes a second operator's approval.

Separated the same way

Production, staging and development are real environments with their own keys and their own data, chosen from the top bar. A staging spike never moves a production number.

Default modules on a new application are analytics, people, errors, metrics and flags. Communications, evaluations, destinations, revenue, the connector and Ask are switches you turn on when you need them. See the changelog for what is in the current build.

Built for the agent doing the integration.

One permission-filtered read layer for agents and operators, over MCP.

Purpose-built MCP

SaaS Pro Max in every conversation with your agent

Open a workspace. Wire the SDK. Read the truth.

Sign in with Google, enrol a passkey, and send your first event in minutes.