Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tkhq/sdk/llms.txt

Use this file to discover all available pages before exploring further.

A backend server is required when you need to:
  • Keep your organization’s API keys off the client
  • Perform admin operations (creating sub-organizations, sending OTPs, validating sessions)
  • Gate which Turnkey API methods the browser can call
If you want to skip running your own backend, use Turnkey’s managed Auth Proxy. Set authProxyConfigId in TurnkeyProviderConfig and configure the proxy in the Turnkey dashboard. Most authentication flows work without a custom server.

Setting up the Turnkey server client

Install @turnkey/sdk-server:
terminal
npm install @turnkey/sdk-server
Instantiate the Turnkey class with your organization credentials:
server/turnkey.ts
import { Turnkey } from "@turnkey/sdk-server";

const turnkey = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com",
  apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!,
  apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!,
  defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID!,
});

export default turnkey;
Call apiClient() on the instance to get a fully typed client that signs requests with your API key:
const client = turnkey.apiClient();

// Example: list sub-organizations
const result = await client.getSubOrgIds({
  organizationId: process.env.TURNKEY_ORGANIZATION_ID!,
  filterType: "EMAIL",
  filterValue: "user@example.com",
});

Environment variables

The following variables are required by @turnkey/sdk-server:
.env
TURNKEY_API_PUBLIC_KEY=your-api-public-key
TURNKEY_API_PRIVATE_KEY=your-api-private-key
TURNKEY_ORGANIZATION_ID=your-parent-organization-id
Generate an API key pair in the Turnkey dashboard under API Keys. The private key is only shown once — store it securely.

Proxy handlers

The proxy handlers let the browser call Turnkey API methods through your server without exposing your API key. The server signs the request before forwarding it to Turnkey.

Express

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

const app = express();
app.use(express.json());

const turnkey = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com",
  apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!,
  apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!,
  defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID!,
});

// Mount the proxy at POST /api/proxy
app.post(
  "/api/proxy",
  turnkey.expressProxyHandler({
    allowedMethods: [
      "oauth",
      "createReadWriteSession",
      "createSubOrganization",
      "emailAuth",
      "initUserEmailRecovery",
    ],
  })
);

app.listen(3001);

Next.js API routes

pages/api/proxy.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { Turnkey } from "@turnkey/sdk-server";

const turnkey = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com",
  apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!,
  apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!,
  defaultOrganizationId: process.env.TURNKEY_ORGANIZATION_ID!,
});

export default turnkey.nextProxyHandler({
  allowedMethods: [
    "oauth",
    "createReadWriteSession",
    "createSubOrganization",
    "emailAuth",
    "initUserEmailRecovery",
  ],
});

allowedMethods configuration

The allowedMethods array in TurnkeyProxyHandlerConfig restricts which Turnkey API method names the proxy will forward. Any method not in this list receives a 401 Unauthorized response. Default allowed methods when allowedMethods is omitted:
[
  "oauth",
  "createReadWriteSession",
  "createSubOrganization",
  "emailAuth",
  "initUserEmailRecovery",
]
Restrict this list to only the methods your application uses. Do not expose destructive or privileged operations unless explicitly required.

Server actions reference

The server object exported from @turnkey/sdk-server provides high-level actions that wrap common Turnkey API calls. These are designed for use in Next.js Server Actions ("use server").
Creates a new sub-organization (user account) with an associated wallet.
import { server } from "@turnkey/sdk-server";

const result = await server.createSuborg({
  email: "user@example.com",
  passkey: passkeyAttestation, // optional
  oauthProviders: [],           // optional
});
// result.subOrganizationId
The default wallet includes one Ethereum account (DEFAULT_ETHEREUM_ACCOUNTS) and one Solana account (DEFAULT_SOLANA_ACCOUNTS). Override this with customAccounts.
Sends an OTP to an email address or phone number.
import { server } from "@turnkey/sdk-server";

const result = await server.sendOtp({
  contact: "user@example.com",
  otpType: "OTP_TYPE_EMAIL",
  appName: "My App",
});
// result.otpId — pass to verifyOtp
Verifies an OTP code and returns a verificationToken.
import { server } from "@turnkey/sdk-server";

const result = await server.verifyOtp({
  otpId: "otp_id_from_sendOtp",
  otpCode: "123456",
  sessionLengthSeconds: 900,
});
// result.verificationToken — pass to otpLogin
Exchanges an OIDC token for a Turnkey session.
import { server } from "@turnkey/sdk-server";

const result = await server.oauthLogin({
  suborgID: userSuborgId,
  oidcToken: "<oidc-token>",
  publicKey: "<ephemeral-public-key>",
  sessionLengthSeconds: 900,
});
// result.session
Looks up sub-organization IDs by a filter (email, phone, OIDC token, or public key).
import { server } from "@turnkey/sdk-server";

const result = await server.getSuborgs({
  filterType: "EMAIL",
  filterValue: "user@example.com",
});
// result.organizationIds — array of matching sub-org IDs
Looks up a sub-organization and creates one if it does not exist. Useful for sign-up / log-in flows that require a single call.
import { server } from "@turnkey/sdk-server";

const result = await server.getOrCreateSuborg({
  filterType: "EMAIL",
  filterValue: "user@example.com",
  additionalData: {
    email: "user@example.com",
  },
});
// result.subOrganizationIds — array with one or more IDs

How the proxy works

1

Browser sends a request

The @turnkey/react-wallet-kit client sends a POST request to your proxy endpoint with a { methodName, params } body.
2

Server validates the method

expressProxyHandler or nextProxyHandler checks that methodName is in allowedMethods. Unlisted methods are rejected with 401 Unauthorized.
3

Server signs and forwards

The handler calls apiClient()[methodName](...params), which stamps the request with your organization’s API key and sends it to https://api.turnkey.com.
4

Response returned to browser

The Turnkey API response is forwarded back to the browser as JSON.