---
title: Manual actions
description: See how you can directly generate email verification links and set emails as verified through manual actions.
sidebar:
  order: 40
---

## Overview

Although the **SuperTokens** covers the entire email verification process you can also intervene manually in the process.
The following page shows you what SDK methods you can use to adjust the verification flow. 

---

## Generate a link

You can use the backend SDK to generate the email verification link as shown below:

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import EmailVerification from "supertokens-node/recipe/emailverification";
import supertokens from "supertokens-node";

async function createEmailVerificationLink(recipeUserId: supertokens.RecipeUserId, email: string) {
  try {
    // Create an email verification link for the user
    const linkResponse = await EmailVerification.createEmailVerificationLink("public", recipeUserId, email);

    if (linkResponse.status === "OK") {
      console.log(linkResponse.link);
    } else {
      // user's email is already verified
    }
  } catch (err) {
    console.error(err);
  }
}
```
</Tab>
<Tab title="Go" value="go">
```go
import (
    "fmt"
    
    "github.com/supertokens/supertokens-golang/recipe/emailverification"
)

func main() {
    userID := "..."
    email := "..."

    // Create an email verification link for the user
    linkRes, err := emailverification.CreateEmailVerificationLink("public", userID, &email)
    if err != nil {
        // handle error
    }

    if linkRes.OK != nil {
        link := linkRes.OK.Link
        fmt.Println(link)

    } else {
        // user's email is already verified.
    }
}
```
</Tab>
<Tab title="Python" value="python">
<DependentContent group="python-io-style" label="I/O style">
<ContentOption title="Asyncio" value="asyncio">
```python
from supertokens_python.recipe.emailverification.asyncio import create_email_verification_link
from supertokens_python.recipe.emailverification.interfaces import CreateEmailVerificationLinkOkResult
from supertokens_python.types import RecipeUserId

async def create_link(recipe_user_id: RecipeUserId, email: str):
    # Create an email verification link for the user
    link_res = await create_email_verification_link("public", recipe_user_id, email)

    if isinstance(link_res, CreateEmailVerificationLinkOkResult):
        link = link_res.link
        print(link)
    else:
        print("user's email is already verified")
```
</ContentOption>
<ContentOption title="Syncio" value="syncio">
```python
from supertokens_python.recipe.emailverification.syncio import create_email_verification_link
from supertokens_python.recipe.emailverification.interfaces import CreateEmailVerificationLinkOkResult
from supertokens_python.types import RecipeUserId

def create_link(recipe_user_id: RecipeUserId, email: str):
    # Create an email verification link for the user
    link_res = create_email_verification_link("public", recipe_user_id, email)

    if isinstance(link_res, CreateEmailVerificationLinkOkResult):
        link = link_res.link
        print(link)
    else:
        print("user's email is already verified")
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

:::info[Multi Tenancy]

Notice that the first argument to the function call above is `"public"`. This refers to the default tenant ID that SuperTokens uses. It means that users belonging to the `"public"` tenant can only consume the generated email verification link.

If you are using the multi tenancy feature, you can pass in the `tenantId` that contains this user, which you can fetch by getting the user object for this `userId`.

Finally, the generated link uses the configured `websiteDomain` from the `appInfo` object (in `supertokens.init`), however, you can change the domain of the generated link to match that of the tenant ID.

:::

---

## Mark the email as verified

To manually mark an email as verified, you need to first create an email verification token for the user and then use the token to verify the user's email.

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import EmailVerification from "supertokens-node/recipe/emailverification";
import supertokens from "supertokens-node";

async function manuallyVerifyEmail(recipeUserId: supertokens.RecipeUserId) {
  try {
    // Create an email verification token for the user
    const tokenRes = await EmailVerification.createEmailVerificationToken("public", recipeUserId);

    // If the token creation is successful, use the token to verify the user's email
    if (tokenRes.status === "OK") {
      await EmailVerification.verifyEmailUsingToken("public", tokenRes.token);
    }
  } catch (err) {
    console.error(err);
  }
}
```
</Tab>
<Tab title="Go" value="go">
```go
import (
    "github.com/supertokens/supertokens-golang/recipe/emailverification"
)

