Users with SDKs

You can use the Descope management SDK for common user management operations like create user, update user, delete user, etc. The management SDK requires a management key, which can be generated here.

Backend SDK

Install SDK

Terminal
npm i --save @descope/node-sdk

Import and initialize SDK

import DescopeClient from '@descope/node-sdk';
try{
    //  baseUrl="<URL>" // When initializing the Descope client, you can also configure the baseUrl ex: https://auth.company.com  - this is useful when you utilize a custom domain within your Descope project.
    const descopeClient = DescopeClient({ projectId: '__ProjectID__' });
} catch (error) {
    // handle the error
    console.log("failed to initialize: " + error)
}

Create User

This operation creates a new user within the project with the details provided. Create will not send an invite. If you want to send an invite on creation, use Invite User.

// Args:
//    loginId (str): user login_id.
const loginId = "custom-login-id";
//    displayName (str): Optional user display name.
const displayName = "Joe Person";
//    phone (str): Optional user phone number.
const phone = "+15555555555";
//    email (str): Optional user email address.
const email = "email@company.com";
//    userTenants (List[UserTenants]): An optional list of the user's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
const userTenants = [{ tenantId: "TestTenant", roleNames: ["TestRole"] }];
//    roles (List[str]): An optional list of the user's role names without tenant association. These roles are mutually exclusive with the `user_tenant` roles, which take precedence over them.
const roles = ["Tenant Admin"];
//   customAttributes (dict): Optional, set the different custom attributes values of the keys that were previously configured in Descope console app
const customAttributes = { attribute1: "Value 1", attribute2: "Value 2" };
//   picture (str): Optional url for user picture
const picture = "https://example.com/picture.jpg";
//   verifiedEmail (bool): Set to true for the user to be able to login with the email address.
const verifiedEmail = true; // or false
//   verifiedPhone (bool): Set to true for the user to be able to login with the phone number.
const verifiedPhone = true; // or false
//   additionalLoginIds (optional List[str]): An optional list of additional login IDs to associate with the user
const additionalLoginIds = ["MyUserName", "+12223334455"];
// templateId (str): Optional template Id for the invitation message
const templateId = "my-template-id"


// A user must have a login ID, other fields are optional. Roles should be set directly if no tenants exist, otherwise set on a per-tenant basis.
const resp = await descopeClient.management.user.create(
  loginId,
  {
      email,
      phone,
      displayName,
      // picture,
      verifiedEmail,
      verifiedPhone,
      customAttributes,
      additionalLoginIds,
      // userTenants,// either userTenants or roles
      roles,
      templateId,
  }
);
if (!resp.ok) {
  console.log("Failed to create user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully created user.")
  console.log(resp.data)
}

Batch Create Users

This operation creates multiple users within the project in a single request. Batch creation comes in two behaviors, which differ only in whether the new users are notified:

  • Create provisions the users directly, without sending anything. Created users are active immediately. Use this for migrations and for programmatic provisioning where you do not want your users to be contacted.
  • Invite provisions the users and sends each one an invitation via email or SMS. Invited users have a status of invited until they sign in for the first time.

Note

A batch request may partially succeed. Always inspect the failedUsers field in the response alongside createdUsers.

Note

A cleartext password on any user in the batch caps the whole request at 100 users. Batches that use only hashedPassword, or carry no password, have no such cap.

