Update a user (admin)
Update any field on any user via the admin API.
Update a user's email, phone, password, metadata, ban status, or role from the server. Unlike update_user (which mutates the current signed-in user), this method can target any user in the project.
Service-role key required
This endpoint requires the project's service_role key. Never call it from a browser, mobile app, or any client you don't fully control. In particular, app_metadata and role MUST never be writeable by end-users — that's the whole point of routing those changes through this admin endpoint.
Signature
supabase.auth.admin.update_user_by_id(uid, attributes)uid is a positional UUID String; attributes is a positional hash. Raises ArgumentError synchronously if uid isn't a syntactically valid UUID.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
uid | String | Required | The user's UUID (the value of user.id). Must be a syntactically valid UUID v4. |
attributes | Hash | Required | Fields to mutate. Any omitted field is left unchanged. See attribute keys below. |
attributes keys
| Name | Type | Required | Description |
|---|---|---|---|
email | String | Optional | Change the user's email. Skips the normal email-confirmation flow — the new address is set immediately. |
phone | String | Optional | Change the user's phone (E.164 format). Skips the normal SMS-confirmation flow. |
password | String | Optional | Reset the user's password directly. The user's current sessions remain valid; revoke them with sign_out if you need to force reauthentication. |
email_confirm | Boolean | Optional | Force the email to be marked confirmed without sending a confirmation link. |
phone_confirm | Boolean | Optional | Force the phone to be marked confirmed without sending a confirmation code. |
user_metadata | Hash | Optional | Replace user_metadata. NOT a merge — pass the full desired hash. Visible in JWT claims and editable by the user themselves via update_user. |
app_metadata | Hash | Optional | Replace app_metadata. NOT a merge. Only writeable via the admin API — safe to use for roles, plan tier, feature flags. |
role | String | Optional | Postgres role embedded in the JWT for RLS (defaults to "authenticated"). Set to "service_role" only with extreme caution. |
ban_duration | String | Optional | Go duration string (e.g. "24h"). Set to "none" to lift an active ban. |
nonce | String | Optional | Used internally for the password-recovery reauthentication flow — rarely set directly. |
Returns
A Struct with a single :user field carrying the updated Types::User. Raises Supabase::Auth::Errors::AuthApiError (status 404) if the user does not exist, or status 422 on validation failures (e.g. duplicate email). Raises ArgumentError synchronously if uid isn't a valid UUID.
Example — promote a user to a paid plan
response = supabase.auth.admin.update_user_by_id(
"8d7f5c4b-1234-4abc-9def-1234567890ab",
app_metadata: { plan: "pro", plan_started_at: Time.now.iso8601 }
)
response.user.app_metadata["plan"] # => "pro"Example — force-reset a password
supabase.auth.admin.update_user_by_id(
user_id,
password: SecureRandom.base64(32)
)
# Existing sessions remain valid! Pair with sign_out if you need to force reauth.
supabase.auth.admin.sign_out(user_access_token, "global")Example — ban a user for 24 hours
supabase.auth.admin.update_user_by_id(user_id, ban_duration: "24h")
# Later, lift the ban early:
supabase.auth.admin.update_user_by_id(user_id, ban_duration: "none")Example — change email without confirmation
# Set the new email AND mark it confirmed in one call to skip the email-link round-trip.
supabase.auth.admin.update_user_by_id(
user_id,
email: "new-address@example.com",
email_confirm: true
)Validates the uid UUID format client-side with Helpers.is_valid_uuid and raises ArgumentError before any HTTP call.