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

# Admin Users

> Admin user management, access control, and invitations via Clerk

## Overview

Admin Users API manages platform administrator accounts through Clerk. Provides tools to list current admins, grant or revoke admin access, invite new admins, and search for existing users. All functions are Convex actions (Node.js runtime) that interact with the Clerk API. All functions require admin authorization.

**Location:** `convex/admins/adminUsers.ts`

***

## List Admin Users

Lists all users who have the `admin` role set in their Clerk public metadata. Returns up to 100 users.

*No arguments required.*

<CodeGroup>
  ```typescript TypeScript theme={null}
  const admins = await convex.action(api.admins.adminUsers.listAdminUsers, {});
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "id": "user_abc123",
      "firstName": "Ali",
      "lastName": "Ahmed",
      "email": "ali@example.com",
      "imageUrl": "https://img.clerk.com/...",
      "createdAt": 1700000000000,
      "lastSignInAt": 1702500000000
    },
    {
      "id": "user_def456",
      "firstName": "Sara",
      "lastName": null,
      "email": "sara@example.com",
      "imageUrl": "https://img.clerk.com/...",
      "createdAt": 1701000000000,
      "lastSignInAt": null
    }
  ]
  ```
</ResponseExample>

<Note>
  Fields `firstName`, `lastName`, `email`, and `lastSignInAt` may be `null`. The email is taken from the user's first email address in Clerk.
</Note>

***

## Grant Admin Access

Grants admin access to an existing Clerk user by setting `role: "admin"` in their public metadata. The user must already exist in Clerk.

<ParamField path="email" type="string" required>
  Email address of the existing Clerk user to promote
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.action(api.admins.adminUsers.grantAdminAccess, {
    email: "newadmin@example.com"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "userId": "user_xyz789",
    "name": "Mohammed Khan"
  }
  ```
</ResponseExample>

<Note>
  Throws an error if no user is found with the given email or if the user is already an admin. The `name` field is constructed from Clerk's `firstName` and `lastName`, defaulting to "Unknown" if both are empty.
</Note>

***

## Revoke Admin Access

Removes admin access from a user by removing the `role` key from their Clerk public metadata. Prevents self-revocation.

<ParamField path="userId" type="string" required>
  Clerk user ID to revoke admin access from
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.action(api.admins.adminUsers.revokeAdminAccess, {
    userId: "user_xyz789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true
  }
  ```
</ResponseExample>

<Note>
  Throws an error if the requesting admin attempts to revoke their own access (`identity.subject === args.userId`).
</Note>

***

## Invite Admin

Sends a Clerk invitation email to a new user with admin role metadata. The invited user will be granted admin access upon signing up.

<ParamField path="email" type="string" required>
  Email address to send the admin invitation to
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.action(api.admins.adminUsers.inviteAdmin, {
    email: "newhire@example.com"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "invitationId": "inv_abc123"
  }
  ```
</ResponseExample>

<Note>
  Throws an error if a user with the given email already exists in Clerk. In that case, use `grantAdminAccess` instead. The invitation is created with `{ role: "admin" }` public metadata so the user is automatically an admin upon signup.
</Note>

***

## Search User By Email

Searches for an existing Clerk user by email address. Used in the admin panel's "add admin" dialog to preview user details before granting access.

<ParamField path="email" type="string" required>
  Email address to search for
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const user = await convex.action(api.admins.adminUsers.searchUserByEmail, {
    email: "someone@example.com"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Found Response theme={null}
  {
    "id": "user_xyz789",
    "firstName": "Mohammed",
    "lastName": "Khan",
    "email": "someone@example.com",
    "imageUrl": "https://img.clerk.com/...",
    "isAdmin": false
  }
  ```

  ```json Not Found Response theme={null}
  null
  ```
</ResponseExample>

<Note>
  Returns `null` if no user is found with the given email. The `isAdmin` flag indicates whether the user already has `role: "admin"` in their Clerk public metadata.
</Note>
