<!-- tempo-docs-context -->
> Source: [/docs/accounts/apps/recover-an-account](https://tempo.xyz/developers/docs/accounts/apps/recover-an-account.md)
> Section: [Accounts](https://tempo.xyz/developers/docs/accounts.md). Create accounts, set up signing methods, policies, access keys, key rotation, and fees, and follow guides for users, automations, agents, and teams.
> [Documentation index](https://tempo.xyz/developers/llms.txt) · [Docs skill](https://tempo.xyz/developers/SKILL.md)

> Tempo MCP: Use `search`, `find_pages`, `read_page`, and `code` at `https://mcp.tempo.xyz` for Tempo and related documentation.
> If an indexed page is missing or reflects an older site, read this site’s [documentation index](https://tempo.xyz/developers/llms.txt) and Markdown pages directly.

# Recover a User's Account

Replaces a user's lost passkey at the same address, with the two signers they still have.

## Overview

Recovery replaces the passkey a user lost with their phone. Their funds don't move to a new account.

The steps assume the [self-custodial setup](https://tempo.xyz/developers/docs/accounts/apps/self-custodial-wallets). It has three signers at weight 1, and any two approve:

* **Passkey:** The user's passkey, lost with the phone.
* **Provider Key:** A key your wallet provider manages.
* **Backup Key:** A second key the user keeps.

The provider's key and the backup approve a new passkey from the user's new phone. The new passkey and the provider then revoke the lost phone's device key. The balance and [Earn](https://tempo.xyz/developers/docs/earn) positions stay at the same address.

<span id="account-recovery-limits" />

<span id="recommended-recovery-setups" />

<span id="replace-a-lost-signer" />

<span id="key-replacement" />

<span id="replace-a-signer" />

<span id="recover-from-a-lost-passkey" />

:::warning
**Multiple signers arrive with the [T14 network upgrade](https://tempo.xyz/developers/docs/protocol/upgrades/t14).** Until T14 activates, an account with several signers and a threshold runs only on the [Tempo Accounts devnet](https://tempo.xyz/developers/docs/accounts/create#connect-to-the-devnet), not on mainnet or testnet, and may still change.
:::

## Recovery Requirements

Replacing a lost signer needs a configurable account. Tempo can't override an account's signers, so recovery works only if the remaining signers reach the threshold. Run this check for every signer before the user funds the account.

```ts [check.ts]
const total = saved.config.owners.reduce((sum, { weight }) => sum + weight, 0)
const recoverable = saved.config.owners.every(({ weight }) => total - weight >= saved.config.threshold)
```

Tempo stores only a hash of the account's configuration. Your app rebuilds the account from the address and configuration it saved after the last update. The examples call them `saved.address` and `saved.config`.

## Recovery Demo

The demo runs the same recovery in this browser, with stand-in signers:

* **Passkey:** A device key at weight 2, which the demo treats as lost.
* **Provider Key:** A private key.
* **Backup Key:** A second private key.

The replacement device key has weight 2, so it approves the revoke alone.

In the interactive web page, a demo runs the steps that follow on the Tempo Accounts devnet with the reader's own signers: a passkey on their device, or keys the page generates. Each step that changes the account sends a devnet transaction and shows its hash, next to what changed in the app and on the account. It runs only where the page can reach Tempo Accounts devnet.

## Setup

::::steps
### Create the Replacement Signer

The user creates a new passkey on their new phone. It isn't a signer until the next step adds it. Save its `credential.id` and `credential.publicKey` with the user's record, as at signup.

```ts [recover.ts]
import { Account, WebAuthnP256 } from 'viem/tempo'

const credential = await WebAuthnP256.createCredential({ label: 'My account' })
const passkey = Account.fromWebAuthnP256(credential) // [!code hl]
```

<span id="sign-in-with-the-remaining-signers" />

### Approve With the Remaining Signers

Rebuild the account from `saved`, and attach the two keys that approve:

* **`providerAccount`:** The provider's key, wrapped as in [Set up a self-custodial wallet](https://tempo.xyz/developers/docs/accounts/apps/self-custodial-wallets#connect-privy-or-turnkey).
* **`backup`:** The user's backup key.

A signer without an attached key stays an address that can't sign, such as the lost passkey at `lostPasskey`.

`openAccount` passes the saved address. Once signers change, `address: 'infer'` derives a different one.

The update swaps the lost passkey for the new one at the same weight. Viem signs it with both attached keys, and their weights of 1 each reach the threshold of 2.

`client.ts` connects to the Tempo Accounts devnet ([set up the devnet key](https://tempo.xyz/developers/docs/accounts/create#connect-to-the-devnet)).

:::code-group
```ts [recover.ts]
import { isAddressEqual } from 'viem'
import { client } from './client'
import { providerAccount } from './provider-account'

/** The account that `saved` describes, holding the approvers' keys. */
function openAccount(approvers: Account.RootAccount[]) {
  const { address, config } = saved
  const owners = config.owners.map(({ owner, weight }) => ({
    owner: approvers.find((key) => isAddressEqual(key.address, owner)) ?? owner,
    weight,
  }))
  return Account.fromConfig({ ...config, address, owners })
}

// the user's backup passkey, from its saved id and public key
const backup = Account.fromWebAuthnP256(backupCredential)

const { config } = await client.accounts.updateConfigSync({ // [!code hl]
  account: openAccount([providerAccount, backup]), // [!code hl]
  nextConfig: { // [!code hl]
    owners: saved.config.owners.map(({ owner, weight }) => ({ // [!code hl]
      owner: isAddressEqual(owner, lostPasskey) ? passkey.address : owner, // [!code hl]
      weight, // [!code hl]
    })), // [!code hl]
    threshold: saved.config.threshold, // [!code hl]
  }, // [!code hl]
}) // [!code hl]
// persist it: the version increments, and the next step builds from it
saved.config = config // [!code hl]
```

```ts [client.ts]
import { http } from 'viem'
import { tempoDevnet } from 'viem/chains'
import { createClient } from 'viem/tempo'

// The Tempo Accounts devnet. Its RPC needs a key, which Tempo gives you.
const rpcKey = process.env.TEMPO_DEVNET_RPC_KEY

export const client = createClient({
  chain: tempoDevnet,
  transport: http('https://rpc-t14-checkpoint.devnet.tempoxyz.dev', {
    fetchOptions: { headers: { Authorization: `Basic ${btoa(`previews:${rpcKey}`)}` } },
  }),
})
```
:::

As with every payment, build the update in your backend. Check it before the provider signs ([route raw signing through one backend](https://tempo.xyz/developers/docs/accounts/apps#route-raw-signing-through-one-backend)). Once the update lands, the lost passkey can't approve anything.

If the attached keys don't reach the threshold, Viem throws `Local owners do not meet the threshold.` and sends nothing. Approvals can also come from [separate devices](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds#approvers-on-separate-devices), such as the backup on the user's laptop.

### Revoke the Lost Device's Keys

The lost phone's device key is an [access key](https://tempo.xyz/developers/docs/accounts/access-keys), so it still pays within its limits. A configuration update doesn't revoke it. Revoke it now.

The new passkey and the provider approve the revoke together, as for any payment. `lostDeviceKey` is the address your app saved when it authorized the key.

Build from the updated `saved.config`. An account built from the old configuration fails with `multisig configuration commitment mismatch`.

```ts [recover.ts]
await client.accessKey.revokeSync({ // [!code hl]
  // rebuilt from the new `saved.config`
  account: openAccount([passkey, providerAccount]), // [!code hl]
  accessKey: lostDeviceKey, // [!code hl]
}) // [!code hl]
```

If you didn't save the lost key's address, [list the keys the account has authorized](https://tempo.xyz/developers/docs/accounts/access-keys/revocation#revoke-when-signers-change). A revoked key can't be authorized again, so authorize a new [device key](https://tempo.xyz/developers/docs/accounts/authentication/device-keys) for the new phone.
::::

<span id="recover-funds-sent-on-another-chain" />

## Replace a Lost Backup Key

The passkey and the provider replace a lost backup the same way. Attach `passkey` and `providerAccount` to `openAccount`, and swap the lost backup's address for the new backup's. Have the user sign a test message or approve one small payment with the new backup before relying on it.

The provider's key can't approve alone. If the user loses both the passkey and the backup, the account stays locked. It recovers only if it has a fourth signer, such as a [recovery service](https://tempo.xyz/developers/docs/accounts/apps/self-custodial-wallets#add-a-recovery-service).

## Related

[Set Up a Self-Custodial Wallet With a Provider](https://tempo.xyz/developers/docs/accounts/apps/self-custodial-wallets): Use for creating the three-signer account this guide recovers.

[Revocation](https://tempo.xyz/developers/docs/accounts/access-keys/revocation): Covers revoking access keys, including when signers change.

[Device Keys](https://tempo.xyz/developers/docs/accounts/authentication/device-keys): Covers the keys that sign on each of a user's devices.

[Signing Thresholds](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds): Covers signer weights and approvers on separate devices.
