Identity blocks are the bridge between what a user typed and the record stored in the platform. They find users by a trait like email or phone, mint new accounts, and patch profile fields. Verification blocks sit alongside them: they dispatch a one-time token to a channel and then confirm the token the user supplies. Together these eight blocks cover the full lifecycle from first-time signup through profile self-service.
A flow that creates an account without these blocks is a flow that writes to nowhere. Almost every Login, Registration, and Password Recovery flow starts with an Identity block and ends with one of the verification pair before handing off to a Session block.
When a user supplies an unrecognised email or phone number, the platform must not reveal that the address is not registered. The Lookup User block enforces this by returning a consistent response shape whether the identity exists or not, and the default Login flow is designed so both the "user not found" and the "wrong password" paths take the same time. If you add a Decision block downstream of a lookup, make sure both branches take equivalent steps — including the password check — before reaching the terminal block. Skipping the password check for unknown users produces a measurable timing difference that attackers can exploit to harvest valid email addresses.
Picking a block
Section titled “Picking a block”- "Find a user by email or phone" → Look Up Identity
- "Mint a new user account" → Create Identity
- "Update a user's profile fields (full overlay)" → Update Profile
- "Send an email or SMS verification token" → Send Verification
- "Confirm the user-supplied verification token" → Check Verification
- "Check that a refresh token is still valid" → Validate Refresh Token
- "Send an email-confirmation link (single-step mode)" → Email Verification
- "Update an identifying alias on an existing user (shorthand)" → Update User
Look Up Identity
Section titled “Look Up Identity”Block id: identity_lookup · Available in: login, registration, password_recovery, mfa_enroll, mfa_step_up
Find a user by a single identifying trait — email or phone — and make the result available to every downstream block. The block is read-only: it never modifies the identity, so it carries no compensating action and cannot deny on its own.
Two config flags drive opposite use cases. Set must_exist: true in a Login flow so the block short-circuits when the trait has no match, routing the engine to a deny path without leaking timing information. Set must_not_exist: true in a Registration flow so an existing account triggers an early exit — the collision guard before Create Identity runs.
Reads from state
identifier— the email address or phone number the user supplied at flow start
Writes to state
step.<slug>.found—truewhen a matching identity exists for the supplied trait valuestep.<slug>.user_id— the platform user ID, set whenfoundistruestep.<slug>.identity_state— the account's lifecycle state (active,suspended, etc.)step.<slug>.identity_traits— the full trait object for the found identitystep.<slug>.identity_verified— per-channel verification flags (email_verified, phone_verified)
Use when — any flow that needs to resolve an email or phone to a user ID. Place it early in the pre-login or pre-registration stage, before credential checks.
Cautions — The must_exist and must_not_exist flags are mutually exclusive. Setting both is an authoring error the editor will catch at save time. When neither is set, the block succeeds regardless of whether a match was found, and downstream blocks must read step.<slug>.found to branch.
Create Identity
Section titled “Create Identity”Block id: identity_create · Available in: registration
Mint a new identity in the platform with the supplied traits. This block is intentionally scoped to the registration trigger only — it should never run on a Login or Password Recovery flow. It expects a prior Look Up Identity block with must_not_exist: true to have already confirmed the address is not taken; Create Identity performs no deduplication of its own.
If a downstream block fails after Create Identity has already written to the identity store, the platform's compensating action will soft-delete the orphan account automatically. The admin console surfaces these partial-registration identities in the Users list under the pending state; they are pruned on the next lifecycle pass.
Reads from state
identifier— the email or phone to register undertraits— any additional trait fields collected by the registration form
Writes to state
step.<slug>.created—truewhen the new identity was successfully mintedstep.<slug>.user_id— the platform user ID of the newly created accountstep.<slug>.identity_state— the initial lifecycle state (typicallyactiveorpending_verification)step.<slug>.identity_traits— the trait object as stored
Use when — the Registration flow, immediately after a Look Up Identity block confirms no collision. Follow it with Send Verification or Set Credential.
Update Profile
Section titled “Update Profile”Block id: profile_update · Available in: login, registration, password_recovery, mfa_enroll, mfa_step_up
Apply a partial trait overlay — and optionally a verified-channel overlay — to an existing identity. At least one overlay must be non-empty; the engine rejects no-op updates as authoring errors at flow execution time, not at save time.
Cymmetri, for example, uses this block in their "Edit Account" flow to let a user update their display name and preferred language without triggering a full re-verification. By supplying only the changed fields in trait_overlay, the block writes only those fields — the rest of the identity is untouched.
Reads from state
user_id— from an earlier Look Up Identity or the current sessiontrait_overlay— the fields to write (partial; unspecified fields are preserved)verified_overlay— optional channel-verification flags to update alongside the traits
Writes to state
step.<slug>.updated—truewhen the overlay was applied successfullystep.<slug>.identity_state— the identity's lifecycle state after the updatestep.<slug>.identity_traits— the full updated trait object
Use when — profile self-service flows, post-registration onboarding steps, or any flow that needs to patch a user record without replacing it entirely.
Send Verification
Section titled “Send Verification”Block id: verification_send · Available in: registration, login
Dispatch a verification token to the user via email or SMS. Configure the delivery channel (email or sms) and an optional TTL in seconds; the platform generates a secure token, stores a hash, and hands the plaintext to the courier. Always pair this block with a downstream Check Verification block to confirm the token the user returns.
The token itself is written to state so a Decision block could, in principle, read it — but you should never surface the raw token in a webhook body or custom action response. It is an audit-redacted field.
Reads from state
user_id— the identity to send the token tochannel— the delivery channel; falls back to the block's config if not in state
Writes to state
step.<slug>.verification_token— the plaintext token dispatched to the userstep.<slug>.verification_token_expires— ISO 8601 timestamp after which the token is invalidstep.<slug>.verification_channel— the channel used:emailorsms
Use when — after Create Identity in a Registration flow, or at the start of a Password Recovery flow before the user supplies the token.
Cautions — If the courier is unconfigured or the send fails, the block is marked fail-open by default, meaning the flow continues. Check your courier configuration under Tenant settings → Notifications before publishing a flow that depends on this block.
Check Verification
Section titled “Check Verification”Block id: verification_check · Available in: registration, login
Verify a user-supplied token against the stored hash written by an earlier Send Verification block. The block sets verified: true on match and can deny the flow on mismatch — place a Decision block after it if you want to offer retry attempts rather than an immediate hard stop.
On admin-created accounts, verification is typically bypassed. The editor exposes a "Skip verification check" toggle that the platform enables by default on admin-create paths, so users Cymmetri's ops team imports programmatically don't get stuck on a verification gate.
Reads from state
verification_token— the token the user supplied in the UIexpected_token— the stored token value from the earlier Send Verification output
Writes to state
step.<slug>.verified—truewhen the user-supplied token matched the stored hash
Use when — as the second half of the send-and-confirm pattern. Always place it after Send Verification, with the user's input wired into verification_token.
Validate Refresh Token
Section titled “Validate Refresh Token”Block id: refresh_token_validate · Available in: token_refresh
Validate a refresh token at the token-refresh boundary. The block calls the OAuth2 introspect endpoint and confirms the token is active, unexpired, and unrevoked. If validation fails, the block denies the flow — the caller must send the user back through a full login.
This block is the entry point of the token_refresh trigger, which is a specialised flow type separate from Login and Registration. You will not add it to a Login flow; it belongs to the dedicated Refresh flow that the platform automatically routes /auth/refresh requests through.
Reads from state
session.refresh_token— the refresh token presented by the client
Writes to state
step.<slug>.refresh_token_valid—truewhen the token is active and unrevokedstep.<slug>.refresh_token_subject— the user ID extracted from the token's subject claim
Use when — you have a custom Refresh flow and need an explicit validation gate before re-issuing tokens.
Email Verification
Section titled “Email Verification”Block id: email_verification · Available in: login, registration, password_recovery, mfa_enroll, mfa_step_up
A unified alias over the Send Verification and Check Verification pair. Configure mode to send to dispatch the token, or mode to check to confirm the user's input. Using this block instead of the individual pair keeps the flow graph cleaner when the send and check steps are in the same stage.
The outputs follow the same state contract as the underlying blocks: verification_token and verification_token_expires when sending; verified when checking. Both modes are auditable and audit-redact the email address.
Reads from state
user_id— the identity to send to or verify againstverification_token— the user-supplied token (check mode only)
Writes to state
step.<slug>.verification_token— the plaintext token (send mode)step.<slug>.verification_token_expires— ISO 8601 expiry (send mode)step.<slug>.verified—truewhen token matched (check mode)
Use when — you want a single block to represent the full email-confirmation responsibility in a stage, or when send and check happen in the same stage without a user round-trip between them.
Update User
Section titled “Update User”Block id: update_user · Available in: login, registration, password_recovery, mfa_enroll, mfa_step_up
A streamlined alias over Update Profile, scoped to the current user in the flow. Where Update Profile accepts explicit trait_overlay and verified_overlay inputs wired from state, Update User reads its overlay from the block's config directly. This makes it convenient for flows where the traits to write are known at flow-design time — for example, setting onboarded: true on first login — rather than being computed at runtime.
Like Update Profile, the block does not roll back on downstream failure: the trait write is visible immediately and persists.
Reads from state
user_id— the current user, typically from an earlier Look Up Identity
Writes to state
step.<slug>.updated—truewhen the trait overlay was appliedstep.<slug>.identity_traits— the updated trait object as stored
Use when — you want to stamp a known field value onto the current user mid-flow, such as updating a last_login_method metadata field or marking a profile step as complete.
Compositions
Section titled “Compositions”Signup flow
Section titled “Signup flow”A typical Registration flow uses this sequence in the pre-registration and post-registration stages:
pre-registration 1. Email Validation — rejects disposable or malformed addresses 2. Look Up Identity — must_not_exist: true (collision guard) 3. Create Identity — mints the account 4. Send Verification — channel: email
(user receives email; enters token)
post-registration 5. Check Verification — confirms the token 6. Issue Session — terminal; hands the user a session JWTThe two stages may run in separate Flow invocations if the UI collects the verification code on a separate screen. The stage token carries state across both /submit calls.
Password recovery flow
Section titled “Password recovery flow”password-reset 1. Look Up Identity — must_exist: true 2. Send Verification — channel: email
(user receives recovery link; clicks it)
3. Check Verification — confirms the recovery token 4. credential_set (password) — sets the new password 5. Issue Session — terminalThe Look Up Identity step must run even if no match is found, and the Send Verification step must appear to run even when the address is unregistered — both to honour anti-enumeration discipline. Use the default fail-open behaviour and a Delay block to equalise timing across branches.