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
Array of API key objects
Unique identifier for the API key record
Friendly name of the API key
The 12-character prefix for identification (e.g., a1b2c3d4e5f6)
Masked version of the key for display (e.g., a1b2c3d4e5f6••••••••)
Access level granted to this key
Unix timestamp when the key was created
Unix timestamp when the key was last modified
Unix timestamp when the key was last used (undefined if never used)
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);
Masked Key
The maskedKey field shows the prefix followed by bullets for secure display:
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 === []
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