// Args:
//    users (array of Descope Users)
//      loginIdOrUserId (str): user login ID or user ID. When a user ID is provided with inviteBatch, the user must already exist — no new user is created, and the invite is resent.
//      email (str): Optional user email address.
//      phone (str): Optional user phone number.
//      displayName (str): Optional user display name.
//      roles (List[str]): An optional list of the user's roles without tenant association. These roles are mutually exclusive with the `user_tenant` roles, which take precedence over them.
//      userTenants (List[UserTenants]): An optional list of the user's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
//      customAttributes (dict): Optional, set the different custom attributes values of the keys that were previously configured in Descope console app
//      picture (str): Optional url for user picture
//      verifiedEmail (bool): Set to true for the user to be able to login with the email address.
//      verifiedPhone (bool): Set to true for the user to be able to login with the phone number.
//      test (bool): Set to true if creating a test user, otherwise false
//      additionalLoginIds (optional List[str]): An optional list of additional login IDs to associate with the user
//      status (optional UserStatus): An optional status for the user. Can be one of "enabled", "disabled", "invited", or "expired". If not provided, defaults to "enabled".
//      createdTime (optional number): Unix timestamp in seconds to set as the user's creation date, for preserving signup dates when migrating users. Must be non-negative and not in the future.
//      password (optional str): Set a cleartext password for the new user. Batches containing a cleartext password are capped at 100 users.
//      hashedPassword (optional object): Import an existing password hash instead of a cleartext password.
const users = [
  {
    loginIdOrUserId: 'email2@company.com',
    email: 'email2@company.com',
    phone: '+15555555555',
    displayName: 'Joe Person',
    userTenants: [{ tenantId: 'TestTenant', roleNames: ['TestRole'] }],
    customAttributes: {"attribute1": "Value 1", "attribute2": "Value 2"},
    picture: "https://xxxx.co/img",
    verifiedEmail: true,
    verifiedPhone: true,
    test: false,
    additionalLoginIds: ["MyUserName", "+12223334455"],
    status: "enabled",
    createdTime: 1609459200
  },
  {
    loginIdOrUserId: 'email@company.com',
    email: 'email@company.com',
    phone: '+15556667777',
    displayName: 'Desmond Copeland',
    userTenants: [{ tenantId: 'TestTenant', roleNames: ['TestRole'] }],
    customAttributes: {"attribute1": "Value 1", "attribute2": "Value 2"},
    picture: "https://xxxx.co/img",
    verifiedEmail: true,
    verifiedPhone: true,
    test: false,
    additionalLoginIds: ["MyUserName", "+12223334455"],
    status: "invited"
  },
  {
    // Optionally set a cleartext password for the new user (subject to the 100-user batch cap noted above).
    loginId: 'email3@company.com',
    email: 'email3@company.com',
    password: 'cleartext-password'
  },
  {
    // Or import an existing password hash instead of a cleartext password.
    loginId: 'email4@company.com',
    email: 'email4@company.com',
    hashedPassword: { bcrypt: { hash: "$2a$..." } }
  }
]
// ---------------------------------------------------------------------------
// Option 1: create the users WITHOUT sending an invitation.
// The users are provisioned directly and no email or SMS is sent to them.
// ---------------------------------------------------------------------------
const createResp = await descopeClient.management.user.createBatch(users);
if (!createResp.ok) {
  console.log("Failed to batch create users.")
  console.log("Status Code: " + createResp.code)
  console.log("Error Code: " + createResp.error.errorCode)
  console.log("Error Description: " + createResp.error.errorDescription)
  console.log("Error Message: " + createResp.error.errorMessage)
}
else {
  // A batch can partially succeed, so always inspect failedUsers as well.
  console.log("Successfully created users: ", createResp.data.createdUsers)
  console.log("Users that could not be created: ", createResp.data.failedUsers)
}

// ---------------------------------------------------------------------------
// Option 2: create the users AND send each one an invitation.
// For inviteBatch, loginIdOrUserId may be a user ID to re-invite an existing user.
// ---------------------------------------------------------------------------
//    inviteUrl // URL to include in user invitation for the user to sign in with
const inviteUrl = "https://company.com/sign-in"
//    sendMail (bool): true or false for sending invite via email
const sendMail = true
//    sendSMS (bool): true or false for sending invite via SMS
const sendSMS = false
// templateOptions (dict): Optional dynamic data injected into the template (keys must be lowercase)
const templateOptions = { k1: 'v1', k2: 'v2' }
// templateId (str): Optional template Id for the invitation message
const templateId = "my-template-id"
// locale (str): Optional locale applied to every invitation in the batch. Only takes effect when the selected template has translations configured; otherwise Descope uses the template's source language.
const locale = "es"

const inviteResp = await descopeClient.management.user.inviteBatch(
  users,
  inviteUrl,
  sendMail,
  sendSMS,
  templateOptions,
  templateId,
  locale
);
if (!inviteResp.ok) {
  console.log("Failed to batch invite users.")
  console.log("Status Code: " + inviteResp.code)
  console.log("Error Code: " + inviteResp.error.errorCode)
  console.log("Error Description: " + inviteResp.error.errorDescription)
  console.log("Error Message: " + inviteResp.error.errorMessage)
}
else {
  // A batch can partially succeed, so always inspect failedUsers as well.
  console.log("Successfully invited users: ", inviteResp.data.createdUsers)
  console.log("Users that could not be invited: ", inviteResp.data.failedUsers)
}

Invite User

