Flows + Actions is the surface for tenant-defined sign-in logic. The platform ships sensible defaults for every sign-in / sign-up / password-reset path; Actions are your hook to insert custom behaviour at specific points.
Four words to know — Flow, Stage, Block, Action — and how they nest (full concept page):
- Flow — the ordered set of steps for one authentication event (login, registration, password reset, MFA enrolment, token refresh, logout). Your tenant ships with system flows; you can also create custom flows.
- Stage — a labelled section inside a Flow (e.g. Pre-Login, Post-Auth, Post-Login). Stages are where you insert your own logic.
- Block — one step inside a Stage. The platform ships ~46 built-in blocks (Password Check, Risk Evaluate, MFA, Issue Session, Run Custom Action, and so on).
- Action — JavaScript you write that runs inside a Run Custom Action block. Decides to continue / block / redirect / demand MFA. Configurable per-tenant.
The mental model
Section titled “The mental model”A Flow is a top-down sequence of Stages. Each Stage holds Blocks — built-in platform steps and, optionally, Run Custom Action blocks where your Actions run. Blocks share a state map as they execute; the next Block reads what the previous one wrote.
Login flow ├─ Stage: Pre-Login │ ├─ Block: Identity Lookup │ └─ Block: Password Check ├─ Stage: Post-Auth │ ├─ Block: Risk Evaluate │ └─ Block: Run Custom Action ◀── your Action runs here └─ Stage: Post-Login ├─ Block: Run Custom Action ◀── your Action runs here └─ Block: Issue SessionIf any Block returns deny or errors, the Flow halts. Otherwise it runs to a terminal Block — Issue Session on a successful login.
What you can do in an Action
Section titled “What you can do in an Action”- Block sign-in with a reason. The user sees the reason; sign-in halts.
- Demand MFA even when the policy wouldn't normally. Useful for risk-based step-up logic.
- Decorate the token with a custom claim. Adds
subscription_tier: enterprise(or whatever) to the access token. - Send a webhook (via your own infrastructure, not the platform's). E.g., post to your internal Slack on admin sign-ins.
- Redirect to a specific URL after success. Useful for "send Beta users to the staging environment".
What you CAN'T do:
- Read or modify the user's password or MFA factor secrets.
- Make synchronous calls to slow external systems (your Action's per-trigger time budget is a few seconds).
- Persist state across runs (each invocation is stateless).
Stages by Flow
Section titled “Stages by Flow”System flows and their editable Stages (more in triggers-and-steps developer concept):
- Login — Pre-Login, Post-Auth, Post-Login.
- Registration — Pre-Create, Post-Create.
- Password reset — Pre-Send (before reset email goes out).
- MFA enrolment — Pre-Create (before factor is recorded).
- MFA verify — Post-Success, Post-Failure.
- Token refresh — Pre-Issue (before refreshed token is minted).
Common Actions you'll write
Section titled “Common Actions you'll write”A few recurring shapes:
- Domain allowlist — block sign-ins from emails not in your customer's allowed domains.
- Welcome email post-signup — fire a notification to your CRM after a new user is created.
- Slack alert on admin sign-in — your security team wants to know when admin accounts authenticate.
- Custom claim decoration — add
tier/region/departmentto every issued token. - Risk-based step-up — if the user's session score crosses a threshold, demand fresh MFA before letting them proceed.
Where Actions live
Section titled “Where Actions live”Authentication → Flows. Pick a Flow. The editor shows each Stage and its Blocks. Add a Run Custom Action block to a Stage, then name one of your saved Actions. Click an existing Run Custom Action block to swap, configure, or remove its Action.
The Actions you can attach are pre-built (platform-shipped templates) or custom (TypeScript / JavaScript you author). See Custom actions and Flow recipes for ready-made starting points.
Authoring custom Actions
Section titled “Authoring custom Actions”Tenant admins write Actions in JavaScript / TypeScript. The platform exposes a small SDK and a sandboxed runtime; your code runs server-side under platform control.
A minimal Action skeleton:
export async function execute(input) { const email = input.user?.email if (email?.endsWith('@cymmetri.com')) { return { kind: 'continue' } } return { kind: 'block', reason: 'Sign-in is restricted to Cymmetri employees.', code: 'domain_not_allowed', }}Authoring details are in Actions → New action in the console. The full programming model — input shape, output shape, ID conventions, observability — is in the developer-facing pipelines concept.
Every Action execution records:
flow.action_executed— actor (the platform; the Flow run), Action slug, outcome (continue / block / error), duration.flow.run_completed— the parent Flow's outcome.
Per-Action logs (your console.log output) are captured against the run; visible in the Flow detail page → Recent runs.
When NOT to use an Action
Section titled “When NOT to use an Action”- For "send a webhook on every login" — use a webhook subscription instead. Cleaner; lighter; survives Action engine outages.
- For business logic that runs frequently in your own application — keep it in your application, not in an Action.
- For anything async that takes more than a few seconds — Actions have tight time budgets.
Actions are for fast, sync decisions about sign-in. Everything else is webhooks + your own backend.