or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

backend-api-client.mdclient-components.mdclient-hooks.mderror-handling.mdindex.mdmiddleware-and-route-protection.mdserver-auth-app-router.mdserver-auth-pages-router.mdsetup-and-provider.mdwebhooks.md
tile.json

backend-api-client.mddocs/

Backend API Client

Access the Clerk Backend SDK for server-side operations like user management, organization operations, and token verification. The backend API client provides full access to Clerk's REST API from your Next.js server.

Key Information for Agents

Required Setup:

  • CLERK_SECRET_KEY environment variable required
  • clerkClient() can only be used in server-side contexts (Server Components, Route Handlers, Server Actions, API routes)
  • Cannot be used in client components or browser

Default Behaviors:

  • clerkClient() returns singleton instance (same client per request)
  • Client is automatically configured with keys from middleware or environment
  • All API methods are async and must be awaited
  • API calls count towards Clerk Backend API rate limits
  • clerkClient() calls are not automatically deduped (each call makes API request)
  • createClerkClient() creates new client instance with custom configuration
  • verifyToken() verifies JWT tokens manually (for custom verification scenarios)

Threading Model:

  • All functions execute in Node.js server runtime (not browser)
  • All methods are async and return Promises
  • Multiple clerkClient() calls in same request return same client instance
  • API calls are made to Clerk backend (network requests)
  • Client instances are not thread-safe for concurrent modification (but safe for concurrent reads)

Lifecycle:

  • clerkClient() initializes client on first call (lazy initialization)
  • Client instance is reused within same request context
  • API calls are made per method call (no automatic caching)
  • Client persists for request lifetime
  • createClerkClient() creates new instance (not singleton)

Edge Cases:

  • Rate limiting: API calls count towards Clerk rate limits (monitor usage)
  • Error handling: All API methods throw ClerkAPIResponseError on failure
  • Pagination: List methods return paginated results (use limit/offset params)
  • Custom client: Use createClerkClient() for multiple clients or custom config
  • Token verification: verifyToken() for manual JWT verification (not session tokens)
  • Missing secret key: Causes client initialization to fail
  • Network errors: API calls can fail due to network issues (handle with try/catch)

Exceptions:

  • Missing CLERK_SECRET_KEY causes clerkClient() to throw error
  • API method failures throw ClerkAPIResponseError with error details
  • Invalid parameters cause API methods to throw validation errors
  • Network failures cause API methods to throw network errors
  • verifyToken() throws if token invalid or expired

Capabilities

clerkClient() Function

Returns the Clerk Backend SDK client instance with access to all backend operations.

/**
 * Returns Clerk Backend SDK client instance
 * Automatically configured with keys from middleware or environment
 * Available in Server Components, Route Handlers, Server Actions, and API routes
 * @returns Promise resolving to ClerkClient instance
 */
function clerkClient(): Promise<ClerkClient>;

interface ClerkClient {
  /**
   * Users API - manage user accounts
   */
  users: UsersAPI;

  /**
   * Sessions API - manage user sessions
   */
  sessions: SessionsAPI;

  /**
   * Organizations API - manage organizations
   */
  organizations: OrganizationsAPI;

  /**
   * Email addresses API - manage email addresses
   */
  emailAddresses: EmailAddressesAPI;

  /**
   * Phone numbers API - manage phone numbers
   */
  phoneNumbers: PhoneNumbersAPI;

  /**
   * Invitations API - manage user invitations
   */
  invitations: InvitationsAPI;

  /**
   * Allowlist identifiers API - manage allowlist
   */
  allowlistIdentifiers: AllowlistIdentifiersAPI;

  /**
   * Blocklist identifiers API - manage blocklist
   */
  blocklistIdentifiers: BlocklistIdentifiersAPI;

  /**
   * Clients API - manage client instances
   */
  clients: ClientsAPI;

  /**
   * Domains API - manage domains
   */
  domains: DomainsAPI;

  /**
   * Redirect URLs API - manage redirect URLs
   */
  redirectUrls: RedirectUrlsAPI;

  /**
   * Sign-in tokens API - generate sign-in tokens
   */
  signInTokens: SignInTokensAPI;

  /**
   * Sign-ups API - manage sign-ups
   */
  signUps: SignUpsAPI;

  /**
   * Actor tokens API - manage actor tokens for impersonation
   */
  actorTokens: ActorTokensAPI;

  /**
   * API keys API - manage API keys
   */
  apiKeys: ApiKeysAPI;

  /**
   * Beta features API
   */
  betaFeatures: BetaFeaturesAPI;