This operation creates a new user (when you pass a login ID) and sends them an invitation. In the Node.js and Go SDKs, you can also re-invite an existing user by passing their user ID instead.

Note

For the .NET SDK, user invitation is handled in the same function call as user creation. To invite users, set Invite = true on the CreateUserRequest or CreateUsersRequest.

Note

In the Node.js and Go SDKs, the identifier also accepts a User ID. A login ID creates the user if they do not exist. A user ID requires an existing user — no new user is created, and the invite is resent.

Note

When inviting users from the SDK, the default connector and template configured within Project Settings will be used, unless a different template Id is specified. Currently, when using the Java SDK, only the default connector and template can be used.

//    loginIdOrUserId (str): user login ID or user ID.
const loginIdOrUserId = "custom-login-id";
//    displayName (str): Optional user display name.
const displayName = "Joe Person";
//    phone (str): Optional user phone number.
const phone = "+15555555555";
//    email (str): Optional user email address.
const email = "email@company.com";
//    userTenants (List[UserTenants]): An optional list of the user's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
const userTenants = [{ tenantId: "TestTenant", roleNames: ["TestRole"] }];
//    roles (List[str]): An optional list of the user's role names without tenant association. These roles are mutually exclusive with the `user_tenant` roles, which take precedence over them.
const roles = ["Tenant Admin"];
//   customAttributes (dict): Optional, set the different custom attributes values of the keys that were previously configured in Descope console app
const customAttributes = { attribute1: "Value 1", attribute2: "Value 2" };
//   picture (str): Optional url for user picture
const picture = "https://example.com/picture.jpg";
//   verifiedEmail (bool): Set to true for the user to be able to login with the email address.
const verifiedEmail = true; // or false
//   verifiedPhone (bool): Set to true for the user to be able to login with the phone number.
const verifiedPhone = true; // or false
//   additionalLoginIds (optional List[str]): An optional list of additional login IDs to associate with the user
const additionalLoginIds = ["MyUserName", "+12223334455"];
//   inviteUrl (str): URL to include in user invitation for the user to sign in with
const inviteUrl = "https://company.com/sign-in"
//    sendMail (bool): true or false for sending invite via email
const sendMail = true
//    sendSMS (bool): true or false for sending invite via SMS
const sendSMS = false
// templateId (str): Optional template Id for the invitation message
const templateId = "my-template-id"
// locale (str): Optional locale for the invitation message. Only takes effect when the selected template has translations configured; otherwise Descope uses the template's source language.
const locale = "es"

// A user must have a login ID or user ID, other fields are optional. Roles should be set directly if no tenants exist, otherwise set on a per-tenant basis.
const resp = await descopeClient.management.user.invite(
  loginIdOrUserId,
  {
      email,
      phone,
      displayName,
      // picture,
      verifiedEmail,
      verifiedPhone,
      customAttributes,
      additionalLoginIds,
      // userTenants,// either userTenants or roles
      roles,
      inviteUrl,
      sendSMS,
      sendMail,
      templateId,
      locale,
  }
);
if (!resp.ok) {
  console.log("Failed to invite user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully invited user.")
  console.log(resp.data)
}

Update User

This operation updates an existing user with the details provided. It is important to note that all parameters are used as overrides to the existing user; empty fields will override populated fields. If you wish to only update a subset of fields, reference the Patch User operation instead.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

//    loginIdOrUserId (str): user login ID or user ID.
const loginIdOrUserId = "custom-login-id";
//    displayName (str): Optional user display name.
const displayName = "Joe Person";
//    phone (str): Optional user phone number.
const phone = "+15555555555";
//    email (str): Optional user email address.
const email = "email@company.com";
//    userTenants (List[UserTenants]): An optional list of the user's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
const userTenants = [{ tenantId: "TestTenant", roleNames: ["TestRole"] }];
//    roles (List[str]): An optional list of the user's role names without tenant association. These roles are mutually exclusive with the `user_tenant` roles, which take precedence over them.
const roles = ["Tenant Admin"];
//   customAttributes (dict): Optional, set the different custom attributes values of the keys that were previously configured in Descope console app
const customAttributes = { attribute1: "Value 1", attribute2: "Value 2" };
//   picture (str): Optional url for user picture
const picture = "https://example.com/picture.jpg";
//   verifiedEmail (bool): Set to true for the user to be able to login with the email address.
const verifiedEmail = true; // or false
//   verifiedPhone (bool): Set to true for the user to be able to login with the phone number.
const verifiedPhone = true; // or false
//   additionalLoginIds (optional List[str]): An optional list of additional login IDs to associate with the user
const additionalLoginIds = ["MyUpdatedUserName", "+9999998765"];

