> **Building with AI coding agents?** If you're using an AI coding agent, install the official Scalekit plugin. It gives your agent full awareness of the Scalekit API — reducing hallucinations and enabling faster, more accurate code generation.
>
> - **Claude Code**: `/plugin marketplace add scalekit-inc/claude-code-authstack` then `/plugin install <auth-type>@scalekit-auth-stack`
> - **GitHub Copilot CLI**: `copilot plugin marketplace add scalekit-inc/github-copilot-authstack` then `copilot plugin install <auth-type>@scalekit-auth-stack`
> - **Codex**: run the bash installer, restart, then open Plugin Directory and enable `<auth-type>`
> - **Skills CLI** (Windsurf, Cline, 40+ agents): `npx skills add scalekit-inc/skills --list` then `--skill <skill-name>`
>
> `<auth-type>` / `<skill-name>`: `agentkit`, `full-stack-auth`, `mcp-auth`, `modular-sso`, `modular-scim` — [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Headless email API for magic link and OTP

Implement email OTP or magic link using direct API calls with full control over UX
This guide shows you how to implement magic link and OTP authentication using Scalekit's headless APIs. You send either a one-time passcode (OTP) or a magic link to the user's email and then verify their identity. Magic link and OTP offer two email-based authentication methods—clickable links or one-time passcodes—so users can sign in without passwords. You control the UI and user flows, while Scalekit provides the backend authentication infrastructure.

##  See the integration in action

##  Review the authentication sequence

Coming soon

### [Build with a coding agent](/dev-kit/build-with-ai/full-stack-auth/)

### Claude Code

```bash title="Claude REPL" showLineNumbers=false frame="none"
/plugin marketplace add scalekit-inc/claude-code-authstack
```
```bash title="Claude REPL" showLineNumbers=false frame="none"
/plugin install full-stack-auth@scalekit-auth-stack
```

   ### GitHub Copilot CLI

```bash title="Terminal" showLineNumbers=false frame="none"
copilot plugin marketplace add scalekit-inc/github-copilot-authstack
```
```bash title="Terminal" showLineNumbers=false frame="none"
copilot plugin install full-stack-auth@scalekit-auth-stack
```

   ### 40+ agents

```bash title="Terminal" showLineNumbers=false frame="none"
npx skills add scalekit-inc/skills --skill implementing-scalekit-fsa
```

  

  [Continue building with AI →](/dev-kit/build-with-ai/full-stack-auth/)

-------

1. ## Set up Scalekit

   Install the Scalekit SDK to your project.

   ### Node.js

```bash showLineNumbers=false frame="none"
npm install @scalekit-sdk/node
```

   ### Python

```sh showLineNumbers=false frame="none"
pip install scalekit-sdk-python
```

  ### Go

```sh showLineNumbers=false frame="none"
go get -u github.com/scalekit-inc/scalekit-sdk-go
```

   ### Java

```groovy showLineNumbers=false frame="none"
/* Gradle users - add the following to your dependencies in build file */
implementation "com.scalekit:scalekit-sdk-java:2.0.11"
```

```xml showLineNumbers=false frame="none"
<!-- Maven users - add the following to your `pom.xml` -->
<dependency>
    <groupId>com.scalekit</groupId>
    <artifactId>scalekit-sdk-java</artifactId>
    <version>2.0.11</version>
</dependency>
```

   Your application is responsible for verifying users and initiating sessions, while Scalekit securely manages authentication tokens to ensure the verification process is completed successfully

2. ## Configure magic link and OTP settings

   In the Scalekit dashboard, enable magic link and OTP and choose your login method.

   Optional security settings:
   - **Enforce same-browser origin**: Users must complete magic-link auth in the same browser they started in.
   - **Issue new credentials on resend**: Each resend generates a fresh code or link and invalidates the previous one.

    > Image: Screenshot

3. ## Send verification email

    The first step in the magic link and OTP flow is to send a verification email to the user's email address. This email contains either a **one-time passcode (OTP), a magic link, or both** based on your selection in the Scalekit dashboard.

    Follow these steps to implement the verification email flow:

    1. Create a form to collect the user's email address
    2. Call the passwordless API (magic link and OTP) when the form is submitted
    3. Handle the response to provide feedback to the user

    ```http showLineNumbers=false title="API endpoint"
    POST /api/v1/passwordless/email/send
    ```

    **Example implementation**

    
   ### cURL

```sh {11} "/api/v1/passwordless/email/send" title="Send a verification code to user's email" { "Only if Link or Link+OTP as authentication method": 9} collapse={17-22}
      curl -L '/api/v1/passwordless/email/send' \
      -H 'Content-Type: application/json' \
      -H 'Authorization: Bearer eyJh..' \
      --data-raw '{
          "email": "john.doe@example.com",
          "expires_in": 300,
          "state": "jAy-state1-gM4fdZ...2nqm6Q",
          "template": "SIGNIN",

          "magiclink_auth_uri": "https://yourapp.com/passwordless/verify",
          "template_variables": {
            "custom_variable_key": "custom_variable_value"
          }
      }'

    # Response
    # {
    # "auth_request_id": "jAy-state1-gM4fdZ...2nqm6Q"
    # "expires_at": "1748696575"
    # "expires_in": 100
    # "passwordless_type": "OTP" | "LINK" | "LINK_OTP"
    # }

    ```
    ## Request parameters

| Parameter  | Required | Description |
      |------------|----------|-------------|
      | `email` | Yes | Recipient's email address  |
      | `expires_in` | No | Code expiration time in seconds (default: 300)  |
      | `state` | No | OIDC state parameter for request validation  |
      | `template` | No | Email template to use (`SIGNIN` or `SIGNUP`)  |
      | `magiclink_auth_uri` | No | Magic Link URI that will be sent to your user to complete the authentication flow. If the URL is of the format `https://yourapp.com/passwordless/verify`, the magic link sent to your user via email will be `https://yourapp.com/passwordless/verify?link_token=<link_token>`. Required if you selected Link or Link + OTP as your authentication method. |
      | `template_variables` | No | Pass variables to be used in the email template sent to the user. You may include up to 30 key-value pairs to reference in the email template.  |

    ## Response parameters

| Parameters | Description |
      |-----------|-------------|
      | `auth_request_id` | A unique identifier for the authentication request that can be used to verify the code  |
      | `expires_at` | Unix timestamp indicating when the verification code will expire  |
      | `expires_in` | The time in seconds after which the verification code will expire. Default is 100 seconds  |
      | `passwordless_type` | The type of magic link and OTP authentication. Currently supports `OTP`, `LINK` and `LINK_OTP`  |

   ### Node.js

```js wrap
    const options = {
      template: "SIGNIN",
      state: "jAy-state1-...2nqm6Q",
      expiresIn: 300,
      // Required if you selected Link or Link+OTP as your authentication method
      magiclinkAuthUri: "https://yourapp.com/passwordless/verify",
        templateVariables: {
            employeeID: "EMP523",
            teamName: "Alpha Team",
        },
    };

    const sendResponse = await scalekit.passwordless
      .sendPasswordlessEmail(
        "<john.doe@example.com>",
        options
    );

    // sendResponse = {
    //   authRequestId: string,
    //   expiresAt: number,    // seconds since epoch
    //   expiresIn: number,    // seconds
    //   passwordlessType: string  // "OTP" | "LINK" | "LINK_OTP"
    // }

    ```

     ## Request parameters

| Parameter | Required | Description |
      |-----------|----------|-------------|
      | `email` | Yes | The email address to send the magic link or OTP verification code to  |
      | `template` | No | The template type (`SIGNIN`/`SIGNUP`)  |
      | `state` | No | Optional state parameter to maintain state between request and callback  |
      | `expiresIn` | No | Optional expiration time in seconds (default: 300)  |
      | `magiclinkAuthUri` | No | Magic Link URI that will be sent to your user to complete the authentication flow. If the URL is of the format `https://yourapp.com/passwordless/verify`, the magic link sent to your user via email will be `https://yourapp.com/passwordless/verify?link_token=<link_token>`. Required if you selected Link or Link + OTP as your authentication method. |
      | `template_variables` | No | Pass variables to be used in the email template sent to the user. You may include up to 30 key-value pairs to reference in the email template.  |

    ## Response parameters

| Parameters | Description |
      |-----------|-------------|
      | `authRequestId` | Unique identifier for the magic link and OTP authentication request  |
      | `expiresAt` | Expiration time in seconds since epoch  |
      | `expiresIn` | Expiration time in seconds  |
      | `passwordlessType` | Type of magic link and OTP authentication (`OTP`, `LINK` or `LINK_OTP`)  |

   ### Python

```python
response = client.passwordless.send_passwordless_email(
    email="john.doe@example.com",
    template="SIGNIN",  # or "SIGNUP", "UNSPECIFIED"
    expires_in=300,
    magiclink_auth_uri="https://yourapp.com/passwordless/verify",
    template_variables={
        "employeeID": "EMP523",
        "teamName": "Alpha Team",
    },
)

# Extract auth request ID from response
auth_request_id = response[0].auth_request_id
```

   ### Go

```go
    // Send a passwordless email (assumes you have an initialized `client` and `ctx`)
    templateType := scalekit.TemplateTypeSignin
    resp, err := scalekitClient.Passwordless().SendPasswordlessEmail(
      ctx,
      "john.doe@example.com",
      &scalekit.SendPasswordlessOptions{
        Template:          &templateType,
        State:             "jAy-state1-gM4fdZ...2nqm6Q",
        ExpiresIn:         300,
        MagiclinkAuthUri:  "https://yourapp.com/passwordless/verify", // required if Link or Link+OTP
        TemplateVariables: map[string]string{
          "employeeID": "EMP523",
          "teamName": "Alpha Team",
        },
      },
    )

    // resp contains: AuthRequestId, ExpiresAt, ExpiresIn, PasswordlessType
    ```
    ## Request parameters

| Parameter | Required | Description |
      |-----------|----------|-------------|
      | `email` | Yes | The email address to send the magic link or OTP verification code to  |
      | `MagiclinkAuthUri` | No | Magic Link URI for authentication  |
      | `State` | No | Optional state parameter  |
      | `Template` | No | Email template type (`SIGNIN`/`SIGNUP`)  |
      | `ExpiresIn` | No | Expiration time in seconds  |
      | `TemplateVariables` | No | Key-value pairs for email template  |

    ## Response parameters

| Parameters | Description |
      |-----------|-------------|
      | `AuthRequestId` | Unique identifier for the magic link and OTP authentication request  |
      | `ExpiresAt` | Expiration time in seconds since epoch  |
      | `ExpiresIn` | Expiration time in seconds  |
      | `PasswordlessType` | Type of magic link and OTP authentication (`OTP`, `LINK` or `LINK_OTP`)  |

   ### Java

```java
import java.util.HashMap;
import java.util.Map;

TemplateType templateType = TemplateType.SIGNIN;
Map templateVariables = new HashMap<>();
templateVariables.put("employeeID", "EMP523");
templateVariables.put("teamName", "Alpha Team");

SendPasswordlessOptions options = new SendPasswordlessOptions();
options.setTemplate(templateType);
options.setExpiresIn(300);
options.setMagiclinkAuthUri("https://yourapp.com/passwordless/verify");
options.setTemplateVariables(templateVariables);

SendPasswordlessResponse response = passwordlessClient.sendPasswordlessEmail(
    "john.doe@example.com",
    options
);

String authRequestId = response.getAuthRequestId();
```

    

3. ### Resend a verification email

    Users can request a new verification email if they need one. Use the following endpoint to resend an OTP or magic link email.

    
   ### cURL

```sh ins={5} "/api/v1/passwordless/email/resend" title="Request" wrap
    curl -L '/api/v1/passwordless/email/resend' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm..' \
    -d '{
        "auth_request_id": "jAy-state1-gM4fdZ...2nqm6Q"
    }'

   # Response

   # {

   # "auth_request_id": "jAy-state1-gM4fdZ...2nqm6Q"

   # "expires_at": "1748696575"

   # "expires_in": 300

   # "passwordless_type": "OTP" | "LINK" | "LINK_OTP"

   # }

    ```

    ## Request parameters

| Parameters | Required | Description |
      |-----------|----------|-------------|
      | `auth_request_id` | Yes | The unique identifier for the authentication request that was sent earlier  |

    ## Response parameters

| Parameters | Description |
      |-----------|-------------|
      | `auth_request_id` | A unique identifier for the authentication request that can be used to verify the code  |
      | `expires_at` | Unix timestamp indicating when the verification code will expire  |
      | `expires_in` | The time in seconds after which the verification code will expire. Default is 300 seconds  |
      | `passwordless_type` | The type of magic link and OTP authentication. Currently supports `OTP`, `LINK` and `LINK_OTP`  |

   ### Node.js

```js
    const { authRequestId } = sendResponse;
    const resendResponse = await scalekit.passwordless
    .resendPasswordlessEmail(
        authRequestId
    );

    // resendResponse = {
    //   authRequestId: "jAy-state1-gM4fdZ...2nqm6Q",
    //   expiresAt: "1748696575",
    //   expiresIn: "300",
    //   passwordlessType: "OTP" | "LINK" | "LINK_OTP"
    // }
    ```

    ## Request parameters

| Parameters | Required | Description |
      |-----------|----------|-------------|
      | `authRequestId` | Yes | The unique identifier for the authentication request that was sent earlier  |

    ## Response parameters

| Parameters | Description |
      |-----------|-------------|
      | `authRequestId` | Unique identifier for the magic link and OTP authentication request  |
      | `expiresAt` | Expiration time in seconds since epoch  |
      | `expiresIn` | Expiration time in seconds. Default is 300 seconds  |
      | `passwordlessType` | `OTP`, `LINK` or `LINK_OTP`  |

   ### Python

```python
resend_response = client.passwordless.resend_passwordless_email(
    auth_request_id=auth_request_id,
)

new_auth_request_id = resend_response[0].auth_request_id
```

   ### Go

```go
    // Resend passwordless email for an existing auth request
    resendResp, err := scalekitClient.Passwordless().ResendPasswordlessEmail(
      ctx,            // context.Context (e.g., context.Background())
      authRequestId,  // string: from the send email response
    )

    if err != nil {
      // handle error (log, return HTTP 400/500, etc.)
      // ...
    }

    // resendResp is a pointer to ResendPasswordlessResponse struct:
    // type ResendPasswordlessResponse struct {
    //   AuthRequestId    string // Unique ID for the passwordless request
    //   ExpiresAt        int64  // Unix timestamp (seconds since epoch)
    //   ExpiresIn        int    // Expiry duration in seconds
    //   PasswordlessType string // "OTP", "LINK", or "LINK_OTP"
    // }
    ```

    ## Request parameters

| Parameters | Required | Description |
      |-----------|----------|-------------|
      | `authRequestId` | Yes | The unique identifier for the authentication request that was sent earlier  |

    ## Response parameters

| Parameters | Description |
      |-----------|-------------|
      | `AuthRequestId` | Unique identifier for the magic link and OTP authentication request  |
      | `ExpiresAt` | Expiration time in seconds since epoch  |
      | `ExpiresIn` | Expiration time in seconds. Default is 300 seconds  |
      | `PasswordlessType` | `OTP`, `LINK` or `LINK_OTP`  |

   ### Java

```java wrap showLineNumbers=false
SendPasswordlessResponse resendResponse = passwordlessClient.resendPasswordlessEmail(authRequestId);
```

    

    If you enabled **Enable new Magic link & OTP credentials on resend** in the Scalekit dashboard, a new verification code or magic link will be sent each time the user requests a new one.

    > caution: Rate limits
>
> Scalekit enforces a rate limit of 2 magic link and OTP emails per minute per email address. This limit includes both initial sends and resends.

4. ### Verify the user's identity

    Once the user receives the verification email,
    - If it is a verification code, they'll enter it in your application. Use the following endpoint to validate the code and complete authentication.
    - If it is a magic link, they'll click the link in the email to verify their address. Capture the `link_token` query parameter and use it to verify.
    - For additional security with magic links, if you enabled "Enforce same browser origin" in the dashboard, include the `auth_request_id` in the verification request.

    
     ### Verification code

         1. Create a form to collect the verification code
         2. Call the verification API when the form is submitted to verify the code
         3. Handle the response to either grant access or show an error
       

       ```http showLineNumbers=false title="API endpoint"
       POST /api/v1/passwordless/email/verify
       ```

       **Example implementation**

       
         
           ```sh ins={5,6} "/api/v1/passwordless/email/verify" title="Request" showLineNumbers=false wrap
           curl -L '/api/v1/passwordless/email/verify' \
           -H 'Content-Type: application/json' \
           -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm..' \
           -d '{
               "code": "123456",
               "auth_request_id": "YC4QR-dVZVtNNVHcHwrnHNDV..."
           }'
           ```
           ## Request parameters

| Parameters | Required | Description |
             |-----------|----------|-------------|
             | `code` | Yes | The verification code entered by the user  |
             | `auth_request_id` | Yes | The request ID from the response when the verification email was sent  |

           ## Response parameters

| Parameters | Description |
             |-----------|-------------|
             | `email` | The email address of the user  |
             | `state` | The state parameter that was passed in the original request  |
             | `template` | The template that was used for the verification code  |
            | `passwordless_type` | The type of magic link and OTP authentication. Currently supports `OTP`, `LINK` and `LINK_OTP`  |

         ### Node.js

```js wrap
           const { authRequestId } = sendResponse;
           const verifyResponse = await scalekit.passwordless
             .verifyPasswordlessEmail(
               { code: "123456"},
               authRequestId
             );

           // verifyResponse = {
           //   "email": "saifshine7@gmail.com",
           //   "state": "jAy-state1-gM4fdZdV22nqm6Q_j..",
           //   "template": "SIGNIN",
           //   "passwordless_type": "OTP" | "LINK" | "LINK_OTP"
           // }
           ```
           ## Request parameters

| Parameters | Required | Description |
             |-----------|----------|-------------|
             | `options.code` | Yes | The verification code received by the user  |
             | `authRequestId` | Yes | The unique identifier for the authentication request that was sent earlier  |

           ## Response parameters

| Parameters | Description |
             |-----------|-------------|
             | `email` | The email address of the user  |
             | `state` | The state parameter that was passed in the original request  |
             | `template` | The template that was used for the verification code  |
            | `passwordlessType` | The type of magic link and OTP authentication. Currently supports `OTP`, `LINK` and `LINK_OTP`  |

         ### Python

```python
verify_response = client.passwordless.verify_passwordless_email(
    code="123456",  # OTP code received via email
    auth_request_id=auth_request_id,
)

# User verified successfully
user_email = verify_response[0].email
```

           ### Go

```go
             // Verify with OTP code
             verifyResponse, err := scalekitClient.Passwordless().VerifyPasswordlessEmail(
                 ctx,
                 &scalekit.VerifyPasswordlessOptions{
                     Code:          "123456", // OTP code
                     AuthRequestId: authRequestId,
                 },
             )

             if err != nil {
                 // Handle error
                 return
             }

             // verifyResp contains the verified user's info
             // type VerifyPasswordLessResponse struct {
             //   Email            string
             //   State            string
             //   Template         string // SIGNIN | SIGNUP
             //   PasswordlessType string // OTP | LINK | LINK_OTP
             // }
             ```

             ## Request parameters

| Parameters | Required | Description |
               |-----------|----------|-------------|
               | `options.Code` | Yes | The verification code received by the user  |
               | `options.AuthRequestId` | Yes | The unique identifier for the authentication request that was sent earlier  |

             ## Response parameters

| Parameters | Description |
               |-----------|-------------|
               | `Email` | The email address of the user  |
               | `State` | The state parameter that was passed in the original request  |
               | `Template` | The template that was used (`SIGNIN` or `SIGNUP`)  |
               | `PasswordlessType` | `OTP`, `LINK` or `LINK_OTP`  |

           ### Java

```java
// Verify with OTP code
VerifyPasswordlessOptions verifyOptions = new VerifyPasswordlessOptions();
verifyOptions.setCode("123456"); // OTP code
verifyOptions.setAuthRequestId(authRequestId);

VerifyPasswordLessResponse verifyResponse = passwordlessClient.verifyPasswordlessEmail(verifyOptions);

// User verified successfully
String userEmail = verifyResponse.getEmail();
```

       
     
    ### Magic link verification

To support magic link verification, add a callback endpoint in your application typically at <code>https://your-app.com/passwordless/verify</code>. Implement it to verify the magic link token and complete the user authentication process.

       
         1. Create a verification endpoint in your application to handle the magic link verification. This is the endpoint that the user lands in when they click the link in the email.
         2. Capture the magic link token from the `link_token` request parameter from the URL.
         3. Call the verification API when the user clicks the link in the email.
         4. Based on token verification, complete the authentication process or show an error with an appropriate error message.
       

       ```http showLineNumbers=false title="API endpoint"
       POST /api/v1/passwordless/email/verify
       ```

       **Example implementation**

       
         
           ```sh ins={5,6} "/api/v1/passwordless/email/verify" title="Request" showLineNumbers=false
           curl -L '/api/v1/passwordless/email/verify' \
           -H 'Content-Type: application/json' \
           -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsIm..' \
           -d '{
               "link_token": "a4143d8f-...c846ed91e_l",
               "auth_request_id": "YC4QR-dVZVtNNVHcHwrnHNDV..." // (optional)
           }'
           ```
           ## Request parameters

| Parameters | Required | Description |
             |-----------|----------|-------------|
             | `link_token` | Yes | The link token received by the user  |
             | `auth_request_id` | No | The request ID you received when the verification email was sent.  |

             > caution: Auth request ID
>
> If you use Magic Link or Magic Link & OTP and have enabled same browser origin enforcement in the Scalekit dashboard, it is required to include the auth request ID in your request.

           ## Response parameters

| Parameters | Description |
             |-----------|-------------|
             | `email` | The email address of the user  |
             | `state` | The state parameter that was passed in the original request  |
             | `template` | The template that was used for the verification code  |
            | `passwordless_type` | The type of magic link and OTP authentication. Currently supports `OTP`, `LINK` and `LINK_OTP`  |

         ### Node.js

```js wrap showLineNumbers collapse={15-21}
           // User clicks the magic link in their email
           // Example magic link: https://yourapp.com/passwordless/verify?link_token=a4143d8f-d13d-415c-8f5a-5a5c846ed91e_l

           // 2. Express endpoint to handle the magic link verification
           app.get('/passwordless/verify', async (req, res) => {
             const { link_token } = req.query;

             try {
               // 3. Verify the magic link token with Scalekit
               const verifyResponse = await scalekit.passwordless
                 .verifyPasswordlessEmail(
                   { linkToken: link_token },
                   authRequestId // (optional) sendResponse.authRequestId
                 );

               // 4. Successfully log the user in
               // Set session/token and redirect to dashboard
               res.redirect('/dashboard');
             } catch (error) {
               res.status(400).json({
                    error: 'The magic link is invalid or has expired. Please request a new verification link.'
                });
             }
           });

           // verifyResponse = {
           //   "email": "saifshine7@gmail.com",
           //   "state": "jAy-state1-gM4fdZdV22nqm6Q_j..",
           //   "template": "SIGNIN",
           //   "passwordless_type": "OTP" | "LINK" | "LINK_OTP"
           // }
           ```
           ## Request parameters

| Parameters | Required | Description |
             |-----------|----------|-------------|
             | `options.linkToken` | Yes | The link token received by the user  |
             | `authRequestId` | No | The unique identifier for the authentication request that was sent earlier.  |
            > caution: Auth request ID
>
> If you use Magic Link or Magic Link & OTP and have enabled same browser origin enforcement in the Scalekit dashboard, it is required to include the auth request ID in your request.

           ## Response parameters

| Parameters | Description |
             |-----------|-------------|
             | `email` | The email address of the user  |
             | `state` | The state parameter that was passed in the original request  |
             | `template` | The template that was used for the verification code  |
            | `passwordlessType` | The type of magic link and OTP authentication. Currently supports `OTP`, `LINK` and `LINK_OTP`  |

         ### Python

```python
# Verify with magic link token
verify_response = client.passwordless.verify_passwordless_email(
    link_token=link_token,  # Magic link token from URL
    # auth_request_id=auth_request_id,  # optional if same-origin enforcement enabled
)

# User verified successfully
user_email = verify_response[0].email
```

         ### Go

```go
verifyResponse, err := scalekitClient.Passwordless().VerifyPasswordlessEmail(
    ctx,
    &scalekit.VerifyPasswordlessOptions{
        LinkToken: linkToken, // Magic link token
    },
)

if err != nil {
    // Handle error
    return
}

// User verified successfully
userEmail := verifyResponse.Email
```

         ### Java

```java
// Verify with magic link token
VerifyPasswordlessOptions verifyOptions = new VerifyPasswordlessOptions();
verifyOptions.setLinkToken(linkToken); // Magic link token
// verifyOptions.setAuthRequestId(authRequestId); // optional if same-origin enforcement enabled

VerifyPasswordLessResponse verifyResponse = passwordlessClient.verifyPasswordlessEmail(verifyOptions);

// User verified successfully
String userEmail = verifyResponse.getEmail();
```

       
     
    

   > caution: Validation attempt limits
>
> To protect your application, Scalekit allows a user only **five** attempts to enter the correct OTP within a ten-minute window.
> If the user exceeds this limit for an <code>auth_request_id</code>, the <code>/passwordless/email/verify</code> endpoint returns an **HTTP&nbsp;429&nbsp;Too&nbsp;Many&nbsp;Requests** error.
> To continue, the user must restart the authentication flow.

You've successfully implemented Magic link & OTP authentication in your application. Users can now sign in securely without passwords by entering a verification code (OTP) or clicking a magic link sent to their email.


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
