[data-reveal]{opacity:1!important;transform:none!important}
Installation
SaaS

Installation

Add the SaaS module to a DMS project, connect Stripe, and run the playground with its data set.

dms-saas runs inside a project that loads @antelopejs/dms in multi-tenant mode, a database module, an e-mail module and a file storage.

Requirements

PackageVersion
@antelopejs/dms>=0.9.0 <1.0.0
@antelopejs/interface-dms>=0.7.0 <1.0.0
@antelopejs/dms-frontend (the renderer)0.6
dms-saas 0.6 runs on DMS 0.9 and renderer 0.6 only. On DMS 0.8, stay on dms-saas 0.5 until you upgrade the backend, interface and renderer together. See DMS installation for the toolchain and Troubleshooting for dependency and frontend integration errors.

Add it to a project

Add the module

Terminal
ajs project modules add @antelopejs/dms-saas

Configure Stripe

Give the module your Stripe keys, from the environment rather than committed values:

antelope.config.ts
export default defineConfig({
  name: "my-saas",
  envOverrides: {
    STRIPE_SECRET_KEY: "modules.dms-saas.config.stripe.secretKey",
    STRIPE_PUBLISHABLE_KEY: "modules.dms-saas.config.stripe.publishableKey",
    STRIPE_WEBHOOK_SECRET: "modules.dms-saas.config.stripe.webhookSecret",
  },
  modules: {
    "dms-saas": {
      source: { type: "package", package: "@antelopejs/dms-saas", version: "^0.6.0" },
      config: {
        stripe: {
          secretKey: "sk_test_placeholder",
          publishableKey: "pk_test_placeholder",
          webhookSecret: "whsec_placeholder",
        },
        allowedRedirectHosts: ["app.example.com"],
      },
    },
    // dms, api, mongodb, auth-jwt, nodemailer, file-storage-local, ...
  },
});

With placeholder keys the module still runs: plan syncs are skipped and Stripe-backed blocks say so.

Point Stripe's webhook at the module

Create a webhook endpoint in the Stripe Dashboard targeting https://<api>/api/saas/webhooks/stripe, with the API version 2026-08-26.dahlia, and put its signing secret in STRIPE_WEBHOOK_SECRET. See Stripe.

Let the frontend server complete sign-ups

The auth/no-workspace completion and OAuth sign-ups log the visitor in through the frontend loader's POST /auth/establish route, which only calls endpoints the deployment names. Declare the finalize endpoint in the frontend server's environment:

.env
DMS_AUTH_ESTABLISH_ENDPOINTS=/api/saas/register/finalize

Publish a free plan

Registration opens every workspace on the catalogue's first free plan. Create one in Modules › SaaS › Plans, or seed it from your module. Until a free plan exists, registration answers 409 saas.errors.plan.no_free_plan.

The pages render on the server, the public pricing page's plans included: the frontend server reads what a page shows and hands it to the browser with the page. The Stripe reads a page renders give up after stripe.readTimeoutMs (4 s by default), and the past-due and trial-ending strips of the layout read Stripe in the browser, so a slow Stripe never holds a page. Dialogs and drawers read their data when they open.

Try it in the playground

Terminal
cd packages/dms-saas
pnpm install && pnpm --dir frontend-vue install && pnpm build
pnpm dev            # backend on http://localhost:5010
pnpm frontend:dev   # dashboard on http://localhost:3001

The playground connects to MongoDB at mongodb://localhost:27017 (set MONGO_URL in the environment or in playground/.env). Stripe keys and the GitHub and Google OAuth credentials are read from playground/.env, never committed; see playground/.env.example. A provider's sign-in button appears once both its client id and secret are set, with http://localhost:3001/auth/oauth/<provider>/callback as its callback URL.

Playground data set

On its first start against an empty database, the playground writes a data set to review every screen. Sign in as [email protected] with the password Playground-Admin-1!: a platform admin who also owns the acme workspace. Every other seeded account (platform admins [email protected] and [email protected], workspace owners and members) uses the same password.

It holds the feature and plan catalogue (Free, Starter, Pro, Team, Business per seat, Enterprise yearly and hidden, Growth 2024 retired), thirteen workspaces in every billing state (active, trialing, free, complimentary with and without an end date, past due, pending payment, suspended, cancelled) with their members and invitations, invoices in every status, credit notes, plan migrations, segments, internal notes, billing rules and legal documents. Dates are relative to the first start, and Stripe ids are fake: nothing calls Stripe for them.

The seed lives in playground/src/seed and runs once; drop the database to start over. Segment counts appear after the first recompute (every 15 minutes), and the running plan migration turns to "reconciliation required" on the next restart, as an interrupted migration does.