> ## Documentation Index
> Fetch the complete documentation index at: https://turnkey-0e7c1f5b-ip-allowlisting.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Co-signing Transactions

> Learn how to set up and use co-signing (multi-sig) wallets with Turnkey.

## Introduction to Co-signing

Co-signing, often referred to as multi-signature (multi-sig), provides an enhanced layer of security for blockchain transactions. It requires approvals from multiple parties before a transaction can be executed. This guide details how to implement a 2/2 co-signing setup using Turnkey, where both the end-user (via passkey) and your application backend (via API key) must approve transactions.

## Co-signing Architecture

The following diagram illustrates the setup and transaction flow for a co-signing wallet managed by Turnkey and your backend application:

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant Turnkey

    %% Setup Phase
    Note over User,Turnkey: Setup Phase
    User->>Frontend: Sign up / Create wallet
    Frontend->>User: Request passkey creation
    User->>Frontend: Create passkey (attestation)
    Frontend->>Backend: Send user info & attestation
    Backend->>Turnkey: Create sub-org with 2 root users:<br/>1. User (passkey)<br/>2. Backend (API key)
    Backend->>Turnkey: Set root quorum threshold = 2
    Turnkey->>Backend: Return sub-org ID & wallet info
    Backend->>Backend: Store sub-org ID in user record
    Backend->>Frontend: Return wallet info
    Frontend->>User: Display wallet address

    %% Transaction Phase
    Note over User,Turnkey: Transaction Phase
    User->>Frontend: Initiate transaction
    Frontend->>User: Request passkey authentication
    User->>Frontend: Authenticate with passkey
    Frontend->>Turnkey: Submit transaction signing request
    Turnkey->>Turnkey: Verify user passkey signature
    Turnkey->>Frontend: Return activity fingerprint
    Frontend->>Backend: Send activity fingerprint for approval
    Backend->>Turnkey: Verify & validate activity details
    Backend->>Turnkey: Approve activity (using backend API key)
    Turnkey->>Turnkey: Sign transaction (requires both approvals)
    Turnkey->>Backend: Return signed transaction
    Backend->>Frontend: Return signed transaction
    Frontend->>User: Show transaction success
