> ## Documentation Index
> Fetch the complete documentation index at: https://stytch.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating user data

> Choose a migration strategy for moving user data to Stytch.

export const jit_provisioning = "Just-in-time provisioning: allow new Members to be added to an Organization as soon as they authenticate, based on email domain or SSO connection.";

export const sso = "Authentication process allowing users to access multiple applications with one set of credentials.";

<Tabs>
  <Tab title="Consumer auth">
    If your users have passwords, Stytch recommends the **static migration** approach by creating jobs to synchronize data to Stytch. If your users are passwordless, you can use a static migration or follow the **dynamic (rolling) migration** approach.

    ## Static migration (recommended for passwords)

    If you have over 1M users, skip to **Dual write and backfill** below.

    ### Export and import

    First, export your user data. Helpful resources for exporting:

    * **Auth0**: Create a [support request](https://community.auth0.com/t/how-to-export-user-password-hashes/45485).
    * **Firebase**: Use the [CLI](https://firebase.google.com/docs/cli/auth#authexport). Note that Firebase password hashes are **not** currently compatible with Stytch, and a static migration of users with passwords from Firebase to Stytch is not possible.
    * **AWS Cognito**: Use the [ListUsers API](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API%5FListUsers.html). 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:

    <Steps>
      <Step title="Parse the data (likely a flat file)." />

      <Step title="Iterate over each row of user data." />

      <Step title="Pass the user's email, phone and/or password hash to the corresponding endpoints:">
        * Passwordless users: [Create User](/api-reference/consumer/api/users/create-user)
        * Users with passwords: [Migrate Password](/api-reference/consumer/api/passwords/migrate)
        * Users with email + phone + password: create the user first, then migrate the password hash
      </Step>
    </Steps>

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

    ### Dual write and backfill (for large user bases)

    Dual write and backfill keeps Stytch in sync with your existing provider while you migrate.

    <img src="https://mintcdn.com/stytch-34ca0595/jCmOQoXV28mXNOhP/images/consumer/from-old-docs/dual-write-backfill-diagram.svg?fit=max&auto=format&n=jCmOQoXV28mXNOhP&q=85&s=501150062de25aff0332680f729bb8c2" alt="Dual write and backfill" width="1101" height="645" data-path="images/consumer/from-old-docs/dual-write-backfill-diagram.svg" />

    <Steps>
      <Step title="Set up dual write">
        Sync all user changes into Stytch (create, update, delete) and save the returned `user_id` in your DB.
      </Step>

      <Step title="Backfill remaining users">
        Create any users without a Stytch `user_id` via [Create User](/api-reference/consumer/api/users/create-user), one at a time, capped at 65 r/s.
      </Step>

      <Step title="Migrate password hashes">
        After backfill, call [Migrate Password](/api-reference/consumer/api/passwords/migrate) for existing Stytch users.
      </Step>
    </Steps>

    ## Dynamic (rolling) migration (recommended for passwordless)

    If your users do not have passwords, you can migrate users as they log in:

    1. As each user logs in, call the Stytch API to authenticate and create a Session.
    2. Look up the user in your internal database.
    3. If the user exists, store the Stytch `user_id` on the record.
    4. If the user does not exist, create a new user record and store the Stytch `user_id`.

           <img src="https://mintcdn.com/stytch-34ca0595/ujkFjtNfcCtlcK2E/images/consumer/from-old-docs/rolling-migration.png?fit=max&auto=format&n=ujkFjtNfcCtlcK2E&q=85&s=729a018515760ec1f6348b0b98a17282" alt="Dynamic migration" width="732" height="286" data-path="images/consumer/from-old-docs/rolling-migration.png" />

    ## Adding TOTP or biometrics

    For auth factors like TOTP or Biometrics, you cannot use Create User or Migrate Password. Re-enroll users using the Stytch APIs or SDKs after they have been imported and authenticated:

    * [TOTP Register](/api-reference/consumer/api/totp/create)
    * [Biometrics Register](/api-reference/consumer/api/passkeys-webauthn/register/start)

    Note: You cannot add additional factors until the primary factor has been verified. See the [Additional migration considerations](/resources/migrations/additional-migration-considerations#consumer-auth) guide for more details.
  </Tab>

  <Tab title="B2B auth">
    In order to migrate your authentication from an existing system to Stytch's B2B SaaS Authentication platform, you will need to import all current organization and user data to Stytch via API or SDK.

    Stytch recommends the data migration approaches outlined below. This involves creating a set of jobs to synchronize organization and user data to Stytch.

    ## Dual-write and backfill

    In order to achieve a smooth, zero-downtime migration without any issues around data reconciliation, we recommend setting up dual-writes prior to kicking off a backfill to Stytch. This allows any create, update, or delete requests during the backfill period are reflected in both your current system as well as in Stytch, ensuring that you can cut over to Stytch as the read source of truth without any customer impact or downtime.

    However, if you have less than \~500k users and would rather skip the dual write step, you can always run the backfill directly via an **Export and Import** approach described below.

    <img src="https://mintcdn.com/stytch-34ca0595/KoFK6_4cd0s5103l/images/resources/migrations/dual-write-b2b.png?fit=max&auto=format&n=KoFK6_4cd0s5103l&q=85&s=dbafc1dd8d65f0c69be4dadb2cb481a0" alt="Dual-write backfill" width="1174" height="645" data-path="images/resources/migrations/dual-write-b2b.png" />

    The above is an example diagram of a Dual-write and Backfill process for users and organizations.

    <Steps>
      <Step title="Set up dual-write on all write paths">
        For every place you write to your internal member or organization tables, introduce a corresponding write to Stytch. This includes new user signups, user updates, user deletions, organization creations, organization updates and organization deletions.

        The returned Stytch `member_id` or `organization_id` should be saved in your tables on creation, so you know the user or organization has been migrated.

        **Organization API endpoints:**

        * Creations: Create organization via [Create Organization](/api-reference/b2b/api/organizations/create-organization)
        * Updates:
          * If the organization doesn't already exist in Stytch, create the organization via [Create Organization](/api-reference/b2b/api/organizations/create-organization)
          * If you already have a `stytch_organization_id` associated with your record, update the organization via [Update Organization](/api-reference/b2b/api/organizations/update-organization)
        * Deletions: If you've already created the Organization in Stytch (`stytch_organization_id` associated with the record) delete the organization via [Delete Organization](/api-reference/b2b/api/organizations/delete-organization)

        **Member API endpoints:**

        * Create user:
          * If this is a new user joining an existing Organization that has already been migrated to Stytch (`stytch_organization_id` on record), create via [Create Member](/api-reference/b2b/api/members/create-member)
          * If this is a user signing up for a new Organization or joining an Organization that has not been migrated yet, [Create Organization](/api-reference/b2b/api/organizations/create-organization) first and store the `stytch_organization_id` and then [Create Member](/api-reference/b2b/api/members/create-member)
        * Updates:
          * If the user doesn't already exist in Stytch, create the user as described above. Use [Migrate Member](/api-reference/b2b/api/passwords/migrate) if the user has a password hash in your DB.
          * If the user already exists (`stytch_member_id` associated with your record) use the [Update Member](/api-reference/b2b/api/members/update-member) API for updates to name, email, roles, or metadata.
          * If the update is to the MFA Phone Number, Password or Member TOTP Registration, you'll need to first delete the old authentication factor record from the Stytch Member, and then call Update Member to complete.
        * Deletions: Delete user via [Delete Member](/api-reference/b2b/api/members/delete-member)
      </Step>

      <Step title="Run a backfill">
        While dual-write is continuously running, you can assume that all users and organizations in your internal tables **without** an associated Stytch `member_id` or `organization_id` must run through a backfill job that adds them to Stytch via the [Create Organization endpoint](/api-reference/b2b/api/organizations/create-organization) or the [Create Member endpoint](/api-reference/b2b/api/members/create-member), one at a time.

        Create Organization:

        ```sh theme={null}
        curl --request POST \
          --url https://test.stytch.com/v1/b2b/organizations \
          -u 'PROJECT_ID:SECRET' \
          -H 'Content-Type: application/json' \
          -d '{
            "organization_name": "Example Org Inc.",
            "organization_slug": "example-org",
            "mfa_policy": "REQUIRED_FOR_ALL",
            "email_allowed_domains": ["example.com"],
            "email_invites": "ALL_ALLOWED",
            "email_jit_provisioning": "RESTRICTED",
          }'
        ```

        Create Member:

        ```sh theme={null}
        curl --request POST \
          --url https://test.stytch.com/v1/b2b/organizations/{ORGANIZATION_ID}/members \
          -u 'PROJECT_ID:SECRET' \
          -H 'Content-Type: application/json' \
          -d '{
            "email_address": "adalovelace@stytch.com",
            "mfa_phone_number": "+18000000000",
            "mfa_enrolled": true
            "trusted_metadata": {"internal_id": "407207d7-2a19-44e8-b192-4e2d45428b31"}
          }'
        ```

        Your backfill job should start with Organizations, then backfill Members once Organizations are complete. This job should submit requests to Stytch at a rate of **100 r/s maximum**.
      </Step>

      <Step title="Move password hashes to Stytch">
        If your manage passwords in-house within your own databases, you can integrate the logic for migrating passwords hashes into the core dual-write and backfill steps. However, if you are migrating over from a legacy auth provider, like Auth0, that stores password hashes on your behalf, you will need a full data export. In order to minimize the time for drift on password changes, we recommend migrating password hashes **at the end** of this process by executing a script to call our [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate) for each user after backfill has been completed.

        To migrate passwords you will leverage the [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate) by passing in the `email_address` associated with the Member, and the password hash from your provider.

        Stytch offers the option to either **allow end users to use the same password across *all* Organizations they belong to under that email** or to **scope passwords to the specific Member/Organization**. This setting **cannot be changed** once you have active passwords in your project, so prior to kicking off your migration update your password settings in the [Stytch Dashboard](https://stytch.com/dashboard/password-strength-config) to match the password behavior that you would like.

        If you want to allow passwords to be used across Organizations, you only need to call the [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate) one time for a given email -- even if the email is associated with multiple Members. If you want passwords to be scoped to the Member/Organization, you will call the Migrate Password endpoint for each MemberID you've migrated to Stytch.

        ```sh theme={null}
        curl --request POST \
          --url https://test.stytch.com/v1/b2b/passwords/migrate \
          -u 'PROJECT_ID:SECRET' \
          -H 'Content-Type: application/json' \
          -d '{
            "email_address": "adalovelace@stytch.com",
            "hash": "$2a$12$vefoDBbzuMb...",
            "hash_type": "bcrypt",
            "organization_id": "organization-test-07971..."
          }'
        ```
      </Step>
    </Steps>

    ## Export and import

    First, you will need to export your organization and user data from your current auth service. We recommend querying this information from your internal user and organization tables if possible. If you don't have a copy of user and workspace records outside of your auth provider, here are some helpful resources to export from various auth providers:

    * To export from **Auth0**, create a [support request](https://community.auth0.com/t/how-to-export-user-password-hashes/45485).

    Next, you will write an import job that calls Stytch's API to create each Organization and Member in Stytch. The logic should resemble the following steps:

    <Steps>
      <Step title="Migrate Stytch Organization">
        Pass each application user group's name, slug, and authentication settings into Stytch [create Organization](/api-reference/b2b/api/organizations/create-organization) API endpoint.

        * If the Organization has <Tooltip tip={sso}>Single Sign-On</Tooltip> connections with an identity provider, you will need to create SAML and OIDC connection objects. See [Additional migration considerations](/resources/migrations/additional-migration-considerations#b2b-auth) for more information.
        * Configure an Organization's auth settings to control approved authentication methods, allowed email domains, invites, MFA, <Tooltip tip={jit_provisioning}>Just-in-Time (JIT) Provisioning</Tooltip>, SSO connections, and more. See the [managing Organization settings](/multi-tenant-auth/enterprise-ready/org-management/org-configuration-rules) guide for more information.

        ```sh theme={null}
        curl --request POST \
          --url https://test.stytch.com/v1/b2b/organizations \
          -u 'PROJECT_ID:SECRET' \
          -H 'Content-Type: application/json' \
          -d '{
            "organization_name": "Example Org Inc.",
            "organization_slug": "example-org",
            "email_allowed_domains": ["exampleorg.com"],
            "email_invites": "RESTRICTED"
          }'
        ```
      </Step>

      <Step title="Migrate Stytch Members">
        For each Stytch Organization a user belongs to, you will need to create a Member object. Pass the user's email address, phone number (optional), and their password hash (optional) into the corresponding Stytch API endpoints:

        * If the user is passwordless, use the [Create Member endpoint](/api-reference/b2b/api/members/create-member).
        * If the user has a password, use our [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate).
        * If the user has a password and two factors (e.g. email and phone), first use the [Create Member endpoint](/api-reference/b2b/api/members/create-member) to add the `email_address` + `mfa_phone_number`, then use the [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate) to add the password hash.

        Stytch offers the option to either **allow end users to use the same password across *all* Organizations they belong to under that email** or to **scope passwords to the specific Member/Organization**. This setting **cannot be changed** once you have active passwords in your project, so prior to kicking off your migration update your password settings in the [Stytch Dashboard](https://stytch.com/dashboard/password-strength-config) to match the password behavior that you would like.

        If you want to allow passwords to be used across Organizations, you only need to call the [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate) one time for a given email -- even if the email is associated with multiple Members. If you want passwords to be scoped to the Member/Organization, you will call the Migrate Password endpoint for each MemberID you've migrated to Stytch.

        You may call [Migrate Password endpoint](/api-reference/b2b/api/passwords/migrate) for existing Stytch Members, using the `email_address` associated with the Member. Stytch will recognize the email address and add the password hash to the existing Member object or allow for use across multiple Member objects depending on your setting.

        ```sh theme={null}
        curl --request POST \
          --url https://test.stytch.com/v1/b2b/organizations/{ORGANIZATION_ID}/members \
          -u 'PROJECT_ID:SECRET' \
          -H 'Content-Type: application/json' \
          -d '{
            "name": "Ada Lovelace",
            "email_address": "adalovelace@stytch.com"
          }'
        ```
      </Step>
    </Steps>

    ## What's next

    Check out the [additional migration considerations guide](/resources/migrations/additional-migration-considerations#b2b-auth) to learn how to move your app logic to Stytch and for more user import guidance.
  </Tab>
</Tabs>
