Skip to content

API keys

Manage API keys for programmatic access. See the API keys guide for usage details.

const TOKEN = "hv_..."; // your API key
const headers = { Authorization: `Bearer ${TOKEN}` };
POST /admin/api-keys

Requires api-keys:create permission.

interface ApiKey {
id: string;
name: string;
key: string;
key_prefix: string;
permissions: string[];
}
const response = await fetch("http://127.0.0.1:3000/admin/api-keys", {
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "CI Deploy",
permissions: ["lexicons:read", "lexicons:create", "backfill:create"],
}),
});
const data: ApiKey = await response.json();
FieldTypeRequiredDescription
namestringyesA label to identify this key’s usage
permissionsstring[]yesPermissions to grant the key. A permission you don’t hold yourself returns 403 Forbidden. A string that names no permission is stored but grants nothing

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "CI Deploy",
"key": "hv_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"key_prefix": "hv_a1b2c3d4",
"permissions": ["lexicons:read", "lexicons:create", "backfill:create"]
}

The key field contains the full API key. It is only returned in this response, so store it securely. The key’s effective permissions are the intersection of the permissions specified here and the creating operator’s permissions at the time of each request. A key never carries the superadmin privilege, even when its operator holds it, so a superadmin-only endpoint such as POST /admin/users/transfer-super refuses every API key.

GET /admin/api-keys

Requires api-keys:read permission.

interface ApiKeySummary {
id: string;
name: string;
key_prefix: string;
permissions: string[];
created_at: string;
last_used_at: string | null;
revoked_at: string | null;
}
const response = await fetch("http://127.0.0.1:3000/admin/api-keys", {
headers,
});
const data: ApiKeySummary[] = await response.json();

Response: 200 OK

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "CI Deploy",
"key_prefix": "hv_a1b2c3d4",
"permissions": ["lexicons:read", "lexicons:create", "backfill:create"],
"created_at": "2026-03-01T00:00:00Z",
"last_used_at": "2026-03-06T12:00:00Z",
"revoked_at": null
}
]

Only returns keys belonging to the caller. The full key is never included, only the prefix.

DELETE /admin/api-keys/{id}

Requires api-keys:delete permission.

const response = await fetch(
"http://127.0.0.1:3000/admin/api-keys/550e8400-e29b-41d4-a716-446655440000",
{ method: "DELETE", headers },
);

Sets revoked_at on the key. The key remains in the database for audit purposes but can no longer authenticate.

Response: 204 No Content. Returns 404 Not Found if the caller has no unrevoked key with that id.