```

## Implementation Steps

<Steps>
  <Step title="Create a Sub-Organization with Multiple Root Users">
    To set up a multi-sig wallet in Turnkey, you first need to create a sub-organization with two root users.
    This sub-organization will function as a separate entity with its own wallet and security settings.

    The key configuration here is setting up:

    * A root user for the end-user, authenticated with their passkey
    * A root user for your application service, authenticated with an API key
    * A root quorum threshold of 2, requiring both users to approve critical operations

    This creates a true multi-sig arrangement where neither party can unilaterally control the wallet.
    The following code shows how to implement this setup on your backend:

    <CodeGroup>
      ```typescript app.ts [expandable] {43} theme={null}
      import { turnkeyServer } from "./turnkey";
      import { DEFAULT_ETHEREUM_ACCOUNTS } from "@turnkey/sdk-browser";

      async function createMultiSigWallet(
        userId,
        userEmail,
        userPasskeyChallenge,
        userPasskeyAttestation
      ) {
        // Create a sub-organization with two root users and a threshold of 2
        const subOrg = await turnkeyServer.createSubOrganization({
          organizationId: process.env.TURNKEY_ORGANIZATION_ID,
          subOrganizationName: `Multi-Sig Wallet for ${userEmail}`,
          rootUsers: [
            // First root user - the end user with their passkey
            {
              userName: "End User",
              userEmail: userEmail,
              apiKeys: [],
              authenticators: [
                {
                  authenticatorName: "User Passkey",
                  challenge: userPasskeyChallenge,
                  attestation: userPasskeyAttestation,
                },
              ],
            },
            // Second root user - your application's service account
            {
              userName: "Application Service",
              userEmail: "service@yourapp.com",
              apiKeys: [
                {
                  apiKeyName: "Service API Key",
                  publicKey: process.env.SERVICE_API_PUBLIC_KEY,
                  curveType: "API_KEY_CURVE_SECP256K1",
                },
              ],
              authenticators: [],
            },
          ],
          // This is the key setting - requiring both users to approve
          rootQuorumThreshold: 2,
          wallet: {
            walletName: "Shared Wallet",
            accounts: DEFAULT_ETHEREUM_ACCOUNTS,
          },
        });

        // Save the sub-organization ID in your database
        await db.users.update({
          where: { id: userId },
          data: { turnkeySubOrgId: subOrg.organizationId },
        });

        return subOrg;
      }
      ```

      ```typescript turnkey.ts theme={null}
      import { Turnkey } from "@turnkey/sdk-server";

      // Initialize server-side client
      export 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();

      ```
    </CodeGroup>
  </Step>

  <Step title="Client-Side Transaction Initiation">
    When the user wants to sign a transaction using their multi-sig wallet, they need to initiate the process from your frontend application.
    This step involves:

    * Authenticating the user with their passkey (handled automatically by Turnkey)
    * Creating a transaction signing request to Turnkey
    * Receiving an activity fingerprint that needs further approval
    * Forwarding this fingerprint to your backend for the second signature

    The transaction won't be fully signed yet - it will be in a "requires consensus" state until your backend approves it.
    Here's how to implement this flow in your frontend:

    <CodeGroup>
      ```typescript app.ts [expandable] theme={null}
      import { turnkeyPasskeyClient } from "./turnkey";


      // User initiates a transaction signing
      async function initiateTransaction(txData, walletAddress) {
        // This will trigger the user's passkey authentication
        const signActivity = await turnkeyPasskeyClient.signTransaction({
          organizationId: subOrgId,
          parameters: {
            type: "TRANSACTION_TYPE_ETHEREUM",
            signWith: walletAddress,
            unsignedTransaction: txData,
          },
        });

        // The activity needs approval from the backend service
        // Send the activity fingerprint to your backend
        const response = await fetch("/api/proxy/turnkey/approve-transaction", {
          method: "POST",
          body: JSON.stringify({
            activityFingerprint: signActivity.fingerprint,
            subOrgId: subOrgId,
          }),
          headers: {
            "Content-Type": "application/json",
          },
          credentials: "include", // Include cookies for JWT authentication
        });

        const result = await response.json();
        return result;
      }
      ```

      ```typescript turnkey.ts theme={null}
      import { Turnkey } from "@turnkey/sdk-browser";

      // Initialize server-side client
      export const turnkeyPasskeyClient = new Turnkey({
        apiBaseUrl: "https://api.turnkey.com",
        defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID,
      });
      ```
    </CodeGroup>
  </Step>

  <Step title="Backend Activity Approval">
    Your backend needs an endpoint to receive the activity fingerprint from the frontend and approve it using its own API key.

    <CodeGroup>
      ```typescript app.ts [expandable] theme={null}
      import { turnkeyServer } from "./turnkey";
      import { verifyJwt } from "./authMiddleware"; // Assume standard JWT middleware

      // Endpoint to approve a transaction activity
      app.post(
        "/api/proxy/turnkey/approve-transaction",
        verifyJwt,
        async (req, res) => {
          const { activityFingerprint, subOrgId } = req.body;
          const { userId } = req.user;

          // --- Authorization Check ---
          // Verify the user is authorized for this subOrgId
          const user = await db.users.findUnique({
            where: { id: userId },
            select: { turnkeySubOrgId: true },
          });

          if (user?.turnkeySubOrgId !== subOrgId) {
            return res.status(403).json({ error: "Forbidden" });
          }
          // --- End Authorization ---

          try {
            // TODO: Validate the transaction details from the activity
            // Fetch the activity from Turnkey to inspect its contents
            // const activity = await turnkeyServer.getActivity({
            //   activityId: activityId, // Need activityId from fingerprint?
            //   organizationId: subOrgId
            // });
            // if (activity.details.isInvalid) { throw new Error(...) }
            
            // Approve the activity using the backend service's API key
            const approvalActivity = await turnkeyServer.approveActivity({
              organizationId: subOrgId,
              fingerprint: activityFingerprint,
            });

            // Once approved, the transaction is signed
            // You might want to poll getActivity or use Webhooks to get the signed tx
            // For simplicity, we assume success here
            return res.status(200).json({ success: true, message: "Transaction approved by backend" });
          } catch (error) {
            console.error("Error approving transaction:", error);
            return res.status(500).json({ error: "Failed to approve transaction" });
          }
        }
      );
      ```

      ```typescript turnkey.ts theme={null}
      import { Turnkey } from "@turnkey/sdk-server";

      // Initialize server-side client
      export 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();
      ```
    </CodeGroup>
  </Step>
</Steps>

#### Security Considerations and Best Practices

* **Validation Before Approval**: Always validate transaction details (recipient, amount, etc.) before approving activities.
* **API Key Security**: Protect your backend service's API key.
* **Authorization**: Ensure the authenticated frontend user is authorized for the `subOrgId` they are interacting with.
* **Webhooks**: Use Turnkey Webhooks to get notified about activity status changes (e.g., when a transaction is fully signed and confirmed).
