# Refresh user keys

If there is a suspicion that private keys were compromised, lock the existing user and create a replacement user with new keys. You cannot replace a user's `publicKey` with `v0_UpdateUser`.

## Prerequisites

Before you refresh the user key, decide what kind of replacement user you need:

- **UI human user**: The user creates a new profile with the Ripple Custody authenticator app and shares the new public key. The replacement user should receive the original human-facing alias and `loginIds`.
- **API or bot user**: Generate a new API key pair for the client or bot, then use the new public key when you create the replacement user. Store the new private key in your secrets manager.


Retrieve the existing user's `id`, `metadata.revision`, `alias`, `roles`, and `loginIds`. Also prepare:

- A new UUID for the replacement user.
- A retired alias for the old user, such as `operator-retired-20260618@example.com`.
- Retired `loginIds` for the old user. Do not set `loginIds` to `null`; when present, `loginIds` must be an array of valid login ID objects.


For more information, see:

- [Ripple Custody API > Generate a key pair and share the public key](/products/custody/identity-and-access/authentication/generate-api-keys-and-register)
- [Ripple Custody UI > Create a profile](/products/custody/identity-and-access/authentication/ui-authentication#register-and-log-in)


## Refresh a user key

These steps describe how to refresh a user key with the API.

To refresh a user key:

1. Get the current user details.
For more information, see [Manage users and roles](/products/custody/governance/users/manage-users-and-roles#view-users-and-roles-with-the-api).
2. Create a `v0_UpdateUser` intent for the existing user that changes the old user's `alias` and `loginIds` to retired values. This releases the original alias and login IDs for the replacement user.

```json
{
  "payload": {
    "reference": {
      "id": "61aaa805-f9b8-11eb-b158-dcfb48cfb3cb",
      "revision": 2
    },
    "alias": "operator-retired-20260618@example.com",
    "roles": ["transaction-operator"],
    "loginIds": [
      {
        "id": "operator-retired-20260618@example.com",
        "providerId": "harmonize"
      }
    ],
    "description": "Retired user record after key refresh",
    "customProperties": {
      "keyRefreshStatus": "retired"
    },
    "type": "v0_UpdateUser"
  }
}
```
If the user has multiple login providers, provide a retired `loginIds` entry for each provider. Do not use `null` for `loginIds`.
3. Create a `v0_LockUser` intent to lock the existing user.

```json
{
  "payload": {
    "reference": {
      "id": "61aaa805-f9b8-11eb-b158-dcfb48cfb3cb",
      "revision": 3
    },
    "type": "v0_LockUser"
  }
}
```
4. Create a replacement user with a new user `id`, the original alias and `loginIds`, the required roles, and the newly generated public key.

```json
{
  "payload": {
    "id": "4d8c6240-3261-4991-9aa9-59e66f089d76",
    "alias": "operator@example.com",
    "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
    "roles": ["transaction-operator"],
    "loginIds": [
      {
        "id": "operator@example.com",
        "providerId": "harmonize"
      }
    ],
    "lock": "Unlocked",
    "description": "Replacement user after key refresh",
    "customProperties": {
      "replacesUserId": "61aaa805-f9b8-11eb-b158-dcfb48cfb3cb"
    },
    "type": "v0_CreateUser"
  }
}
```
For a UI human user, use the public key from the user's new authenticator profile. For an API or bot user, use the public key from the newly generated API key pair.
5. After the intents execute, confirm that the old user is locked and that the replacement user can authenticate and perform the expected read-only operation before allowing state mutation work.


For more information about submitting update, lock, and create-user intents, see [Manage intents and approvals](/products/custody/governance/intents/manage-intents-and-approvals#submit-an-intent-with-the-api) and [Manage users and roles](/products/custody/governance/users/manage-users-and-roles#create-a-user-with-the-api).