  /**
   * Billing API (experimental) - manage billing and subscriptions
   */
  billing: BillingAPI;

  /**
   * IdP OAuth access tokens API - manage identity provider OAuth access tokens
   */
  idPOAuthAccessToken: IdPOAuthAccessTokenAPI;

  /**
   * Instance API - manage instance settings
   */
  instance: InstanceAPI;

  /**
   * JWKS API - manage JSON Web Key Sets
   */
  jwks: JwksAPI;

  /**
   * JWT templates API - manage custom JWT templates
   */
  jwtTemplates: JwtTemplatesAPI;

  /**
   * Machines API - manage machine-to-machine authentication
   */
  machines: MachinesAPI;

  /**
   * M2M tokens API - generate machine-to-machine tokens
   */
  m2m: M2MAPI;

  /**
   * OAuth applications API - manage OAuth applications
   */
  oauthApplications: OauthApplicationsAPI;

  /**
   * Proxy checks API - verify proxy configuration
   */
  proxyChecks: ProxyChecksAPI;

  /**
   * SAML connections API - manage SAML connections
   */
  samlConnections: SamlConnectionsAPI;

  /**
   * Testing tokens API - generate tokens for testing
   */
  testingTokens: TestingTokensAPI;

  /**
   * Waitlist entries API - manage waitlist entries
   */
  waitlistEntries: WaitlistEntriesAPI;

  /**
   * Webhooks API - manage webhooks
   */
  webhooks: WebhooksAPI;

  /**
   * Experimental accountless applications API (experimental, subject to change)
   */
  __experimental_accountlessApplications: any;
}

Usage Example - Server Component:

import { clerkClient } from '@clerk/nextjs/server';
import { auth } from '@clerk/nextjs/server';

export default async function UserProfile() {
  const { userId } = await auth();

  if (!userId) {
    return <div>Not signed in</div>;
  }

  const client = await clerkClient();
  const user = await client.users.getUser(userId);

  return (
    <div>
      <h1>{user.firstName} {user.lastName}</h1>
      <p>Email: {user.emailAddresses[0]?.emailAddress}</p>
    </div>
  );
}

Usage Example - Route Handler:

import { clerkClient } from '@clerk/nextjs/server';
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';

export async function GET() {
  const { userId } = await auth();

  if (!userId) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const client = await clerkClient();
  const user = await client.users.getUser(userId);

  return NextResponse.json({
    id: user.id,
    email: user.emailAddresses[0]?.emailAddress,
    name: `${user.firstName} ${user.lastName}`,
  });
}

Usage Example - Server Action:

'use server';

import { clerkClient } from '@clerk/nextjs/server';
import { auth } from '@clerk/nextjs/server';

export async function updateUserMetadata(data: Record<string, any>) {
  const { userId } = await auth();

  if (!userId) {
    throw new Error('Unauthorized');
  }

  const client = await clerkClient();

  await client.users.updateUserMetadata(userId, {
    publicMetadata: data,
  });

  return { success: true };
}

Users API

Complete user management operations.

/**
 * Users API - manage user accounts
 */
interface UsersAPI {
  /**
   * Get user by ID
   * @param userId - User ID
   * @returns Promise resolving to User object
   */
  getUser(userId: string): Promise<User>;

  /**
   * List all users
   * @param params - Query parameters
   * @returns Promise resolving to paginated user list
   */
  getUserList(params?: UserListParams): Promise<PaginatedResourceResponse<User[]>>;

  /**
   * Get count of users
   * @param params - Query parameters
   * @returns Promise resolving to user count
   */
  getCount(params?: CountParams): Promise<number>;

  /**
   * Create new user
   * @param params - User creation parameters
   * @returns Promise resolving to created User
   */
  createUser(params: CreateUserParams): Promise<User>;

  /**
   * Update user
   * @param userId - User ID
   * @param params - Update parameters
   * @returns Promise resolving to updated User
   */
  updateUser(userId: string, params: UpdateUserParams): Promise<User>;

  /**
   * Update user metadata
   * @param userId - User ID
   * @param metadata - Metadata to update
   * @returns Promise resolving to updated User
   */
  updateUserMetadata(
    userId: string,
    metadata: UserMetadataParams
  ): Promise<User>;

  /**
   * Delete user
   * @param userId - User ID
   * @returns Promise resolving to deleted User
   */
  deleteUser(userId: string): Promise<User>;

  /**
   * Ban user
   * @param userId - User ID
   * @returns Promise resolving to updated User
   */
  banUser(userId: string): Promise<User>;