const resp = await descopeClient.management.user.update(
  loginIdOrUserId,
  {
      email,
      phone,
      displayName,
      // picture,
      verifiedEmail,
      verifiedPhone,
      customAttributes,
      additionalLoginIds,
      // userTenants,// either userTenants or roles
      roles,
  }
);
if (!resp.ok) {
  console.log("Failed to update user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user.")
  console.log(resp.data)
}

Patch User

This operation updates only the fields provided for an existing user, leaving all other fields unchanged. Unlike Update User, omitted fields will not override existing values.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

//    loginIdOrUserId (str): user login ID or user ID.
const loginIdOrUserId = "desmond@descope.com";
//    options (PatchUserOptions): Only the fields you provide will be updated.
const options = {
  displayName: "Desmond Copeland Jr.",
  // email: "new@company.com",
  // phone: "+15555555555",
  // verifiedEmail: true,
  // verifiedPhone: true,
  // customAttributes: { attribute1: "Value 1" },
  // picture: "https://example.com/picture.jpg",
  // roles: ["Tenant Admin"],
  // userTenants: [{ tenantId: "TestTenant", roleNames: ["TestRole"] }],
};

const resp = await descopeClient.management.user.patch(loginIdOrUserId, options);
if (!resp.ok) {
  console.log("Failed to patch user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully patched user.")
  console.log(resp.data)
}

Load Existing User Details

This operation loads the details of an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

Note

Suppose you frequently load a user for a specific user detail, such as their email address or a particular custom attribute. In that case, you can save execution time and additional API/SDK calls to load the user by adding the items to the custom claim. For details on adding items to the custom claims, see this documentation.

// Args:
//    loginIdOrUserId (str): The login ID or user ID of the user to be loaded.
const loginIdOrUserId = "xxxx"

let resp = await descopeClient.management.user.load(loginIdOrUserId)
if (!resp.ok) {
  console.log("Failed to load user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully loaded user.")
  console.log(resp.data)
}

The older loadByUserId(userId) method is still supported for backward compatibility.

Note

Each entry in the userTenants field of the response includes the user's tenant-level roles and permissions, allowing you to retrieve a user's authorization information without making additional role lookup calls.

Get User's Login History

Retrieve users' authentication history, by the given user's ids.

You get one entry per login, with these fields:

FieldTypeDescription
userIdstringThe ID of the user who logged in.
loginTimeintWhen the login happened, as a Unix timestamp in seconds.
citystringThe city the login came from.
countrystringThe country the login came from.
ipstringThe IP address the login came from.
selectedTenantstringThe ID of the tenant the user signed in to.

Note

selectedTenant holds the tenant that became the user's active tenant for that login, matching the dct claim in the session JWT. Descope sets it when a tenant arrives in the flow's start options, when the user picks one through the Tenant Select component, or when the user belongs to a single tenant and your JWT template has Set active tenant claim automatically turned on. Every other login returns an empty string, including logins recorded before the field existed.

Note

The selectedTenant field is available through the Descope API and the Python and Go SDKs.

// Args:
//    userIds (list[str]): user IDs to load history for.
const userIds = ["xxxx", "yyyy"]


const resp = await descopeClient.management.user.history(userIds);
if (!resp.ok) {
  console.log("Failed to load users history.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully loaded users history.")
  console.log(resp.data)
}

Load Existing User's Provider Token

This operation loads the user's access token generated by the OAuth/OIDC provider, using a valid management key. When querying for OAuth providers, this only applies when utilizing your own account with the provider and have selected Manage tokens from provider selected under the social auth methods.

// Args:
//    loginId (str): The login_id of the user to be loaded.
const loginId = "xxxx"
//    provider (str): The provider name (google, facebook, etc')
const provider = "google"

const resp = await descopeClient.management.user.getProviderToken(loginId, provider)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to load user's provider token.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully loaded user's provider token.")
  console.log(resp.data)
}

Search Users

This operation returns user details based on the applicable search.

Note

Pass null for a key in customAttributes to match users where that attribute is unset — the same way null clears an attribute on Update a User's Custom Attributes.

