Docs

Admin API (auth.admin)

Create, list, update, ban, invite and delete app users from your server code and generate sign-in, recovery and invite links: supabase's auth.admin in every SDK, with who may call it, limits and errors.

Updated Oct 31, 2026

auth.admin manages your app users from code you run yourself — a back office, a support tool, a migration script, a cron job. It is supabase's admin API with the same names. It is the same work the console's Users page does, under the same checks.

import { createClient } from '@sovaria/sdk'

const admin = createClient(SOVARIA_URL, SERVER_KEY, { results: 'supabase' })   // or a team member's session

const { data: { user }, error } = await admin.auth.admin.createUser({
  email: 'ada@example.com', password: 'Correct-Horse-9!', email_confirm: true,
  user_metadata: { name: 'Ada' }, app_metadata: { plan: 'pro' }, roles: ['librarian'],
})
const { data: page } = await admin.auth.admin.listUsers({ page: 1, perPage: 100 })     // { users, nextPage, lastPage, total }
const { data: one } = await admin.auth.admin.getUserById(user.id)
await admin.auth.admin.updateUserById(user.id, { app_metadata: { plan: 'team' }, ban_duration: '24h' })
await admin.auth.admin.inviteUserByEmail('grace@example.com', { data: { team: 'blue' } })
// A temporary password made by the server, returned once; the user must change it at first sign-in:
const { data: first } = await admin.auth.admin.createUser({ email: 'lin@example.com', roles: ['STAFF'], temporary_password: true })
// first.temporaryPassword → hand it to Lin (never log or store it)
const { data: link } = await admin.auth.admin.generateLink({ type: 'recovery', email: 'ada@example.com' })
const { data: signup } = await admin.auth.admin.generateLink({ type: 'signup', email: 'lin@example.com', password: 'Correct-Horse-9!' })
await admin.auth.admin.deleteUser(user.id)

#Every call

CallWhat it doesHTTP
createUser({ email, password?, email_confirm?, user_metadata?, app_metadata?, roles? })Creates the user. Without a password they sign in by link or code. email_confirm skips the confirmation email.POST /projects/{id}/auth/users, then PATCH …/users/{uid} for metadata
listUsers({ page, perPage })Newest first, up to 500 a page and 50 pages deep (use auth.productUsers.listPage and its cursor for more). total comes with the answer.GET /projects/{id}/auth/users
getUserById(id)The Users row plus user_metadata, app_metadata, custom claims and banned.GET /projects/{id}/auth/users/{uid}
updateUserById(id, { user_metadata?, app_metadata?, email_confirm?, roles?, ban_duration? })Metadata is merged (a null value removes the key); app_metadata only admins can set; ban_duration: 'none' lifts a ban, any other value bans until you lift it.PATCH …/users/{uid}, PUT …/roles, POST …/ban
deleteUser(id)Deletes the user and their sessions.DELETE /projects/{id}/auth/users/{uid}
inviteUserByEmail(email, { data? })Creates a passwordless user and sends your project's sign-in email.POST /projects/{id}/auth/users (send_magic_link)
generateLink({ type, email, password?, options })Returns the link an email would carry — magiclink, recovery, invite (creates a passwordless user) or signup (creates a user with password, address not yet confirmed; the link confirms it) — with the 6-digit email_otp (sign-in links) and the hashed_token for verifyOtp({ token_hash, type }). created: true when the call created the user. Nothing is emailed. CLI: sov auth-product link --type signup --email ….POST /projects/{id}/auth/generate-link
mfa.listFactors({ userId }) / mfa.deleteFactors({ userId })A user's second factors; remove them all (they enroll again; backup codes are revoked).GET / POST /projects/{id}/auth/users/{uid}/mfa[/reset]

#Security and limits

  • Every call checks that the caller administers this project's users; a user of another project is a 404.
  • generateLink is for org owners and admins, with a team member's session (API keys are refused): at most 60 links an hour per admin, and each one is written to the audit log (auth.admin.link_generated, with the type and the user). The link signs that user in — send it only to them, never log it.
  • Invite links last 24 hours; magic links, recovery links and signup confirmation links last as long as the emailed ones (Auth › Settings). A signup password must pass the project's password policy, and the audit entry records created: true (never the password).
  • User changes are recorded in the project history by field name (values can be personal data, so they are not).
  • Email, password and phone belong to the user: they change them with updateUser, or you send a recovery link.

#Errors

StatusMeaning
401No session, or an expired one.
403Not an owner/admin of the organization, an app user's session, or an API key where a person is needed (generateLink).
404No such user in this project (or, for magiclink/recovery, no active user with that email).
409EMAIL_EXISTS: the address already has an account in this project (createUser, invite, signup).
422A reserved claim, metadata that is too large, a signup link without a password (PASSWORD_REQUIRED) or one the password policy refuses.
429ADMIN_LINK_RATE_LIMITED: more than 60 links in the last hour.