Skip to main content

listUserApiKeys

Lists all API keys (both active and revoked) for the authenticated user. Use this to display API key management in your settings UI.

Parameters

No parameters required.

Returns

keys
Array<ApiKey>
Array of API key objects

Usage

Display in Settings

import { useQuery, useMutation } from "convex/react";
import { api } from "@teak/convex";

function ApiKeyList() {
  const apiKeys = useQuery(api.apiKeys.listUserApiKeys);
  const revokeKey = useMutation(api.apiKeys.revokeUserApiKey);
  
  if (!apiKeys) return <div>Loading...</div>;
  
  const activeKeys = apiKeys.filter(k => !k.revokedAt);
  const revokedKeys = apiKeys.filter(k => k.revokedAt);
  
  return (
    <div>
      <h2>Active API Keys</h2>
      {activeKeys.length === 0 ? (
        <p>No active API keys</p>
      ) : (
        <ul>
          {activeKeys.map(key => (
            <li key={key.id}>
              <div>
                <strong>{key.name}</strong>
                <code>{key.maskedKey}</code>
              </div>
              <div>
                <small>Created: {new Date(key.createdAt).toLocaleDateString()}</small>
                {key.lastUsedAt && (
                  <small>Last used: {new Date(key.lastUsedAt).toLocaleDateString()}</small>
                )}
              </div>
              <button onClick={() => revokeKey({ keyId: key.id })}>
                Revoke
              </button>
            </li>
          ))}
        </ul>
      )}
      
      {revokedKeys.length > 0 && (
        <>
          <h3>Revoked Keys</h3>
          <ul>
            {revokedKeys.map(key => (
              <li key={key.id}>
                <code>{key.maskedKey}</code>
                <small>(revoked)</small>
              </li>
            ))}
          </ul>
        </>
      )}
    </div>
  );
}

Example Response

[
  {
    "id": "jh7x5t9p2k8n4m1q6r3s",
    "name": "Raycast Extension",
    "keyPrefix": "a1b2c3d4e5f6",
    "maskedKey": "a1b2c3d4e5f6••••••••",
    "access": "full_access",
    "createdAt": 1704067200000,
    "updatedAt": 1704067200000,
    "lastUsedAt": 1704153600000
  },
  {
    "id": "kj8y6u0q3l9o5n2r7t4u",
    "name": "API Keys",
    "keyPrefix": "b2c3d4e5f6g7",
    "maskedKey": "b2c3d4e5f6g7••••••••",
    "access": "full_access",
    "createdAt": 1703980800000,
    "updatedAt": 1703980800000,
    "revokedAt": 1704067200000
  }
]

Key States

Active Keys

Keys without a revokedAt timestamp are active and can be used for authentication.
const activeKeys = apiKeys.filter(key => !key.revokedAt);

Revoked Keys

Keys with a revokedAt timestamp have been revoked and cannot be used.
const revokedKeys = apiKeys.filter(key => key.revokedAt);

Display Format

Masked Key

The maskedKey field shows the prefix followed by bullets for secure display:
a1b2c3d4e5f6••••••••
This allows users to identify their keys without exposing the secret.
Never display the full API key after creation. The secret portion is not retrievable from the database.

Last Used

The lastUsedAt field is updated whenever the API key is used to authenticate a request. This helps track which keys are actively used.
lastUsedAt is undefined for keys that have never been used.

Sorting

Keys are returned in reverse chronological order (newest first) by creation date.
// Keys are already sorted by createdAt descending
const mostRecentKey = apiKeys[0];

Authentication

This query requires an authenticated user session. Unauthenticated requests return an empty array.
// Unauthenticated
const keys = await client.query(api.apiKeys.listUserApiKeys);
// keys === []

Performance

This query uses the by_user_revoked index for efficient lookups:
.withIndex("by_user_revoked", (q) =>
  q.eq("userId", userId)
)
Performance is O(log n + k) where k is the number of keys for the user (typically 1-5).

Create API Key

Generate a new API key

Revoke API Key

Revoke an existing API key

Authentication

Learn how to use API keys

Build docs developers (and LLMs) love