// Args:
//  tenantIds (List[str]): Optional list of tenant IDs to filter by
const tenantIds = ["Test1", "Test2", "Test3"]
//  roleNames (List[str]): Optional list of role names to filter by
const roleNames = ["TestRole1", "TestRole2", "TestRole3"]
//  limit (number): Optional limit of the number of users returned. Leave empty for default.
const limit = 1
//   page (number): Optional pagination control. Pages start at 0 and must be non-negative.
const page = 0
//    testUsersOnly: boolean: Given true, it will only return test users.
const testUsersOnly = false
//    withTestUser: boolean: Given true, it will also return test users. False will omit test users.
const withTestUser = true
//    customAttributes: Record<string, AttributesTypes>: Searches users with certain custom attributes.
//    Pass null for a key to match users where that attribute is unset.
const customAttributes = {"mycustomattribute": "Test", "unsetattribute": null}
//    statuses (List[str]): a list of statuses to search users for, the options are: "invited", "enabled", "disabled"
const statuses = ["invited", "enabled", "disabled"]
//    emails (List[str]): Optional list of emails to search for
const emails = ["email@company.com"]
//    phones (List[str]): Optional list of phones to search for
const phones = ["+12223334444"]
//    userIds (List[str]): Optional list of user IDs to search for
const userIds = ["user-id-1"]
//    sort (List[str]): Optional list of fields to sort by.
const sort = [{ field: "displayName", desc: true }]
//    text (str): Optional full text search across relevant columns.
const text = ""
//    fromCreatedTime (number): Optional search parameter returning users who were created on or after this time (in Unix epoch milliseconds).
const fromCreatedTime = 1735689600000
//    toCreatedTime (number): Optional search parameter returning users who were created on or before this time (in Unix epoch milliseconds).
const toCreatedTime = 1738368000000
//    fromModifiedTime (number): Optional search parameter returning users whose last modification/update occurred on or after this time (in Unix epoch milliseconds).
const fromModifiedTime = 1735689600000
//    toModifiedTime (number): Optional search parameter returning users whose last modification/update occurred on or before this time (in Unix epoch milliseconds).
const toModifiedTime = 1738368000000
//    tenantRoleIds (Record<string, { values: string[], and?: boolean }>): Optional map of tenant IDs to role IDs to filter by. Set and: true to require all listed roles instead of any.
const tenantRoleIds = { Test1: { values: ['role-id-1', 'role-id-2'], and: true } }
//    tenantRoleNames (Record<string, { values: string[], and?: boolean }>): Optional map of tenant IDs to role names to filter by. Set and: true to require all listed roles instead of any.
const tenantRoleNames = { Test2: { values: ['TestRole1', 'TestRole2'], and: false } }

// Search all users with no filter: let resp = await descopeClient.management.user.search({})
// Search users with limit filter:   let resp = await descopeClient.management.user.search({ limit: 10 })
// Search users with tenant filter:   let resp = await descopeClient.management.user.search({ tenantIds: ['Test1', 'Test2'] })
// Search users with role filter:   let resp = await descopeClient.management.user.search({ roleNames: ['TestRole1', 'TestRole2'] })
// Search users with a combination of filters:
let resp = await descopeClient.management.user.search({ tenantIds: ['Test1', 'Test2'], roleNames: ['TestRole1', 'TestRole2'], fromCreatedTime, toCreatedTime, fromModifiedTime, toModifiedTime, tenantRoleIds, tenantRoleNames });
if (!resp.ok) {
  console.log("Failed to search users.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully searched users.")
  console.log(resp.data)
}

Note

Each entry in the userTenants field of the response includes the user's tenant-level roles and permissions, allowing you to retrieve a user's authorization information without making additional role lookup calls.

Update a User's Email Address

This operation allows administrators to update a user's email address.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update the email for.
const loginIdOrUserId = "xxxx"
//   email (str): The new email address for the user. Leave empty to remove.
const email = "xxxx@xxxxxx.xxx"
//   verified (bool): Set to true for the user to be able to login with the email address.
const verified = true // or false
//   failOnConflict (bool, optional): false (default) merges with a conflicting user;
//   true fails the request before any merge happens.
const failOnConflict = false // or true

let resp = await descopeClient.management.user.updateEmail(loginIdOrUserId, email, verified, failOnConflict)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to update user's email address.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user's email address.")
  console.log(resp.data)
}

Handling login ID conflicts on update

When updating a user's email or phone number to a value already assigned to a different user, the failOnConflict parameter controls whether the request fails or merges the two accounts. By default (failOnConflict omitted or false), the conflicting user is deleted and their data is folded into the user being updated; setting it to true fails the request instead, before any merge happens. Note that failOnConflict: true currently returns a 500, while a default-path conflict during a concurrent update returns a retryable 409 (E013017) — the two failure modes aren't currently distinguishable by status code alone in the same way.

