Users
Manage admin users and their permissions. See the Permissions guide for available permissions and templates.
const TOKEN = "hv_..."; // your API key
const headers = { Authorization: `Bearer ${TOKEN}` };Create a user
POST /admin/usersRequires users:create permission. You cannot grant permissions you don't have yourself (escalation guard).
interface User {
id: string;
did: string;
is_super: boolean;
permissions: string[];
}
const response = await fetch("http://127.0.0.1:3000/admin/users", {
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
},
body: JSON.stringify({
did: "did:plc:newuser",
template: "operator",
}),
});
const data: User = await response.json();| Field | Type | Required | Description |
|---|---|---|---|
did | string | yes | The atproto DID of the user to add |
template | string | no | Permission template: viewer, operator, manager, or full_access |
permissions | string[] | no | Explicit list of permissions to grant (used instead of or in addition to template) |
If neither template nor permissions is provided, the user is created with no permissions.
Response: 201 Created
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"did": "did:plc:newuser",
"is_super": false,
"permissions": ["lexicons:read", "records:read", "script-variables:read", "users:read", "api-keys:read", "api-keys:create", "api-keys:delete", "backfill:read", "backfill:create", "stats:read", "events:read"]
}List users
GET /admin/usersRequires users:read permission.
interface UserWithTimestamps {
id: string;
did: string;
is_super: boolean;
permissions: string[];
created_at: string;
last_used_at: string;
}
const response = await fetch("http://127.0.0.1:3000/admin/users", {
headers,
});
const data: UserWithTimestamps[] = await response.json();Response: 200 OK
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"did": "did:plc:admin",
"is_super": true,
"permissions": ["lexicons:create", "lexicons:read", "lexicons:delete", "records:read", "records:delete", "records:delete-collection", "script-variables:create", "script-variables:read", "script-variables:delete", "users:create", "users:read", "users:update", "users:delete", "api-keys:create", "api-keys:read", "api-keys:delete", "backfill:create", "backfill:read", "stats:read", "events:read"],
"created_at": "2025-01-01T00:00:00Z",
"last_used_at": "2025-01-02T12:00:00Z"
}
]Get a user
GET /admin/users/{id}Requires users:read permission.
const response = await fetch(
"http://127.0.0.1:3000/admin/users/550e8400-e29b-41d4-a716-446655440000",
{ headers },
);
const data: UserWithTimestamps = await response.json();Response: 200 OK with the same shape as a single item from the list response.
Update user permissions
PATCH /admin/users/{id}/permissionsRequires users:update permission. You cannot grant permissions you don't have yourself, and you cannot modify the super user's permissions.
const response = await fetch(
"http://127.0.0.1:3000/admin/users/550e8400-e29b-41d4-a716-446655440000/permissions",
{
method: "PATCH",
headers: {
...headers,
"Content-Type": "application/json",
},
body: JSON.stringify({
grant: ["lexicons:create", "lexicons:delete"],
revoke: ["records:delete"],
}),
},
);
const data: User = await response.json();| Field | Type | Required | Description |
|---|---|---|---|
grant | string[] | no | Permissions to add |
revoke | string[] | no | Permissions to remove |
Response: 200 OK with the updated user object.
Transfer super user
POST /admin/users/transfer-superOnly the current super user can call this endpoint. Transfers super user status to another existing user.
const response = await fetch("http://127.0.0.1:3000/admin/users/transfer-super", {
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
},
body: JSON.stringify({
target_user_id: "550e8400-e29b-41d4-a716-446655440000",
}),
});| Field | Type | Required | Description |
|---|---|---|---|
target_user_id | string | yes | The ID of the user to receive super status |
Response: 200 OK
Delete a user
DELETE /admin/users/{id}Requires users:delete permission. You cannot delete the super user or yourself.
const response = await fetch(
"http://127.0.0.1:3000/admin/users/550e8400-e29b-41d4-a716-446655440000",
{
method: "DELETE",
headers,
},
);Response: 204 No Content