Documentation
ReferenceJavaScript SDKNamespacesauth.admin

auth.admin

Administering users, roles, grants and OAuth applications.

Server client only, and every method here acts as the platform — not as a signed-in person. That means no permission is enforced on the caller and nothing records who asked. Log that on your side; the audit trail cannot recover it for you.

The line to hold in mind: auth.grants is a user doing something, auth.admin.grants is your back office doing something. Reaching for admin because it is easier removes the enforcement you wanted.

Four namespaces sit under auth.admin:

admin.usersAdminUsersClientOptional

Creating, listing, updating, banning — and the support paths for sessions and factors.

admin.rolesAdminRolesClientOptional

Defining roles and the scopes they carry.

admin.grantsAdminGrantsClientOptional

Assigning access directly, with no invitation.

admin.oauthClientsAdminOAuthClientsClientOptional

Registering applications allowed to request tokens from your issuer.

admin.users

create · list · get · update · delete · ban · unban

await nulvless.auth.admin.users.create(params);
await nulvless.auth.admin.users.list({ page, page_size });
await nulvless.auth.admin.users.get(userId);
await nulvless.auth.admin.users.update(userId, params);
await nulvless.auth.admin.users.delete(userId);

create

emailstringOptional
phonePhoneOptional
usernamestringOptional
passwordstringOptional

Omit to create a user who has no way to sign in yet — useful when you invite them to set one.

profileUserProfileOptional
metadataRecord<string, string | number>Optional
email_verifiedbooleanOptional

Create the user with the email already verified.

phone_verifiedbooleanOptional

Create the user with the phone already verified. Requires a plan with phone authentication.

Unlike auth.signUp nothing is strictly required here, because an admin may be seeding a record ahead of the person. A user with no identity cannot sign in, so pass at least one of email, phone or username unless that is the point.

When every credential you send is marked as verified (email with email_verified, phone with phone_verified), the user is created confirmed: no confirmation is sent, no session is created, and they can sign in right away. Otherwise the user goes through the confirmation your platform's sign-up settings define, such as a link sent to the email.

list

pagenumberOptional
page_sizenumberOptional
sortPaginationSortOptional
emailstringOptional
Filters, all optional.
usernamestringOptional
phonestringOptional
idstringOptional
is_confirmedbooleanOptional
is_bannedbooleanOptional

Returns Paginated<User>.

update

Takes userId, then a narrower set than create:

usernamestringOptional
profileUserProfileOptional
metadataRecord<string, string | number | null>Optional

Merges into what is stored, so you only send the keys you are changing. Send a key as null to remove it. Omit the field to leave everything untouched.

Identities are deliberately absent. An email or phone is changed through its own confirmed flow in auth.users, never edited in place by an admin — otherwise an administrator could point an account at an address nobody proved they own.

The same fields are editable by the user themselves through auth.users.update. Reach for the admin path only when acting on someone else.

Cutting access

await nulvless.auth.admin.users.ban(userId);
await nulvless.auth.admin.users.unban(userId);

Banning stops sign-in and leaves everything else intact, so the history behind the account still resolves. Deleting is for when the record itself must go — remember their grants do not disappear with them, so clean those up too.

Sessions and factors

await nulvless.auth.admin.users.getSessions(userId);
await nulvless.auth.admin.users.revokeSession(userId, sessionId);
await nulvless.auth.admin.users.signOutAll(userId);

await nulvless.auth.admin.users.getFactors(userId);
await nulvless.auth.admin.users.removeFactor(userId, factorId);

signOutAll is what you call after a compromise — it ends every session the user has, everywhere.

removeFactor is the support path for someone who lost both their authenticator and their recovery codes. It strips a security control with no confirmation from the user, so it must sit behind whatever verification your support process requires. The platform will not second-guess an admin key.

admin.roles

list · create · update · delete

A role is a named bundle of scopes inside a context. Changing the scopes on a role changes what every holder can do, without touching a single grant — which is why it, and not the grant, is where permission changes belong.

create

const { data: role } = await nulvless.auth.admin.roles.create({
  name: 'Administrator',
  key: 'admin',
  context: 'org',
  scopes: ['members:read', 'members:write', 'projects:read'],
});
namestringRequired

Human-readable. What a members screen shows.

keystringRequired

The stable identifier, and what verify({ roles }) matches against. Not the name.

contextstringRequired

Which kind of resource this role applies to. Fixed once created.

scopesstring[]Required

What the role grants.

descriptionstringOptional

list

list(context) → Role[]

const { data: roles } = await nulvless.auth.admin.roles.list('org');

The roles defined for one context. This is where the role_id values for grants.invite come from — read them once at start-up and map them, rather than looking them up per invitation.

update

Takes role_id, then name, description or scopes. key and context are fixed, because changing either would silently reinterpret every existing grant.

admin.grants

list · set · delete · deleteByResource

set

Assigns access directly — no invitation, no acceptance step. The grant exists immediately with status: 'granted'.

await nulvless.auth.admin.grants.set({
  resource: org.slug,
  granted_to_user_id: creatorId,
  role_id: ownerRoleId,
});
resourcestringRequired
granted_to_user_idstringRequired

The user must already exist. Unlike invite, this cannot address someone by email.

role_idstringOptional

Omit to grant bare scopes with no role — useful for a service account that no role should describe.

scopesstring[]Optional

The canonical use is seeding the first owner. Whoever created a resource should not have to accept an invitation to it, and doing this in the same transaction that creates the resource means a failure leaves no orphan nobody can administer.

list

pagenumberOptional
page_sizenumberOptional
sortPaginationSortOptional
resourcestringOptional
contextstringOptional
granted_to_user_idstringOptional
role_idstringOptional

Returns Paginated<Grant>. Filtering by granted_to_user_id answers "what does this person reach?" — the back-office question auth.grants.list answers only for the caller.

deleteByResource

await nulvless.auth.admin.grants.deleteByResource(org.slug);

Clears every grant on one resource at once. Call it when a project or organization is deleted.

Skipping this leaves grants pointing at nothing. Harmless until a slug is reused — at which point the old members quietly reappear on the new resource.

admin.oauthClients

create · list · get · update · delete · regenerateSecret

await nulvless.auth.admin.oauthClients.create(params);
await nulvless.auth.admin.oauthClients.list();
await nulvless.auth.admin.oauthClients.get(id);
await nulvless.auth.admin.oauthClients.update(id, params);
await nulvless.auth.admin.oauthClients.delete(id);
await nulvless.auth.admin.oauthClients.regenerateSecret(id);

These are the applications allowed to request tokens from your issuer — the other side of auth.oauth.

regenerateSecret rotates a client secret and the old one stops working immediately. There is no overlap window, so coordinate it with whoever operates that integration rather than treating it as routine hygiene.

Next

On this page