See the Update User Email and Update User Phone API references for the full request schema.

Known limitation: when relying on the default merge behavior, the phone number can be dropped during the merge in some cases (etc#4442, still open). Setting failOnConflict: true avoids this by preventing the merge entirely.

Note: failOnConflict governs collisions with a different existing user's login ID. This is separate from AddToLoginIDs/OnMergeUseExisting, which intentionally links a second login ID to the same user.

Update a User's Login ID

This operation allows administrators to update a user's Login ID. If you'd like to remove a login ID, provide an empty string for the new login ID.

// Args:
//   login_id (str): The login ID of the user to update the Login ID for.
const loginId = "xxxx"
//   new_login_id (str): New login ID to set for the user.
const newLoginId = "xxxx"

const resp = await descopeClient.management.user.updateLoginId(loginId, newLoginId)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to update user's Login ID.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user's Login ID.")
  console.log(resp.data)
}

Update a User's Phone Number

This operation allows administrators to update a user's phone number.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update the phone number for.
const loginIdOrUserId = "xxxx"
//   phone (str): The new user phone number. Leave empty to remove.
const phone = "+17777777777"
//   verified (bool): Set to true for the user to be able to login with the phone number.
const verified = true // or false
//   failOnConflict (bool, optional): false (default) merges with a conflicting user;
//   true fails the request before any merge happens.
const failOnConflict = false // or true

let resp = await descopeClient.management.user.updatePhone(loginIdOrUserId, phone, verified, failOnConflict)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to update user's phone number.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user's phone number.")
  console.log(resp.data)
}

Handling login ID conflicts on phone update

When updating a user's email or phone number to a value already assigned to a different user, the failOnConflict parameter controls whether the request fails or merges the two accounts. By default (failOnConflict omitted or false), the conflicting user is deleted and their data is folded into the user being updated; setting it to true fails the request instead, before any merge happens. Note that failOnConflict: true currently returns a 500, while a default-path conflict during a concurrent update returns a retryable 409 (E013017) — the two failure modes aren't currently distinguishable by status code alone in the same way.

See the Update User Email and Update User Phone API references for the full request schema.

Known limitation: when relying on the default merge behavior, the phone number can be dropped during the merge in some cases (etc#4442, still open). Setting failOnConflict: true avoids this by preventing the merge entirely.

Note: failOnConflict governs collisions with a different existing user's login ID. This is separate from AddToLoginIDs/OnMergeUseExisting, which intentionally links a second login ID to the same user.

Update a User's Display Name

This operation allows administrators to update a user's display name.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   displayName (str): Optional user display name. Leave empty to remove.
const displayName = "Updated Display Name"

let resp = await descopeClient.management.user.updateDisplayName(loginIdOrUserId, displayName)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to update user's display name.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user's display name.")
  console.log(resp.data)
}

Update a User's Picture

This operation allows administrators to update a user's profile picture granularly without updating all user details.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   picture (str): Optional url to user avatar. Leave empty to remove.
const picture = "https://example.com/picture.png"

const resp = await descopeClient.management.user.updatePicture(loginIdOrUserId, picture)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to update user's picture.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user's picture.")
  console.log(resp.data)
}

Update a User's Custom Attributes

This operation allows administrators to update a user's custom attributes granularly without updating all user details.

Note

Pass null as the attribute value to clear the attribute on the user. This works for all custom attribute types (string, number, boolean, and so on). You no longer need type-specific workarounds such as "" or 0.

The same null behavior applies when you set a key to null inside the customAttributes map on Update User.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   attributeKey: The custom attribute that needs to be updated, this attribute needs to exists in Descope console app
const attributeKey = "mycustomattribute"
//   attributeValue: The value to set, or null to clear the attribute
const attributeValue = "Test Value"
// const attributeValue = null  // clears the attribute

const resp = await descopeClient.management.user.updateCustomAttribute(loginIdOrUserId, attributeKey, attributeValue)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to update user's custom attribute.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully updated user's custom attribute.")
  console.log(resp.data)
}

Expire a User's Password

This operation allows administrators to expire an existing user's password. Upon next login, the user will need to follow the reset password flow.

// Args:
//   loginId (str): The login ID of the user to expire password for.
const loginId = "xxxx"

