> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/guides/identity/21.-recover-access-to-an-onchainid.md).

# 21. Recover access to an ONCHAINID

Recovery restores control of an identity whose `MANAGEMENT` keys are lost. The guardians the owner chose in advance sign an operation that gives the identity a new `MANAGEMENT` key. The operation is scheduled, waits for a delay during which it can be cancelled, then executes. The Identity Owner triggers the flow by asking its guardians, and it only works if the recovery module was installed while the owner still had its keys.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                                                                         |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Identity Owner, through its guardians                                                                                                                                                                                                                                                                                                                                                                   |
| **Actors**        | Identity Owner, Guardian, Platform                                                                                                                                                                                                                                                                                                                                                                      |
| **Contracts**     | `RecoveryModule`, ONCHAINID, `ERC734Validator`                                                                                                                                                                                                                                                                                                                                                          |
| **Preconditions** | The identity exists (see [02](/guides/identity/02.-deploy-an-onchainid.md)); the `RecoveryModule` is installed on the identity with its guardians, weights, threshold, delay and expiration, and holds the `MANAGEMENT` purpose (see [28](/guides/identity/28.-install-the-recovery-module-and-manage-guardians.md)); enough guardians to reach the threshold are available; the owner has a new signer |
| **Result**        | The identity has a new `MANAGEMENT` key, controlled by the owner                                                                                                                                                                                                                                                                                                                                        |

## Actors and roles

| Actor          | Role in this flow                                                                                                          | What they hold                                                  |
| -------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| Identity Owner | Has lost its `MANAGEMENT` keys. Provides the new signer and asks the guardians.                                            | A new signer, not yet a key of the identity                     |
| Guardian       | Verifies the owner's request off-chain and signs the recovery.                                                             | A signer listed as guardian in the identity's recovery settings |
| Platform       | Builds the operation, collects the signatures, submits the transactions and monitors the delay. Anyone can play this part. | No role and no key of the identity                              |

## Sequence

```mermaid
sequenceDiagram
    participant Owner as Identity Owner
    participant Platform
    participant Guardian as Guardians
    participant Recovery as RecoveryModule
    participant Identity as ONCHAINID

    Owner->>Platform: new signer
    Platform->>Recovery: threshold, delay, guardians of the identity (read)
    Platform->>Guardian: operation to sign
    Guardian-->>Platform: signatures over the recovery operation
    Platform->>Recovery: startRecovery(identity, salt, mode, recoveryData, multisignature)
    Recovery->>Recovery: signers are guardians, weights reach the threshold
    Note over Recovery,Identity: delay, during which the recovery can be cancelled
    Platform->>Recovery: executeRecovery(identity, salt, mode, recoveryData)
    Recovery->>Recovery: delay elapsed, not expired
    Recovery->>Identity: execute the operation
    Identity->>Identity: addKeyWithData: new MANAGEMENT key
```

## Step by step

### Step 1. Build the recovery operation

**Who:** Platform, on behalf of the Identity Owner

**Call:** Off-chain, using read calls

**Before the call:** The owner has generated a new signer and has given its signer data to the Platform.

**What happens:** The Platform reads the identity's recovery settings from the `RecoveryModule`: its guardians with `getGuardians(identity, start, end)` and `getGuardianCount(identity)`, the threshold with `threshold(identity)`, the delay with `getDelay(identity)` and the expiration with `getExpiration(identity)`. It builds the operation the recovery performs, `recoveryData`: a call from the identity to itself, `addKeyWithData(keyHash, MANAGEMENT, keyType, signerData, clientData)` for the new signer. It chooses a `salt` that identifies this recovery, and the execution `mode` for a single call.

**What changes:** Nothing on-chain.

### Step 2. Collect the guardians' signatures

**Who:** Guardians

**Call:** Off-chain

**Before the call:** Each guardian has verified, by its own means, that the request comes from the owner. This verification is the whole security of the recovery.

**What happens:** Each guardian signs the ERC-712 typed message for scheduling a recovery, `ScheduleRecover(address executor, bytes32 salt, bytes32 mode, bytes recoveryCalldata)`, in the signing domain of the identity being recovered. `executor` is the address of the `RecoveryModule`. The Platform gathers signatures until the weights of the signers reach the threshold, and assembles the signers and their signatures in one bundle.

**What changes:** Nothing on-chain.

### Step 3. Schedule the recovery

**Who:** Platform, or anyone holding the bundle

**Call:** `RecoveryModule.startRecovery(identity, salt, mode, recoveryData, multisignature)`, with `multisignature` the bundle of signers and signatures

**Before the call:** Step 2 is done.

**What the contract checks:**