  /**
   * Unban user
   * @param userId - User ID
   * @returns Promise resolving to updated User
   */
  unbanUser(userId: string): Promise<User>;

  /**
   * Lock user
   * @param userId - User ID
   * @returns Promise resolving to updated User
   */
  lockUser(userId: string): Promise<User>;

  /**
   * Unlock user
   * @param userId - User ID
   * @returns Promise resolving to updated User
   */
  unlockUser(userId: string): Promise<User>;

  /**
   * Get user's OAuth access token
   * @param userId - User ID
   * @param provider - OAuth provider name
   * @returns Promise resolving to OAuth access tokens
   */
  getUserOauthAccessToken(
    userId: string,
    provider: string
  ): Promise<OauthAccessToken[]>;

  /**
   * Get user's organization memberships
   * @param userId - User ID
   * @param params - Query parameters
   * @returns Promise resolving to organization memberships
   */
  getOrganizationMembershipList(
    userId: string,
    params?: OrganizationMembershipListParams
  ): Promise<PaginatedResourceResponse<OrganizationMembership[]>>;

  /**
   * Verify user's password
   * @param userId - User ID
   * @param password - Password to verify
   * @returns Promise resolving to verification result
   */
  verifyPassword(userId: string, password: string): Promise<{ verified: boolean }>;

  /**
   * Verify user's TOTP code
   * @param userId - User ID
   * @param code - TOTP code to verify
   * @returns Promise resolving to verification result
   */
  verifyTOTP(userId: string, code: string): Promise<{ verified: boolean; codeUsed: boolean }>;

  /**
   * Disable user's MFA
   * @param userId - User ID
   * @returns Promise resolving to updated User
   */
  disableMFA(userId: string): Promise<User>;
}

interface UserListParams {
  limit?: number;
  offset?: number;
  orderBy?: string;
  query?: string;
  userId?: string[];
  emailAddress?: string[];
  phoneNumber?: string[];
  username?: string[];
  web3Wallet?: string[];
  externalId?: string[];
  organizationId?: string[];
  lastActiveAtSince?: number;
}

interface CreateUserParams {
  externalId?: string;
  emailAddress?: string[];
  phoneNumber?: string[];
  username?: string;
  password?: string;
  firstName?: string;
  lastName?: string;
  publicMetadata?: Record<string, any>;
  privateMetadata?: Record<string, any>;
  unsafeMetadata?: Record<string, any>;
  skipPasswordChecks?: boolean;
  skipPasswordRequirement?: boolean;
  totpSecret?: string;
  backupCodes?: string[];
  createdAt?: string;
}

interface UpdateUserParams {
  externalId?: string;
  firstName?: string;
  lastName?: string;
  username?: string;
  password?: string;
  skipPasswordChecks?: boolean;
  signOutOfOtherSessions?: boolean;
  primaryEmailAddressID?: string;
  primaryPhoneNumberID?: string;
  primaryWeb3WalletID?: string;
  profileImageID?: string;
  totpSecret?: string;
  backupCodes?: string[];
  publicMetadata?: Record<string, any>;
  privateMetadata?: Record<string, any>;
  unsafeMetadata?: Record<string, any>;
  createdAt?: string;
}

interface UserMetadataParams {
  publicMetadata?: Record<string, any>;
  privateMetadata?: Record<string, any>;
  unsafeMetadata?: Record<string, any>;
}

Usage Example - Create User:

import { clerkClient } from '@clerk/nextjs/server';

export async function POST(req: Request) {
  const body = await req.json();
  const client = await clerkClient();

  const user = await client.users.createUser({
    emailAddress: [body.email],
    firstName: body.firstName,
    lastName: body.lastName,
    password: body.password,
    publicMetadata: {
      role: 'member',
    },
  });

  return Response.json({ userId: user.id });
}

Usage Example - Update User Metadata:

import { clerkClient } from '@clerk/nextjs/server';
import { auth } from '@clerk/nextjs/server';

export async function POST(req: Request) {
  const { userId } = await auth();

  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const body = await req.json();
  const client = await clerkClient();

  await client.users.updateUserMetadata(userId, {
    publicMetadata: {
      theme: body.theme,
      language: body.language,
    },
    privateMetadata: {
      subscriptionId: body.subscriptionId,
    },
  });

  return Response.json({ success: true });
}

Usage Example - List Users:

