Skip to content

Actions overview

An Action is a reusable piece of custom logic that runs at a specific trigger point inside a Flow. Two flavours:

  • Platform-shipped templates — pre-built Actions for common needs. Drop in; configure via form; save.
  • Custom Actions — TypeScript / JavaScript you author yourself. Upload; configure schema; attach.

Authentication → Actions. Lists every Action available in your tenant.

Each row:

  • Name — the Action's name.
  • Kindtemplate (platform-shipped) or custom (yours).
  • Compatible triggers — the trigger slots this Action can be attached to.
  • Attached — how many flow slots currently use this Action.

The platform ships these out of the box. The list grows over releases:

  • domain-allowlist — block sign-ins from emails outside an allowed list.
  • domain-blocklist — block sign-ins from emails on a denied list.
  • rate-limit-by-ip — refuse requests above a per-IP threshold.
  • rate-limit-by-user — refuse repeated attempts on the same user account.
  • decorate-token — add a custom claim to the issued token.
  • webhook-emit — POST to a URL when this slot fires.
  • slack-notify — post to a Slack incoming webhook (a specialised webhook-emit).
  • block-by-risk-score — block when the risk engine's score exceeds a threshold.
  • demand-mfa-by-risk-score — force MFA even when policy wouldn't normally.
  • redirect-by-group — redirect specific group members to specific URLs after sign-in.

Each template ships with a configuration form. No code; just fill in fields.

When templates don't cover your case, write your own. The flow:

  1. Open Actions → New action.
  2. Pick a starter template (or "Empty Action" for fully custom).
  3. The editor opens with a TypeScript skeleton.
  4. Write the logic.
  5. Configure the Action's metadata (name, slug, compatible triggers, config schema).
  6. Save. The Action is now available to attach to flow slots.

The full programming model + the platform's runtime sandbox is documented in the developer-facing pipelines concept. For a hands-on walkthrough, see Your first custom action. For ready-made starting points, see Flow recipes — each recipe names the Blocks and Action code needed for a specific goal.

// src/index.ts in the Action package
export async function execute(input) {
// input: { trigger, user, request, state, context }
// return: { kind: 'continue' | 'block' | 'demand_mfa' | 'redirect' }
return { kind: 'continue' }
}

The skeleton is filled in by the editor; you customise the logic.

Each Action has a version field on its metadata. When you edit + save, the version bumps. Flows reference Actions by version — a Flow attached to domain-allowlist v1.0.0 keeps running v1.0.0 even after you publish v1.1.0.

To roll out a new version to an attached flow:

  1. Publish the new version (v1.1.0).
  2. Open the flow.
  3. Update the version pinned to the slot.
  4. Save the flow.

The flow now runs v1.1.0. Existing in-flight sign-ins continue on whatever version started them; new sign-ins use the new version.

This pattern is the cheap insurance against "the Action update broke production for 10 minutes". You stage new versions; you roll out per-flow when you've verified the staging.

Actions are tenant-scoped. The Actions in tenant A's catalogue are not visible to tenant B. There's no cross-tenant Action sharing today.

If you have many tenants running the same Action, you'll author it in each — or use the management API to script the upload across tenants.

Every Action execution + every Action edit lands in audit:

  • flow.action_attached — Action added to a flow slot.
  • flow.action_detached — removed.
  • flow.action_updated — config changed.
  • flow.action_executed — ran during a flow.
  • flow.action_published — new version published.

The combination lets you reconstruct "why did sign-ins start failing yesterday at 14:00?" — usually a flow.action_attached or flow.action_updated entry near that timestamp.