const resp = await descopeClient.management.user.expirePassword(loginId)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to expire user's password.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully expired user's password.")
}

Set a Temporary User's Password

This operation allows administrators to set a temporary password for an existing user. This will require the user to change their password on next authentication.

// Args:
//   loginId (str): The login ID of the user set password for.
const loginId = "xxxx"
//   password (str): The password to be set for the user.
const password = "xxxxx"

const resp = await descopeClient.management.user.setTemporaryPassword(loginId, password)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to set user's password.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully set user's password.")
}

Set an Active Password for User

This endpoint allows you to set an active password for an existing user. This will allow the user to authenticate with this password without changing it.

// Args:
//   loginId (str): The login ID of the user set password for.
const loginId = "xxxx"
//   password (str): The password to be set for the user.
const password = "xxxxx"

const resp = await descopeClient.management.user.setActivePassword(loginId, password)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to set user's password.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully set user's password.")
}

Add a Role to a User

This operation allows administrators to add roles to an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   roleNames (List[str]): A list of roles to add to a user without tenant association.
const roleNames = ["TestRole1","TestRole2"]

let resp = await descopeClient.management.user.addRoles(loginIdOrUserId, roleNames)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to add roles to user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully added roles to user.")
  console.log(resp.data)
}

Set Roles for a User

This endpoint allows you to set a user's roles. This will override the current roles associated to the user and will set all passed roles.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   roles (List[str]): A list of roles to set for a user without tenant association.
const roles = ["TestRole1","TestRole2"]

let resp = await descopeClient.management.user.setRoles(loginIdOrUserId, roles)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to set roles to user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully set roles to user.")
  console.log(resp.data)
}

Remove a Role from a User

This operation allows administrators to remove roles from an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   roleNames (List[str]): A list of roles to remove from a user without tenant association.
const roleNames = ["TestRole1","TestRole2"]

let resp = await descopeClient.management.user.removeRoles(loginIdOrUserId, roleNames)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to remove roles from user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully removed roles from user.")
  console.log(resp.data)
}

Add a Tenant to a User

This operation allows administrators to add tenants to an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   tenantId (str): The ID of the tenant to add to the user.
const tenantId = "TestTenant"

let resp = await descopeClient.management.user.addTenant(loginIdOrUserId, tenantId)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to add tenant to user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully added tenant to user.")
  console.log(resp.data)
}

Remove a Tenant from a User

This operation allows administrators to remove tenants from an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   tenantId (str): The ID of the tenant to remove from the user.
const tenantId = "TestTenant"

let resp = await descopeClient.management.user.removeTenant(loginIdOrUserId, tenantId)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to remove tenant from user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully removed tenant from user.")
  console.log(resp.data)
}

Add Roles to a User in a Specific Tenant

This operation allows administrators to add roles to a user within a specific tenant.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   tenantId (str): The ID of the user's tenant.
const tenantId = "TestTenant"
//   roleNames (List[str]): A list of roles to add to the user.
const roleNames = ["TestRole1","TestRole2"]

let resp = await descopeClient.management.user.addTenantRoles(loginIdOrUserId, tenantId, roleNames)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to add roles to the user in the specified tenant.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully added roles to the user in the specified tenant.")
  console.log(resp.data)
}

Set Roles for a User in a Specific Tenant

This operation allows administrators to set roles to a user within a specific tenant. This will override the current roles associated to the user for the tenant and will set all passed roles.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   tenantId (str): The ID of the user's tenant.
const tenantId = "TestTenant"
//   roles (List[str]): A list of roles to set for the user.
const roles = ["TestRole1","TestRole2"]

let resp = await descopeClient.management.user.setTenantRoles(loginIdOrUserId, tenantId, roles)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to set roles to the user in the specified tenant.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully set roles to the user in the specified tenant.")
  console.log(resp.data)
}

Remove Roles from a User in a Specific Tenant

This operation allows administrators to remove roles from a user within a specific tenant.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx"
//   tenantId (str): The ID of the user's tenant.
const tenantId = "TestTenant"
//   roleNames (List[str]): A list of roles to remove from the user.
const roleNames = ["TestRole1","TestRole2"]

let resp = await descopeClient.management.user.removeTenantRoles(loginIdOrUserId, tenantId, roleNames)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to remove roles from the user in the specified tenant.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully removed roles from the user in the specified tenant.")
  console.log(resp.data)
}

Associate an Application to a User