import { clerkClient } from '@clerk/nextjs/server';

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const query = searchParams.get('q');

  const client = await clerkClient();

  const users = await client.users.getUserList({
    query,
    limit: 20,
    orderBy: '-created_at',
  });

  return Response.json({
    users: users.data,
    totalCount: users.totalCount,
  });
}

Organizations API

Manage organizations and memberships.

/**
 * Organizations API - manage organizations
 */
interface OrganizationsAPI {
  /**
   * Get organization by ID
   */
  getOrganization(params: { organizationId: string }): Promise<Organization>;

  /**
   * List all organizations
   */
  getOrganizationList(params?: OrganizationListParams): Promise<PaginatedResourceResponse<Organization[]>>;

  /**
   * Create new organization
   */
  createOrganization(params: CreateOrganizationParams): Promise<Organization>;

  /**
   * Update organization
   */
  updateOrganization(
    organizationId: string,
    params: UpdateOrganizationParams
  ): Promise<Organization>;

  /**
   * Update organization metadata
   */
  updateOrganizationMetadata(
    organizationId: string,
    metadata: OrganizationMetadataParams
  ): Promise<Organization>;

  /**
   * Delete organization
   */
  deleteOrganization(organizationId: string): Promise<Organization>;

  /**
   * Get organization memberships
   */
  getOrganizationMembershipList(
    params: GetOrganizationMembershipListParams
  ): Promise<PaginatedResourceResponse<OrganizationMembership[]>>;

  /**
   * Create organization membership
   */
  createOrganizationMembership(
    params: CreateOrganizationMembershipParams
  ): Promise<OrganizationMembership>;

  /**
   * Update organization membership
   */
  updateOrganizationMembership(
    params: UpdateOrganizationMembershipParams
  ): Promise<OrganizationMembership>;

  /**
   * Delete organization membership
   */
  deleteOrganizationMembership(params: {
    organizationId: string;
    userId: string;
  }): Promise<OrganizationMembership>;

  /**
   * Update organization membership metadata
   */
  updateOrganizationMembershipMetadata(
    params: UpdateOrganizationMembershipMetadataParams
  ): Promise<OrganizationMembership>;

  /**
   * Get organization invitations
   */
  getOrganizationInvitationList(
    params: GetOrganizationInvitationListParams
  ): Promise<PaginatedResourceResponse<OrganizationInvitation[]>>;

  /**
   * Create organization invitation
   */
  createOrganizationInvitation(
    params: CreateOrganizationInvitationParams
  ): Promise<OrganizationInvitation>;

  /**
   * Revoke organization invitation
   */
  revokeOrganizationInvitation(params: {
    organizationId: string;
    invitationId: string;
  }): Promise<OrganizationInvitation>;
}

interface CreateOrganizationParams {
  name: string;
  slug?: string;
  createdBy: string;
  publicMetadata?: Record<string, any>;
  privateMetadata?: Record<string, any>;
  maxAllowedMemberships?: number;
}

interface UpdateOrganizationParams {
  name?: string;
  slug?: string;
  publicMetadata?: Record<string, any>;
  privateMetadata?: Record<string, any>;
  maxAllowedMemberships?: number;
}

interface CreateOrganizationMembershipParams {
  organizationId: string;
  userId: string;
  role: string;
}

interface CreateOrganizationInvitationParams {
  organizationId: string;
  emailAddress: string;
  inviterUserId: string;
  role: string;
  publicMetadata?: Record<string, any>;
  privateMetadata?: Record<string, any>;
  redirectUrl?: string;
}

Usage Example - Create Organization:

import { clerkClient } from '@clerk/nextjs/server';
import { auth } from '@clerk/nextjs/server';

export async function POST(req: Request) {
  const { userId } = await auth();

  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const { name, slug } = await req.json();
  const client = await clerkClient();

  const organization = await client.organizations.createOrganization({
    name,
    slug,
    createdBy: userId,
    publicMetadata: {
      plan: 'free',
    },
  });

  return Response.json({ organizationId: organization.id });
}

Usage Example - Manage Memberships:

import { clerkClient } from '@clerk/nextjs/server';

export async function POST(req: Request) {
  const { organizationId, userId, role } = await req.json();
  const client = await clerkClient();

  // Add member
  const membership = await client.organizations.createOrganizationMembership({
    organizationId,
    userId,
    role,
  });

  return Response.json({ membershipId: membership.id });
}

export async function DELETE(req: Request) {
  const { organizationId, userId } = await req.json();
  const client = await clerkClient();

  // Remove member
  await client.organizations.deleteOrganizationMembership({
    organizationId,
    userId,
  });

  return Response.json({ success: true });
}

