Migrating user data statically

If your users have passwords, Stytch recommends the static approach outlined below, by creating a set of jobs to syncrhonize data to Stytch.

If your users are passwordless, you may either follow the steps below to move user data over statically, or see the migrating user data dynamically guide for an alternative strategy.

If you have over 1M users, skip to the Dual Write and Backfill section below.

Export and Import

First, you will need to export your user data. Here are some helpful resources for exporting your user data from other auth platforms:

  • To export from Auth0, create a support request.
  • To export from Firebase, use the CLI.
  • To export from AWS Cognito, use the ListUsers API. AWS Cognito does not support exporting password hashes through this API

Next, write an import job that calls Stytch's API to create each exported user in Stytch. The logic should resemble the following steps:

Step 1: Parse the data (likely in flat file format).

Step 2: Iterate over each row of user data.

Step 3: Pass the user's email, phone and/or password hash into the corresponding Stytch API endpoints:

Here are some sample scripts using our backend SDKs in Go, Ruby, Python and Node. You can select your preferred language in the dropdown located at the top right of the screen.

Your script should submit requests to Stytch at a maximum rate of 65 r/s.

const stytch = require("stytch");
const csv = require("csvtojson");
const client = new stytch.Client({
  project_id: "PROJECT_ID",
  secret: "SECRET",
  env: stytch.envs.test,
  .then((jsonArray) => {  
    jsonArray.forEach(async (row) => {
      try {
        const response = await client.users.create({
          email: row.email,
          phone_number: row.phone_number

        console.log(`User migration succeeded with status: ${response.status_code}`);

      } catch (error) {
        console.error(`${error.status_code} error migrating user ${row.email}: ${error.error_message}`);


Dual Write and Backfill

This is a variation of the Export and Import static migration process and is recommended for migrations of over 1M users.

Dual Write and Backfill is a technique for static migrations where logic is introduced to keep Stytch in-sync with the existing authentication provider in addition to running a one-time data backfill. This prevents Stytch from ever getting out of sync with the existing authentication service during the cutover.

Dual Write Backfill Img

Step 1: Set up dual-write using the create user endpoint

Dual write: Deploy logic to sync all changes between your internal user db and Stytch. This includes new user signups, user updates and user deletions. For every write into your db, duplicate that write into Stytch’s APIs. The returned Stytch user_id should be saved in your user db so you know the user has been migrated.

  • Creations: Create user via Create User
  • Deletions: Delete user via Delete User
  • Updates:
    • If user doesn't already exist in stytch, create user via Create User.
    • If user already exists and change is limited to metadata, name or attributes, update user via Update User
    • If user already exists and change is limited to a change in email or phone (and user has only one factor), update user via Exchange Primary Factor
    • Else, delete Stytch user and recreate user via Create User. Ensure the new Stytch user_id is updated in your user db.

Step 2: Deploy a backfill

While dual write is continuously running, you can assume that all users in your internal db without an associated Stytch user_id must run through a backfill job that adds them to Stytch via the Create User endpoint, one at a time.

You'll need to provide the Create user endpoint with the user's email, phone_number and/or crypto_wallet_address. Usage of trusted_metadata is optional but recommended for important data associations like your user's internal application id.

curl --request POST \
  --url https://test.stytch.com/v1/users \
  -u 'your-project-id:your-secret' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "test@test.com",
    "phone_number": "+18000000000",
    "trusted_metadata": {"internal_id": "407207d7-2a19-44e8-b192-4e2d45428b31"}  

Your backfill job should submit requests to Stytch at a rate of 65 r/s maximum.

Step 3: Move password hashes to Stytch

If you are migrating over from a legacy auth provider that stores password hashes on your behalf, in order to minimize time for drift on password changes, we recommend to migrate password hashes at the end of this process by executing a script to call our Migrate API for each user after backfill has been completed.

See the Export & Import section above for guidance on how to export password hashes and import to Stytch using a script.

You may call Migrate Password endpoint for existing Stytch users — Stytch will recoginze the email address and add the password hash to the existing user object.

If you are migrating over from an in-house build, you may fold password hashes into the dual write and backfill logic given this information is readily available in your own db.

Adding TOTP or Biometrics for MFA

For auth factors like TOTP and Biometrics, you cannot use the Create User endpoint or Migrate Password endpoint.

Instead, you will need to re-enroll users using Stytch's APIs or SDKs after they've been imported and authenticated (i.e. the Stytch User record must have a user_id and an active status). See the relevant endpoints here: TOTP Register and Biometrics Register.

Note: You cannot add additional factors onto user's accounts until their primary factor has been verified. See the Email/phone verification status section under Additional Migration Considerations for more information.

What's next

Check out the additional migration considerations guide to learn how to move your app logic to Stytch and for more user import guidance.