Admin API

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/users

Requires 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();
FieldTypeRequiredDescription
didstringyesThe atproto DID of the user to add
templatestringnoPermission template: viewer, operator, manager, or full_access
permissionsstring[]noExplicit 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/users

Requires 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}/permissions

Requires 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();
FieldTypeRequiredDescription
grantstring[]noPermissions to add
revokestring[]noPermissions to remove

Response: 200 OK with the updated user object.

Transfer super user

POST /admin/users/transfer-super

Only 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",
  }),
});
FieldTypeRequiredDescription
target_user_idstringyesThe 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