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

Configuration

Every option of the SaaS module, how registration asks for a card, and which plan a workspace falls back to.

The module reads its options from its entry in antelope.config.ts. Only stripe is required.

Options

OptionTypeDefaultDescription
stripe.secretKeystringrequiredStripe secret key. A placeholder key (sk_test_placeholder) disables every Stripe call.
stripe.publishableKeystringrequiredStripe publishable key, sent to the browser for the card element.
stripe.webhookSecretstringrequiredSigning secret of the webhook endpoint targeting /api/saas/webhooks/stripe.
stripe.readTimeoutMsnumber4000How long a Stripe read a page renders (billing status, next invoice preview, VAT check) waits before the page renders without it. Writes keep the Stripe SDK's own timeout and retries.
stripe.readMaxNetworkRetriesnumber0How many times such a read is retried on a network failure.
admissionMode"open" | "invitation-only""open"invitation-only closes public registration; invitations keep working.
registration.paymentMethod"required" | "optional" | "none""required"Whether public registration asks for a card. See Registration.
publicScreens.registerbooleantruefalse frees the register slug for your own page.
publicScreens.pricingbooleantruefalse frees the pricing slug for your own page.
allowedRedirectHostsstring[][]Hosts accepted as Stripe Checkout and billing portal return URLs, matched on URL.host. Loopback hosts are accepted in development.
defaultPlanSlugstringfirst free planThe free plan every workspace falls back to. See Default plan.
upcomingInvoicePreviewCacheTtlSecondsnumber3600How long the upcoming invoice preview is cached per workspace; 0 prices every request.
reportingCurrencystring"EUR"ISO 4217 code the back office reports recurring revenue in. Other currencies are shown apart, never converted.

The public options reach the browser through the frontend module options, under useDmsRuntimeConfig().public.dmsSaas (admissionMode, registrationPaymentMethod). Stripe secrets never do.

The frontend server needs DMS_AUTH_ESTABLISH_ENDPOINTS=/api/saas/register/finalize; see Installation.

Registration

Public registration is short: an account (name, e-mail, password) and the acceptance of the terms, plus a card when the deployment asks for one. The workspace it opens lands on the catalogue's first free plan — the lowest order among active plans priced 0 and open to individuals — under a default name its owner renames from the workspace settings. Customer type, billing address, VAT number and plan choice belong to the upgrade flow. Registration answers 409 saas.errors.plan.no_free_plan while the catalogue has no such plan.

POST /api/saas/register holds the password to the DMS password policy (isPasswordCompliant from @antelopejs/interface-dms/auth/password) before it looks the e-mail up or creates anything, and answers 400 saas.errors.registration.password_policy otherwise. The OAuth completion carries no password.

Two module options shape it:

{
  "modules": {
    "dms-saas": {
      "config": {
        "admissionMode": "open",
        "registration": { "paymentMethod": "required" }
      }
    }
  }
}

registration.paymentMethod decides whether registration asks for a card:

ValueBehaviour
required (default)The card step is shown and must be completed. The workspace gets a Stripe customer holding that card and a free subscription — a local one while the free plan is off Stripe, which the upgrade checkout replaces on that customer — and the free-workspace-per-card cap applies when it is on.
optionalThe card step is shown, and the visitor may choose to add a card later. Without a card the workspace is card-less, as under none.
noneThe card step is never shown, GET /api/saas/register/setup-intent answers 400 saas.errors.registration.payment_method_disabled, and Stripe is never called: the workspace gets a local free subscription with no Stripe customer, which the upgrade checkout creates when the owner first pays. A card sent anyway is ignored.

The free-workspace-per-card cap ("Free workspaces per card" in the operator billing rules) is off by default (0): limiting free workspaces per person is usually the consuming SaaS's own business rule. An operator may turn it on; it then asks a card of every free workspace created in the app.

Under optional and none, a card-less workspace has no card fingerprint, so the free-workspace-per-card cap does not apply to it: the only remaining limit on free workspaces is one account per e-mail address.

The option covers both public entry points — POST /api/saas/register and the auth/no-workspace completion — and nothing else: invitation sign-up never asks for a plan or a card, whatever admissionMode and registration.paymentMethod say. Tenant-side workspace creation keeps requiring a card.

admissionMode: "invitation-only" closes public registration: every /api/saas/register route answers 403 saas.errors.registration_closed before looking at the card policy, the registration screens render a "registration by invitation only" state instead of the form, and the login page drops its sign-up link. Invitations keep working.

Both values reach the browser through the frontend module options (useDmsRuntimeConfig().public.dmsSaas.admissionMode and .registrationPaymentMethod); useSaasRegistration() already reads them.

The Stripe SetupIntent behind the card step is restricted to cards on the server (allowed_payment_method_types: ["card"], since Stripe is retiring payment_method_types on SetupIntent creation); the Payment Element is created from its client secret alone, since Stripe refuses paymentMethodTypes next to a clientSecret.

Default plan

A workspace is never without a plan. The default plan is the catalogue's first free plan — the lowest order among active plans priced 0 and open to individuals — unless defaultPlanSlug names another such plan:

{ "modules": { "dms-saas": { "config": { "defaultPlanSlug": "free" } } } }

It is what registration opens, and it is attached, as an active card-less subscription, to every workspace that has no subscription at all — the platform's default tenant and workspaces created outside dms-saas included. The backfill runs at startup and with the billing-state recompute every 15 minutes, and the billing page covers a workspace on first read; each pass is idempotent and never touches a workspace that holds any subscription. While the catalogue has no free plan, nothing is attached and a warning is logged.

Once attached, the plan's permissions cap the workspace's members like any other plan (see Tenant owner permissions).