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

# Create a sub-org with a passkey user

> In this guide, we'll walk through the process of creating a new end user with a passkey.

> ⚠️ **This example is outdated !**<br />
> Head over to [SDK Reference](https://docs.turnkey.com/sdks/react/sub-organization-customization) for the updated packages.

## Overview

Generally, these new users will take the form of a [Turnkey Sub-Organization](/concepts/sub-organizations). This process involves using the following Turnkey SDK packages:

1. [`@turnkey/sdk-server`](https://www.npmjs.com/package/@turnkey/sdk-server): Used on the server-side to leverage the parent organization's public/private API key pair to create the new user's sub-organization.
2. [`@turnkey/sdk-browser`](https://www.npmjs.com/package/@turnkey/sdk-browser): Used on the client-side to complete the email recovery process by adding an end-user passkey.
3. [`@turnkey/sdk-react`](https://www.npmjs.com/package/@turnkey/sdk-react): Used for Next.js applications to initialize the Turnkey SDK.

The process of creating a new sub-organization is split between client-side and server-side operations to prevent exposing the parent organization's private API key.

<Note>
  For a refresher on the relationship between your application's end users and Turnkey Sub-Organizations, see [this page](/embedded-wallets/overview#how-it-works) for more.
</Note>

## Implementation

### Initialize the Turnkey SDK on the browser

<Tabs>
  <Tab title="Next.js">
    Wrap the root layout of your application with the `TurnkeyProvider` providing the required configuration options. This allows you to use the Turnkey client throughout your app via the `useTurnkey()` hook.

    ```tsx app/layout.tsx theme={"system"}
    import { TurnkeyProvider } from "@turnkey/sdk-react";

    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html>
          <body>
            <TurnkeyProvider
              config={{
                rpId: process.env.NEXT_PUBLIC_TURNKEY_RP_ID,
                apiBaseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL,
                defaultOrganizationId:
                  process.env.NEXT_PUBLIC_ORGANIZATION_ID,
              }}
            >
              {children}
            </TurnkeyProvider>
          </body>
        </html>
      );
    }
    ```

    <Note>
      The `NEXT_PUBLIC_ORGANIZATION_ID` should be set to the parent organization ID which can be found in the [Turnkey Dashboard](https://app.turnkey.com/dashboard).

      The `NEXT_PUBLIC_TURNKEY_RP_ID` should be set to your application's desired relying party ID; this is typically your domain, or localhost if developing locally. See [this page](/authentication/passkeys/options#rp) for more details.
    </Note>
  </Tab>

  <Tab title="TypeScript">
    ```tsx src/turnkey.ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-browser";

    // Initialize the Turnkey SDK with your organization ID and API base URL
    const turnkeyBrowser = new Turnkey({
      rpId: process.env.TURNKEY_RP_ID,
      apiBaseUrl: "https://api.turnkey.com",
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
    });
    ```

    <Note>
      The `TURNKEY_ORGANIZATION_ID` should be set to the parent organization ID which can be found in the [Turnkey Dashboard](https://app.turnkey.com/dashboard).

      The `TURNKEY_RP_ID` should be set to your application's desired relying party ID; this is typically your domain, or localhost if developing locally. See [this page](/authentication/passkeys/options#rp) for more details.
    </Note>
  </Tab>
</Tabs>

### Initialize the Passkey client

Next, we'll initialize the `passkeyClient`, which will enable your application to interact with passkeys.

<Tabs>
  <Tab title="Next.js">
    We add the `"use client"` directive to the Recovery component to as react hooks can only be used client-side.

    ```tsx app/create-suborg.tsx theme={"system"}
    "use client";

    import { useTurnkey } from "@turnkey/sdk-react";

    export default function CreateSubOrganization() {
      const { passkeyClient } = useTurnkey();

      return <div>{/* ... rest of the code */}</div>;
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```tsx src/create-suborg.ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-browser";

    // Initialize the Turnkey SDK with your organization credentials
    const turnkey = new Turnkey({
      rpId: process.env.TURNKEY_RP_ID, // Your relying party ID
      apiBaseUrl: process.env.TURNKEY_API_BASE_URL, // Turnkey API base URL
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID, // Your parent organization ID
    });

    // Initialize the Passkey Client
    const passkeyClient = turnkey.passkeyClient();

    // We'll add more functionality here in the following steps
    ```
  </Tab>
</Tabs>

### Create user passkey

In order to create a new passkey for a user, you can call the `createUserPasskey` SDK function. Calling this method will prompt the user to create a passkey, which will be securely stored by their browser. This credential will be associated with the user's account (sub-organization) and used for future authentication. Once the credential is created, we'll use it in the next step to create a new sub-organization that corresponds to the user.

<Note>
  The result of `createUserPasskey` includes an encoded challenge and attestation. The encoded challenge ensures the request is fresh and legitimate, while the attestation verifies the authenticity of the device creating the credential. For more information on how passkeys work, including details on the challenge and attestation objects, you can refer to the [Passkeys Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API#passkeys).
</Note>

<Tabs>
  <Tab title="Next.js">
    ```tsx app/create-suborg.tsx theme={"system"}
    // ... previous code

    export default function CreateSubOrganization() {
      const { passkeyClient } = useTurnkey();

      const createNewPasskey = async () => {
        const credential = await passkeyClient?.createUserPasskey({
          publicKey: {
            // This is the name of the passkey that will be displayed to the user
            rp: {
              name: "Wallet Passkey",
            },
            user: {
              // We can use the username as the name and display name
              name: "Default User Name",
              displayName: "Default User Name",
            },
          },
        });

        // we'll use this credential in the next step to create a new sub-organization
        return credential;
      };

      // ... rest of the code

      return (/* ... */);
    }
    ```
  </Tab>

  <Tab title="TypeScript">
    ```tsx src/create-suborg.ts theme={"system"}
    // ... previous code

    const createNewPasskey = async () => {
      const credential = await passkeyClient?.createUserPasskey({
        publicKey: {
          // This is the name of the passkey that will be displayed to the user
          rp: {
            name: "Wallet Passkey",
          },
          user: {
            // We can use the username as the name and display name
            name: "Default User Name",
            displayName: "Default User Name",
          },
        },
      });

      // we'll use this credential in the next step to create a new sub-organization
      return credential;
    };
    ```
  </Tab>
</Tabs>

### Initialize the Turnkey SDK on the server

Initialize the Turnkey SDK on the **server-side** using the `@turnkey/sdk-server` package. This allows you to use the parent organization's public/private API key pair to create sub-organizations.

<Tabs>
  <Tab title="Next.js">
    For Next.js, add the `"use server"` directive at the top of the file where you're initializing the Turnkey server client. This will ensure that the function is executed on the server-side and will have access to the server-side environment variables e.g. your parent organization's public/private API key pair. For more information on Next.js server actions, see the Next.js documentation on [Server Actions and Mutations](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).

    ```tsx app/actions.ts theme={"system"}
    "use server";

    import { Turnkey } from "@turnkey/sdk-server";

    // Initialize the Turnkey Server Client on the server-side
    const turnkeyServer = new Turnkey({
      apiBaseUrl: "https://api.turnkey.com",
      apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY,
      apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY,
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
    }).apiClient();
    ```
  </Tab>

  <Tab title="TypeScript">
    ```tsx src/turnkey.ts theme={"system"}
    import { Turnkey } from "@turnkey/sdk-server";

    // Initialize the Turnkey Server Client on the server-side
    const turnkeyServer = new Turnkey({
      apiBaseUrl: "https://api.turnkey.com",
      apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY,
      apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY,
      defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
    }).apiClient();
    ```
  </Tab>
</Tabs>

### Create a function for sub-org creation

Next we'll create a new function called `createSubOrganization` that will be used to create a new sub-organization from the server-side. This method will be called from the client-side with the end-user's details.

<Tabs>
  <Tab title="Next.js">
    We export the `createSubOrganization` server action to be called from the client-side.

    ```tsx app/actions.tsx theme={"system"}
    import { DEFAULT_ETHEREUM_ACCOUNTS } from "@turnkey/sdk-browser";

    // ... previous code

    type TAttestation = {
      credentialId: string;
      clientDataJson: string;
      attestationObject: string;
      transports: (
        | "AUTHENTICATOR_TRANSPORT_BLE"
        | "AUTHENTICATOR_TRANSPORT_INTERNAL"
        | "AUTHENTICATOR_TRANSPORT_NFC"
        | "AUTHENTICATOR_TRANSPORT_USB"
        | "AUTHENTICATOR_TRANSPORT_HYBRID"
      )[];
    };

    export const createSubOrganization = async (
      email: string,
      credential: string,
      attestation: string,
    ) => {
      const createSubOrgResponse = await turnkeyServer.createSubOrganization({
        subOrganizationName: "My New Suborg",
        rootUsers: [
          {
            userName: "Default User Name",
            userEmail: email,
            apiKeys: [],
            authenticators: [
              {
                authenticatorName: "Default Passkey",
                challenge: challenge,
                attestation: attestation,
              },
            ],
            oauthProviders: [],
          },
        ],
        rootQuorumThreshold: 1,
        wallet: {
          walletName: "Default Wallet",
          accounts: DEFAULT_ETHEREUM_ACCOUNTS,
        },
      });

      return createSubOrgResponse;
    };
    ```
  </Tab>

  <Tab title="TypeScript">
    ```tsx src/turnkey-server.ts theme={"system"}
    /// ... previous code

    type TAttestation = {
      credentialId: string;
      clientDataJson: string;
      attestationObject: string;
      transports: (
        | "AUTHENTICATOR_TRANSPORT_BLE"
        | "AUTHENTICATOR_TRANSPORT_INTERNAL"
        | "AUTHENTICATOR_TRANSPORT_NFC"
        | "AUTHENTICATOR_TRANSPORT_USB"
        | "AUTHENTICATOR_TRANSPORT_HYBRID"
      )[];
    };

    export const createSubOrganization = async (
      email: string,
      credential: string,
      attestation: string,
    ) => {
      const createSubOrgResponse = await turnkeyServer.createSubOrganization({
        subOrganizationName: "My New Suborg",
        rootUsers: [
          {
            userName: "Default User Name",
            userEmail: email,
            apiKeys: [],
            authenticators: [
              {
                authenticatorName: "Default Passkey",
                challenge: challenge,
                attestation: attestation,
              },
            ],
            oauthProviders: [],
          },
        ],
        rootQuorumThreshold: 1,
        wallet: {
          walletName: "Default Wallet",
          accounts: DEFAULT_ETHEREUM_ACCOUNTS,
        },
      });

      return createSubOrgResponse;
    };
    ```
  </Tab>
</Tabs>

### Complete create sub-organization

At this stage, we create the sub-organization using the **server-side** function we created in the previous step.

<Tabs>
  <Tab title="Next.js">
    <Steps>
      <Step title="Import the server action">
        ```tsx app/create-suborg.tsx theme={"system"}
        import { createSubOrganization } from "./actions";
        ```
      </Step>

      <Step title="Call createSubOrganization with the end-user's details">
        ```tsx app/create-suborg.tsx theme={"system"}
        // ...

        import { useForm } from "react-hook-form";

        type TSubOrgFormData = {
          email: string;
        };

        export default function CreateSubOrganization() {
          // ...

          // Use form handler for suborg creation
          const { register: subOrgFormRegister, handleSubmit: subOrgFormSubmit } =
            useForm<TSubOrgFormData>();

          // Maintain state
          const [createSubOrganizationResponse, setCreateSubOrganizationResponse] =
            useState(null);

          const createSubOrg = async (data: TSubOrgFormData) => {
            const { encodedChallenge: challenge, attestation } =
              await createNewPasskey();

            const createSubOrganizationResponse = await createSubOrganization(
              data.email,
              challenge,
              attestation,
            );

            setCreateSubOrganizationResponse(createSubOrganizationResponse);
          };

          return (
            <div>
              {createSubOrganizationResponse ? (
                <h2>You've created a sub-organization!</h2>
              ) : (
                <form onSubmit={subOrgFormSubmit(createSubOrg)}>
                  <label>
                    Email
                    <input {...subOrgFormRegister("email")} placeholder="User Email" />
                  </label>
                  <input type="submit" value="Create new sub-organization" />
                </form>
              )}
            </div>
          );
        }
        ```

        <Accordion title="create-suborg.tsx">
          ```tsx theme={"system"}
          "use client";

          import { useState } from "react";
          import { useTurnkey } from "@turnkey/sdk-react";
          import { useForm } from "react-hook-form";

          // Import the createSubOrganization server action
          import { createSubOrganization } from "./actions";

          type TSubOrgFormData = {
          email: string;
          };

          type TAttestation = {
          credentialId: string;
          clientDataJson: string;
          attestationObject: string;
          transports: (
            | "AUTHENTICATOR_TRANSPORT_BLE"
            | "AUTHENTICATOR_TRANSPORT_INTERNAL"
            | "AUTHENTICATOR_TRANSPORT_NFC"
            | "AUTHENTICATOR_TRANSPORT_USB"
            | "AUTHENTICATOR_TRANSPORT_HYBRID"
          )[];
          };

          export default function CreateSubOrganization() {
          const { passkeyClient } = useTurnkey();

          // Use form handler for suborg creation
          const { register: subOrgFormRegister, handleSubmit: subOrgFormSubmit } =
            useForm<TSubOrgFormData>();

          // Maintain state
          const [createSubOrganizationResponse, setCreateSubOrganizationResponse] =
            useState(null);

          const createNewPasskey = async () => {
            const credential = await passkeyClient?.createUserPasskey({
              publicKey: {
                // This is the name of the passkey that will be displayed to the user
                rp: {
                  name: "Wallet Passkey",
                },
                user: {
                  // We can use the username as the name and display name
                  name: "Default User Name",
                  displayName: "Default User Name",
                },
              },
            });

            // we'll use this credential in the next step to create a new sub-organization
            return credential;
          };

          const createSubOrg = async (data: TSubOrgFormData) => {
            const { encodedChallenge: challenge, attestation } =
              await createNewPasskey();

            const createSubOrganizationResponse = await createSubOrganization(
              data.email,
              challenge,
              attestation,
            );

            setCreateSubOrganizationResponse(createSubOrganizationResponse);
          };

          return (
            <div>
              {createSubOrganizationResponse ? (
                <h2>You've created a sub-organization!</h2>
              ) : (
                <form onSubmit={subOrgFormSubmit(createSubOrg)}>
                  <label>
                    Email
                    <input {...subOrgFormRegister("email")} placeholder="User Email" />
                  </label>
                  <input type="submit" value="Create new sub-organization" />
                </form>
              )}
            </div>
          );
          }
          ```
        </Accordion>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Next.js">
    <Steps>
      <Step title="Import the server action">
        ```tsx app/create-suborg.tsx theme={"system"}
        import { createSubOrganization } from "./turnkey-server";
        ```
      </Step>

      <Step title="Call createSubOrganization with the end-user's details">
        ```tsx src/turnkey.ts theme={"system"}
        import { createSubOrganization } from "./turnkey-server";

        // ... rest of the code

        const createSubOrganizationResponse = await createSubOrganization(
          email,
          attestation,
          challenge,
        );
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Examples

A few mini examples where sub-orgs are created with passkeys, see the following:

<CardGroup cols={1}>
  <Card title="https://github.com/tkhq/sdk/tree/main/examples/with-solana-passkeys" href="https://github.com/tkhq/sdk/tree/main/examples/with-solana-passkeys" icon="github" iconType="solid" horizontal />

  <Card title="https://github.com/tkhq/sdk/tree/main/examples/with-eth-passkeys-galore" href="https://github.com/tkhq/sdk/tree/main/examples/with-eth-passkeys-galore" icon="github" iconType="solid" horizontal />

  <Card title="https://github.com/tkhq/sdk/tree/main/examples/with-federated-passkeys" href="https://github.com/tkhq/sdk/tree/main/examples/with-federated-passkeys" icon="github" iconType="solid" horizontal />
</CardGroup>