This operation allows administrators to associate an Application with a user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

//   loginIdOrUserId (str): The login ID or user ID of the user to update.
const loginIdOrUserId = "xxxx";
//   ssoAppIds (array(str)): The IDs of the sso apps to add to the user.
const ssoAppIds = ["app1", "app2"];

let resp = await descopeClient.management.user.addSSOapps(loginIdOrUserId, ssoAppIds)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to add sso apps to user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully added sso apps to user.")
  console.log(resp.data)
}

Set Applications for user

This operation allows administrators to set Applications associated to a user. This will override the current Application associated to the user for the user and set all passed Applications.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

//   loginIdOrUserId (str): The login ID or user ID of the user to update.
loginIdOrUserId = "xxxx"
//   ssoAppIds (array(str)): The IDs of the sso apps to add to the user.
ssoAppIds = ["app1", "app2"]

let resp = await descopeClient.management.user.setSSOapps(loginIdOrUserId, ssoAppIds)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to set sso apps to user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully set sso apps to user.")
  console.log(resp.data)
}

Remove an Application from a User

This operation allows administrators to remove an Application from being associated with a user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

//   loginIdOrUserId (str): The login ID or user ID of the user to update.
loginIdOrUserId = "xxxx"
//   ssoAppIds (array(str)): The IDs of the sso apps to add to the user.
ssoAppIds = ["app1", "app2"]

let resp = await descopeClient.management.user.removeSSOapps(loginIdOrUserId, ssoAppIds)
if (!resp.ok) {
  console.log(resp)
  console.log("Unable to remove sso apps to user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully removed sso apps to user.")
  console.log(resp.data)
}

Activate User

This operation allows administrators to activate an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//  loginIdOrUserId (str): The login ID or user ID of the user to be activated.
const loginIdOrUserId = "xxxx"

let resp = await descopeClient.management.user.activate(loginIdOrUserId)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to activate user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully activated user.")
  console.log(resp.data)
}

Deactivate User

This operation allows administrators to deactivate an existing user.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//  loginIdOrUserId (str): The login ID or user ID of the user to be deactivated.
const loginIdOrUserId = "xxxx"

let resp = await descopeClient.management.user.deactivate(loginIdOrUserId)
if (!resp.ok) {
  console.log(resp)
  console.log("Failed to deactivate user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully deactivated user.")
  console.log(resp.data)
}

Logout All User Sessions

This operation allows administrators to log an existing user out of all sessions. This operation can be done via loginId or userId.

// Args:
//    loginId (str): The loginId of the user to be logged out.
const loginId = "email@company.com"

const resp = await descopeClient.management.user.logoutUser(loginId);
if (!resp.ok) {
  console.log("Failed to logout user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully logged user out.")
}

// Args:
//    userId (str): The userId of the user to be logged out.
const userId = "email@company.com"

const resp = await descopeClient.management.user.logoutUserByUserId(userId);
if (!resp.ok) {
  console.log("Failed to logout user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully logged user out.")
}

Delete User's Passkeys

This operation will delete all existing passkeys for a user.

// Args:
//    loginId (str): The loginId of the user to be remove passkeys for.
const loginId = "email@company.com"

const resp = await descopeClient.management.user.removeAllPasskeys(loginId);
if (!resp.ok) {
  console.log("Failed to remove user's passkeys.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully removed user's passkeys.")
}

Delete User

This operation allows administrators to delete an existing user. It is important to note that this operation is irreversible and the user will be removed and will not be able to be added back without recreation.

Note

The Login ID parameter also accepts a User ID. Descope detects which one you passed and resolves the user.

// Args:
//    loginIdOrUserId (str): The login ID or user ID of the user to be deleted.
const loginIdOrUserId = "email@company.com"

const resp = await descopeClient.management.user.delete(loginIdOrUserId);
if (!resp.ok) {
  console.log("Failed to delete user.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully deleted user.")
  console.log(resp.data)
}

Batch Delete Users

This operation deletes several existing users in a single request. As with Delete User, this operation is irreversible: the deleted users are removed from the project and cannot be added back without recreation.

// Args:
//    userIds (string[]): The Descope user IDs of the users to be deleted.
const userIds = ["<user-ID-1>", "<user-ID-2>"]

const resp = await descopeClient.management.user.deleteBatch(userIds);
if (!resp.ok) {
  console.log("Failed to batch delete users.")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully batch deleted users.")
}
Was this helpful?

On this page