func main() {
    userID := "..."
    // Create an email verification token for the user
    tokenRes, err := emailverification.CreateEmailVerificationToken("public", userID, nil)
    if err != nil {
        // handle error
    }

    // If the token creation is successful, use the token to verify the user's email
    if tokenRes.OK != nil {
        _, err := emailverification.VerifyEmailUsingToken("public", tokenRes.OK.Token)
        if err != nil {
            // handle error
        }
    }
}
```
</Tab>
<Tab title="Python" value="python">
<DependentContent group="python-io-style" label="I/O style">
<ContentOption title="Asyncio" value="asyncio">
```python
from supertokens_python.recipe.emailverification.asyncio import create_email_verification_token, verify_email_using_token
from supertokens_python.recipe.emailverification.interfaces import CreateEmailVerificationTokenOkResult
from supertokens_python.types import RecipeUserId

async def manually_verify_email(recipe_user_id: RecipeUserId):
    try:
        # Create an email verification token for the user
        token_res = await create_email_verification_token("public", recipe_user_id)

        # If the token creation is successful, use the token to verify the user's email
        if isinstance(token_res, CreateEmailVerificationTokenOkResult):
            await verify_email_using_token("public", token_res.token)
    except Exception as e:
        print(e)
```
</ContentOption>
<ContentOption title="Syncio" value="syncio">
```python
from supertokens_python.recipe.emailverification.syncio import create_email_verification_token, verify_email_using_token
from supertokens_python.recipe.emailverification.interfaces import CreateEmailVerificationTokenOkResult
from supertokens_python.types import RecipeUserId

def manually_verify_email(recipe_user_id: RecipeUserId):
    try:
        # Create an email verification token for the user
        token_res = create_email_verification_token("public", recipe_user_id)

        # If the token creation is successful, use the token to verify the user's email
        if isinstance(token_res, CreateEmailVerificationTokenOkResult):
            verify_email_using_token("public", token_res.token)
    except Exception as e:
        print(e)
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

:::info[Multi Tenancy]
Notice that the first argument of the function call above is `"public"`.
This refers to the `"public"` `tenantId` (which is the default `tenantId`).
In case you are using the multi tenancy feature, you can still pass in the `"public"` tenant ID here. Even if the user ID is not part of that tenant, you can pass it because the system creates and consumes the token in one shot.
:::

---

## Mark the email as unverified

To manually mark an email as unverified, you need to first retrieve the user's email address and then update their email verification status in the database.

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import EmailVerification from "supertokens-node/recipe/emailverification";
import supertokens from "supertokens-node";

async function manuallyUnverifyEmail(recipeUserId: supertokens.RecipeUserId) {
  try {
    // Set email verification status to false
    await EmailVerification.unverifyEmail(recipeUserId);
  } catch (err) {
    console.error(err);
  }
}
```
</Tab>
<Tab title="Go" value="go">
```go
import (
    "github.com/supertokens/supertokens-golang/recipe/emailverification"
)

func main() {
    userID := "..."
    // Set email verification status to false
    _, err := emailverification.UnverifyEmail(userID, nil)
    if err != nil {
        // handle error
    }
}
```
</Tab>
<Tab title="Python" value="python">
<DependentContent group="python-io-style" label="I/O style">
<ContentOption title="Asyncio" value="asyncio">
```python
from supertokens_python.recipe.emailverification.asyncio import unverify_email
from supertokens_python.types import RecipeUserId
async def manually_unverify_email(recipe_user_id: RecipeUserId):
    try:
        # Set email verification status to false
        await unverify_email(recipe_user_id)
    except Exception as e:
        print(e)
```
</ContentOption>
<ContentOption title="Syncio" value="syncio">
```python
from supertokens_python.recipe.emailverification.syncio import unverify_email
from supertokens_python.types import RecipeUserId

def manually_unverify_email(recipe_user_id: RecipeUserId):
    try:
        # Set email verification status to false
        unverify_email(recipe_user_id)
    except Exception as e:
        print(e)
```
</ContentOption>
</DependentContent>
</Tab>
</CodeGroup>

:::info[Multi Tenancy]
For a multi tenant setup, the function above does not take a tenant ID.
A user ID and the associated email verification status is unique on an app level (and not a tenant level).
:::


---

## See also

<CardGroup cols={3}>
  <Card title="Protect routes" href="/additional-verification/email-verification/protecting-routes" />
  <Card title="Hooks and overrides" href="/additional-verification/email-verification/hooks-and-overrides" />
  <Card title="Claim validation" href="/additional-verification/session-verification/claim-validation" />
  <Card title="Backend session validation" href="/additional-verification/session-verification/protect-api-routes" />
</CardGroup>
