---
title: Email Verification
description: "Elements Version 3.8+ Elements supports email-based identity verification for [User](https://namazustudios.com/glossary/user/) accounts. When a [User](https://namazustudios.com/glossary/user/)'s email address is linked to their account via the `email` scheme, the address starts in the `UNVERIFIED` state. The verification flow confirms that the [User](https://namazustudios.com/glossary/user/) controls that address. --- ## Verification Lifecycle ``` UNVERIFIED…"
url: "https://namazustudios.com/docs/namazu-elements-core/user-authentication-sign-in/email-verification/"
updated: "2026-08-13"
category: Namazu Elements Core Features / User Authentication / Sign In
---

# Email Verification

Elements Version 3.8+

Elements supports email-based identity verification for [User](https://namazustudios.com/glossary/user/) accounts. When a [User](https://namazustudios.com/glossary/user/)'s email address is linked to their account via the `email` scheme, the address starts in the `UNVERIFIED` state. The verification flow confirms that the [User](https://namazustudios.com/glossary/user/) controls that address.

---

## Verification Lifecycle

```
UNVERIFIED
    │
    │  POST /user/me/email/verify
    │  (EmailVerificationService.requestVerification)
    ▼
PENDING
    │
    │  GET /verify?token=<token>
    │  (EmailVerificationService.completeVerification)
    ▼
VERIFIED
```

| Status | Meaning |
| --- | --- |
| `UNVERIFIED` | Default state for a newly created email UID. |
| `PENDING` | Verification email has been sent; awaiting the [User](https://namazustudios.com/glossary/user/) to click the link. |
| `VERIFIED` | [User](https://namazustudios.com/glossary/user/) has clicked the link and confirmed ownership of the address. |

UIDs created via [OIDC](https://namazustudios.com/glossary/oidc/) or [OAuth2](https://namazustudios.com/glossary/oauth2/) providers are set to `VERIFIED` immediately - the external provider is the trusted verifier.

---

## [REST](https://namazustudios.com/glossary/rest/) Endpoints

### Request verification

```
POST /user/me/email/verify
Authorization: Bearer <session-secret>
Content-Type: application/json

{
  "email": "user@example.com"
}
```

Sends a verification link to the given address if it is linked to the authenticated [User](https://namazustudios.com/glossary/user/)'s account. Moves the UID status from `UNVERIFIED` to `PENDING`. Returns the updated `UserUid`.

**Responses**

| Status | Condition |
| --- | --- |
| `200` | Verification email sent; UID is now `PENDING`. |
| `400` | SMTP is not configured (see [Email Service](file:///Users/keithhudnall/Documents/workspace/Elements/web-services/docs/api/email.md)). |
| `403` | Not authenticated, or the email does not belong to the current [User](https://namazustudios.com/glossary/user/). |
| `404` | The email address is not linked to the account. |

---

### Complete verification

```
GET /verify?token=<token>
```

Public endpoint - no authentication required. Consumes the single-use token from the email link and moves the UID status from `PENDING` to `VERIFIED`. Returns the updated `UserUid`.

**Responses**

| Status | Condition |
| --- | --- |
| `200` | Token accepted; UID is now `VERIFIED`. |
| `404` | Token is unknown, already used, or expired (24-hour TTL). |

---

## Security model

- **Single-use**: the token is deleted immediately on first successful use. Clicking the link twice returns `404`.
- **Time-limited**: tokens expire after **24 hours**. MongoDB removes expired token documents automatically via a TTL index on the `expiry` field.
- **Cryptographically random**: the token is a UUID v4 generated by `SecureRandom` (122 bits of entropy). A database-native `ObjectId` was deliberately avoided because it encodes a timestamp, machine [id](https://namazustudios.com/glossary/id/), and counter, making it partially predictable.

---

## [Service](https://namazustudios.com/glossary/service/) interface

```
package dev.getelements.elements.sdk.service.user;

public interface EmailVerificationService {

    /** Fired on requestVerification; carries the updated UserUid (status PENDING). */
    String EMAIL_VERIFICATION_REQUESTED_EVENT = "dev.getelements.email_verification.requested";

    /** Fired on completeVerification; carries the updated UserUid (status VERIFIED). */
    String EMAIL_VERIFICATION_COMPLETED_EVENT = "dev.getelements.email_verification.completed";

    UserUid requestVerification(String email, String verificationBaseUrl);

    UserUid completeVerification(String token);
}
```

---

## [Element](https://namazustudios.com/glossary/element/) attributes

| Attribute key | Default | Description |
| --- | --- | --- |
| `dev.getelements.elements.verification.base_url` | *(blank - derived from request)* | Override the base URL embedded in the link, e.g. when sitting behind a reverse proxy. |
| `dev.getelements.elements.verification.email_subject` | `Verify your email` | Subject line for the verification email. |
| `dev.getelements.elements.verification.email_template` | *(inline link - see below)* | Full HTML body. Must contain `{link}` as the placeholder for the verification URL. |

### Default email template

If not defined, the email body will default to this template:

```
<p>Please verify your email address by clicking the link below:</p>
<p><a href="{link}">Verify Email</a></p>
```

### Custom template example

Override `VERIFICATION_EMAIL_TEMPLATE` in your [Element](https://namazustudios.com/glossary/element/)'s [Guice](https://namazustudios.com/glossary/guice/) module or via [Element](https://namazustudios.com/glossary/element/) attributes:

```
public class MyGameElementModule extends AbstractModule {
    @Override
    protected void configure() {
        bindConstant()
            .annotatedWith(Names.named(EmailVerificationService.VERIFICATION_EMAIL_TEMPLATE))
            .to("<html><body>"
              + "<h1>Confirm your email</h1>"
              + "<p><a href=\"{link}\">Click here to verify</a></p>"
              + "</body></html>");
    }
}
```

The `{link}` token is always replaced with the full verification URL before the email is sent.

---

## Using EmailVerificationService in custom Elements

`EmailVerificationService` is exported to [Element](https://namazustudios.com/glossary/element/) child injectors. Inject it to trigger verification programmatically on behalf of any [User](https://namazustudios.com/glossary/user/):

```
import dev.getelements.elements.sdk.service.user.EmailVerificationService;
import jakarta.inject.Inject;
import jakarta.inject.Named;

import static dev.getelements.elements.sdk.service.Constants.UNSCOPED;

public class MyOnboardingService {

    private EmailVerificationService emailVerificationService;

    public void sendVerification(String email) {
        // UNSCOPED skips the current-user ownership check
        emailVerificationService.requestVerification(email, "https://mygame.com/api/rest/verify");
    }

    @Inject
    public void setEmailVerificationService(@Named(UNSCOPED) EmailVerificationService emailVerificationService) {
        this.emailVerificationService = emailVerificationService;
    }
}
```

**Note:** The `@Named(UNSCOPED)` qualifier is needed when calling `requestVerification` from [Element](https://namazustudios.com/glossary/element/) code on behalf of any [User](https://namazustudios.com/glossary/user/). Without it, the [User](https://namazustudios.com/glossary/user/)-scoped [Service](https://namazustudios.com/glossary/service/) is used, which checks that the email belongs to the current [Session](https://namazustudios.com/glossary/session/) [User](https://namazustudios.com/glossary/user/).

---

## Prerequisites

Email sending requires SMTP to be configured at the platform level. See [Email Service](file:///Users/keithhudnall/Documents/workspace/Elements/web-services/docs/api/email.md) for the full configuration reference.

---

## Events

Both events carry a `UserUid` argument accessible from [Element](https://namazustudios.com/glossary/element/) event listeners.

```
@ElementEventConsumer(EmailVerificationService.EMAIL_VERIFICATION_COMPLETED_EVENT)
public void onEmailVerificationComplete(final UserUid uid) {     // uid.getVerificationStatus() == VerificationStatus.VERIFIED
}
```