1. The bundle decodes, each signer is a guardian of this identity, and each signature is valid for the scheduling message in this identity's domain. Otherwise the call reverts with `ERC7579SocialRecoveryExecutorInvalidSchedule`.
2. The weights of the signers reach the threshold. Otherwise `ERC7579SocialRecoveryExecutorInvalidSchedule`.
3. The module is installed on the identity. Otherwise `ERC7579ExecutorModuleNotInstalled`.
4. No operation with the same identity, salt, mode and `recoveryData` was ever scheduled. Otherwise `ERC7579ExecutorUnexpectedOperationState`.

**What changes:** The module records the operation as scheduled, executable once the delay has elapsed and until the expiration. It emits `ERC7579ExecutorOperationScheduled(account, operationId, salt, mode, executionCalldata, schedule)`, where `schedule` is the time from which the operation can execute. Nothing changes on the identity.

### Step 4. Wait for the delay

**Who:** Identity Owner and Guardians

**Call:** Off-chain. Optionally `RecoveryModule.cancelRecovery(identity, salt, mode, recoveryData)` by the identity, or `RecoveryModule.cancelRecovery(identity, salt, mode, recoveryData, multisignature)` by anyone holding a bundle of guardian signatures over the cancellation message, `CancelRecover` with the same fields as the scheduling message.

**Before the call:** The recovery is scheduled.

**What happens:** Nothing, if the recovery is legitimate. The delay exists for the opposite case: an owner who still holds a `MANAGEMENT` key and did not ask for a recovery cancels it from the identity. Guardians who reach the threshold can cancel as well. A signature made to schedule a recovery cannot be used to cancel one, and the reverse. A cancellation that comes neither from the identity nor with valid guardian signatures reverts with `ERC7579SocialRecoveryExecutorInvalidCancel`.

**What changes:** The module emits `ERC7579ExecutorOperationCanceled(account, operationId)`. A cancelled recovery can no longer be executed, and the same operation cannot be scheduled again: a new attempt uses another salt.

### Step 5. Execute the recovery

**Who:** Platform, or anyone

**Call:** `RecoveryModule.executeRecovery(identity, salt, mode, recoveryData)`

**Before the call:** The delay has elapsed and the expiration has not passed.

**What the contract checks:**

1. The operation is in the state `Ready`: it was scheduled with these exact parameters, it was not cancelled or already executed, its delay has elapsed and it has not expired. Otherwise the call reverts with `ERC7579ExecutorUnexpectedOperationState(operationId, currentState, allowedStates)`, which names the state the operation is in: `Unknown`, `Scheduled`, `Expired`, `Executed` or `Canceled`.
2. The module holds the purpose required for the target of the operation on the identity. A call to the identity itself requires `MANAGEMENT`. Otherwise the identity reverts with `ExecutorPurposeNotAuthorized`.

**What changes:** The module has the identity execute the operation. The identity adds the new signer with the `MANAGEMENT` purpose, and the registry emits `KeyAdded`. The owner controls the identity again.

### Step 6. Clean up

**Who:** Identity Owner (the new `MANAGEMENT` key)

**Call:** `Identity.removeKey(keyHash, purpose)` for each lost key (see [22](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md))

**Before the call:** Step 5 is confirmed.

**What the contract checks:** As in [22](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md). The last `MANAGEMENT` signer cannot be removed, and the new key now counts as one.

**What changes:** The lost keys no longer hold any purpose. The owner also reviews its guardians and its linked wallets.

## State after the flow

* `Identity.keyHasPurpose(newKeyHash, 1)` is true.
* The identity's address, type, claims, linked wallets and positions are unchanged. Its eligibility for every asset is unchanged.
* The recovery settings are unchanged: same guardians, threshold, delay and expiration.
* The scheduled operation is consumed and cannot be executed again.
* After step 6, the lost keys hold no purpose.

## Failure modes