Sessions API

Manage user sessions.

/**
 * Sessions API - manage user sessions
 */
interface SessionsAPI {
  /**
   * Get session by ID
   */
  getSession(sessionId: string): Promise<Session>;

  /**
   * List user's sessions
   */
  getSessionList(params?: SessionListParams): Promise<PaginatedResourceResponse<Session[]>>;

  /**
   * Revoke session
   */
  revokeSession(sessionId: string): Promise<Session>;

  /**
   * Verify session token
   */
  verifySession(sessionId: string, token: string): Promise<Session>;

  /**
   * Get session token
   */
  getToken(sessionId: string, template?: string): Promise<string>;
}

Usage Example - Revoke Session:

import { clerkClient } from '@clerk/nextjs/server';

export async function POST(req: Request) {
  const { sessionId } = await req.json();
  const client = await clerkClient();

  await client.sessions.revokeSession(sessionId);

  return Response.json({ success: true });
}

createClerkClient() Function

Creates a Clerk client with custom configuration options.

/**
 * Creates Clerk client with custom options
 * Useful when you need multiple clients or custom configuration
 * @param options - Client configuration options
 * @returns ClerkClient instance
 */
function createClerkClient(options: ClerkClientOptions): ClerkClient;

interface ClerkClientOptions {
  /**
   * Secret key for authentication
   */
  secretKey?: string;

  /**
   * Publishable key
   */
  publishableKey?: string;

  /**
   * API URL override
   */
  apiUrl?: string;

  /**
   * API version
   */
  apiVersion?: string;

  /**
   * User agent string
   */
  userAgent?: string;

  /**
   * Telemetry configuration
   */
  telemetry?: TelemetryOptions;
}

Usage Example - Custom Client:

import { createClerkClient } from '@clerk/nextjs/server';

const customClient = createClerkClient({
  secretKey: process.env.CUSTOM_CLERK_SECRET_KEY,
  publishableKey: process.env.CUSTOM_CLERK_PUBLISHABLE_KEY,
});

export async function GET() {
  const users = await customClient.users.getUserList();
  return Response.json({ users: users.data });
}

verifyToken() Function

Verifies a Clerk session token manually.

/**
 * Verifies Clerk session token
 * Use for custom token verification scenarios
 * @param token - Session token to verify
 * @param options - Verification options
 * @returns Promise resolving to JWT payload
 */
function verifyToken(
  token: string,
  options?: VerifyTokenOptions
): Promise<JWTPayload>;

interface VerifyTokenOptions {
  /**
   * Secret key for verification
   */
  secretKey?: string;

  /**
   * Publishable key
   */
  publishableKey?: string;

  /**
   * JWT key for verification
   */
  jwtKey?: string;

  /**
   * Authorized parties
   */
  authorizedParties?: string[];

  /**
   * Clock skew tolerance in milliseconds
   */
  clockSkewInMs?: number;

  /**
   * Skip JWT key validation
   */
  skipJwksCache?: boolean;
}

interface JWTPayload {
  /**
   * User ID
   */
  sub: string;

  /**
   * Session ID
   */
  sid: string;

  /**
   * Organization ID
   */
  org_id?: string;

  /**
   * Organization role
   */
  org_role?: string;

  /**
   * Organization slug
   */
  org_slug?: string;

  /**
   * Organization permissions
   */
  org_permissions?: string[];

  /**
   * Issued at timestamp
   */
  iat: number;

  /**
   * Expiration timestamp
   */
  exp: number;

  /**
   * Additional claims
   */
  [key: string]: any;
}

Usage Example - Verify Token:

import { verifyToken } from '@clerk/nextjs/server';

export async function POST(req: Request) {
  const token = req.headers.get('Authorization')?.replace('Bearer ', '');

  if (!token) {
    return Response.json({ error: 'No token provided' }, { status: 401 });
  }

  try {
    const payload = await verifyToken(token);

    return Response.json({
      userId: payload.sub,
      sessionId: payload.sid,
      orgId: payload.org_id,
    });
  } catch (error) {
    return Response.json({ error: 'Invalid token' }, { status: 401 });
  }
}

Shared Types

/**
 * Paginated response wrapper
 */
interface PaginatedResourceResponse<T> {
  data: T;
  totalCount: number;
}

Requirements

Backend API functions require:

  1. Environment variables properly set (CLERK_SECRET_KEY)
  2. Server-side context (cannot be used on client)
  3. Authentication via middleware for protected operations