email field and stamps emailVerified on a successful verify.
The OAuth and SSO sign-in paths stamp emailVerified automatically, because the upstream IdP already verified the email. Use this flow when the IdP did not verify the email (manual signup, edit-email flow, etc.).
Endpoints
Schema
The User entity needs anemailVerified field. Pylon writes the current ISO 8601 timestamp on success:
update rejects the unknown column and /email/verify returns 500 PERSIST_FAILED (with a hint to add an emailVerified datetime field to the User entity) instead of silently succeeding.
Sending a verification code
email, creates a 6-digit code, and sends an email with subject "Verify your email address" through the configured email transport. Body: "Your email verification code is: <code>\n\nThis code will expire in 10 minutes.".
Response in production:
PYLON_DEV_MODE=true):
The throttle is shared with magic-code sends: 1 code per email per minute. The 10-minute TTL is shared too.
Verifying
emailVerified to the current ISO 8601 timestamp and echoes it back in the response.
Errors:
Gating handlers on emailVerified
In your TS code:
emailVerified from the User entity and reference it directly:
auth.emailVerified to the policy DSL today. The field lives on your User row. You reference it through your own handlers or queries.
Security guarantees
- Code generation, comparison, throttling shared with magic-codes — 6-digit numeric, 10-minute TTL, burn-after-5-wrong-attempts, constant-time comparison.
- Session-gated — only the authenticated user can request and verify their own email. There is no admin override. To mark an email verified from another process, write the User row directly with admin auth.
- ISO 8601 timestamp format —
pylon_kernel::util::now_iso()produces2026-01-15T10:30:00Z. An email is verified only whenemailVerifiedis non-null.
Where to go next
- Magic codes — the same 6-digit code primitive as a primary sign-in flow
- Password — the register flow that produces an unverified email