| Symptom                                                                                            | Cause                                                                                                                                                                            | Fix                                                                                                                                                                             |
| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| There is no recovery module on the identity                                                        | It was never installed. It can only be installed by a `MANAGEMENT` key                                                                                                           | No on-chain recovery is possible. The positions the identity holds can be moved by each asset's agents to a new identity: [14](/guides/agents/14.-execute-a-forced-transfer.md) |
| `startRecovery` reverts with `ERC7579SocialRecoveryExecutorInvalidSchedule`                        | A signer is not a guardian of this identity, a signature was made over other parameters or in another domain, or the weights do not reach the threshold (step 3, checks 1 and 2) | Rebuild the message with the exact salt, mode and `recoveryData`, in the identity's own signing domain. Collect more signatures                                                 |
| `startRecovery` reverts with `ERC7579ExecutorModuleNotInstalled`                                   | The module is not installed on this identity (step 3, check 3)                                                                                                                   | No on-chain recovery is possible for it                                                                                                                                         |
| `startRecovery` reverts with `ERC7579ExecutorUnexpectedOperationState`                             | The same operation was scheduled before, with the same salt (step 3, check 4)                                                                                                    | Use a new salt and collect signatures over it                                                                                                                                   |
| `executeRecovery` reverts with `ERC7579ExecutorUnexpectedOperationState` and the state `Scheduled` | The delay has not elapsed (step 5, check 1)                                                                                                                                      | Wait. Read `getSchedule` for the time                                                                                                                                           |
| The same error with the state `Expired`                                                            | The operation was not executed before its expiration (step 5, check 1)                                                                                                           | Schedule it again with a new salt and new signatures                                                                                                                            |
| The same error with the state `Unknown`                                                            | The salt, the mode or `recoveryData` differ from what was scheduled (step 5, check 1)                                                                                            | Use the exact scheduled parameters                                                                                                                                              |
| The same error with the state `Canceled` or `Executed`                                             | The recovery was cancelled, or has already run (step 5, check 1)                                                                                                                 | See the rows below for a cancellation. Nothing to do when it already ran                                                                                                        |
| `executeRecovery` reverts with `ExecutorPurposeNotAuthorized`                                      | The module is installed and does not hold `MANAGEMENT` on the identity (step 5, check 2)                                                                                         | The purpose must be granted while a `MANAGEMENT` key exists. Without it, the module cannot change the keys                                                                      |
| The recovery was cancelled                                                                         | The owner, or the guardians, cancelled during the delay                                                                                                                          | If the cancellation was hostile, the party that cancelled holds a `MANAGEMENT` key or a guardian quorum. Treat the identity as compromised                                      |
| A recovery the owner did not ask for is scheduled                                                  | Guardians reaching the threshold signed it                                                                                                                                       | Cancel from the identity with a `MANAGEMENT` key before the delay elapses, then replace the guardians                                                                           |

## Common misunderstandings

> **Wrong:** "The Issuer can recover an investor's identity."
>
> **Right:** No Issuer and no agent has any authority over an identity. An identity is recovered by its guardians, chosen by its owner. What an asset's agents can do is move the positions of that asset, which is a different operation on a different contract.

> **Wrong:** "Recovering the identity recovers the tokens."
>
> **Right:** The tokens never left. Positions belong to the identity, at its own address and in its linked wallets. Recovery restores the ability to act through the identity. A wallet whose own key is lost is a separate matter: the identity moves its tokens with `identityTransfer`.

> **Wrong:** "Guardians hold a copy of the key."
>
> **Right:** Guardians hold no key of the identity. Together, at the threshold, they can authorize one specific operation, after a delay. They cannot act alone, at once, or for anything they did not sign.

> **Wrong:** "Recovery can be set up when it is needed."
>
> **Right:** Installing the module takes a `MANAGEMENT` key. Once the keys are lost, it is too late. Guardians are set up when the identity is created.

## Integration notes

* Offer the installation of the recovery module, with its guardians, in the same session as the creation of the identity.
* Show each guardian exactly what it signs: the identity, and the new key that will control it.
* Submitting `startRecovery` and `executeRecovery` requires no key of the identity. A Platform or a relayer can pay for both. Anyone can execute a recovery once its delay has elapsed.
* Guardians can cancel only while the identity has a guardian configuration with a threshold above zero. The identity itself can always cancel.
* Read the state of a recovery with `RecoveryModule.state(identity, salt, mode, recoveryData)` and its times with `getSchedule(identity, salt, mode, recoveryData)`, which returns when it was scheduled, when it can execute and when it expires.
* Monitor scheduled recoveries on every identity your application serves, by indexing `ERC7579ExecutorOperationScheduled` on the module, and notify the owner. The delay protects the owner only if the owner learns of the recovery in time.
* Choose the delay with the owner. A short delay recovers faster. A long delay leaves more time to cancel a recovery that was not requested.
* Guardians are signers of any supported type. Their signatures are bound to the identity being recovered and cannot be replayed on another identity that uses the same module.
* After a recovery, remove the lost keys and consider replacing the guardians: the event that cost the keys may have exposed them too.

## Related flows

* [22 Add and remove keys on an ONCHAINID](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md) : removing the lost keys afterward
* [02 Deploy an ONCHAINID](/guides/identity/02.-deploy-an-onchainid.md) : when to install guardians
* [23 Revoke a wallet](/guides/identity/23.-revoke-a-wallet.md) : a lost wallet is not a lost identity
* [15 Recover a position from a lost wallet](/guides/agents/15.-recover-a-position-from-a-lost-wallet.md) : the agent's operation on an asset
* [14 Execute a forced transfer](/guides/agents/14.-execute-a-forced-transfer.md) : when an identity cannot be recovered
* [28 Install the recovery module and manage guardians](/guides/identity/28.-install-the-recovery-module-and-manage-guardians.md) : the precondition of this flow


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.t-rex.network/guides/identity/21.-recover-access-to-an-onchainid.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
