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.
Two halves:
Flow — the ordered list of steps the platform runs for a particular event (sign-in, sign-up, password reset, MFA enrolment, token refresh, logout). Tenant ships with system flows; you can also create custom flows.
Action — JavaScript you write that runs at a specific trigger point inside a Flow. Decides to continue / block / redirect / demand MFA. Configurable per-tenant.
The mental model
Think of a Flow as a pipeline with named slots. The slots are trigger points — login.pre-credential-check, login.post-credential-check, login.post-success, and so on. Each slot can contain zero or more Actions; if there are Actions, they run in order; if any Action returns block, the Flow halts.
Login flow:
├─ pre-credential-check ◀── your Actions can hook in here
│ ├─ (built-in) parse the form
│ └─ (built-in) check rate limits
├─ credential check ◀── platform runs this
├─ post-credential-check ◀── your Actions can hook in here
├─ MFA challenge (if required)
├─ session minted
├─ post-success ◀── your Actions can hook in here
└─ tokens issued
Three trigger points in the login flow. Your code runs at any of them.
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).
Trigger points by Flow
System flows + their trigger points (more in triggers-and-steps developer concept):
Login —
pre-credential-check,post-credential-check,post-success.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
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
Authentication → Flows. Pick a Flow. Each trigger slot shows currently-attached Actions. Drag an Action to attach, click an attached Action to configure or remove.
The Actions you can attach are pre-built (platform-shipped templates) or custom (TypeScript / JavaScript you author + upload).
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.
Audit
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
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.