<!-- tempo-docs-context -->
> Source: [/docs/accounts/account-configuration/configuration-updates](https://tempo.xyz/developers/docs/accounts/account-configuration/configuration-updates.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.

# Configuration Updates

Changes a configurable account's signers, weights, or threshold at the same address.

## Overview

A configuration update replaces the whole signer list and threshold of a [configurable account](https://tempo.xyz/developers/docs/development/configurable-accounts) in one transaction. A signer is a key that approves the account's transactions. The current signers approve the update. The address, balance, token approvals, and access keys stay the same.

Use a configuration update to back up a key, add an approver, end an approver's access, or rotate a key.

:::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.
:::

<span id="how-it-works" />

<span id="send-the-signer-update" />

<span id="how-a-signer-update-works" />

## Build an Update

Build each update from the address and configuration your app saved for the account ([keep the current configuration](https://tempo.xyz/developers/docs/development/configurable-accounts#keep-the-current-configuration)):

1. Rebuild the account with the approving signers' keys attached, as in [Signing Thresholds](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds#collect-approvals).
2. Pass the new signer list and threshold to `updateConfigSync`. Viem signs with the attached keys, highest weight first, until their weights reach the current threshold.

Approvals can also come from [separate devices](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds#approvers-on-separate-devices).

Each update raises the configuration's version by one. Save the `config` it returns with the account record. Rebuild from the latest saved record before every payment or update. An account built from the old configuration no longer matches the hash Tempo stores, so its next transaction fails.

Once the update lands, a removed signer can't approve anything. Approvals collected under the old signer list stop counting.

<span id="what-adding-a-signer-does" />

<span id="choose-the-new-signers-weight" />

<span id="add-a-signer-in-code" />

<span id="add-a-recovery-signer" />

## Add a Signer

The weight you give the new signer decides what it can approve alone. Every signer has a weight of at least 1:

* **Below the Threshold:** The new signer approves only together with others. No single backup or recovery signer controls the account.
* **At or Above the Threshold:** The new signer approves anything alone, including removing every other signer. Give this weight only to a key you'd trust with the whole account.

The threshold stays the same unless you change it, so each new signer adds a way to reach it. To keep requiring the same approvers, raise the threshold in the same update.

A recovery signer can approve payments at its weight too. Give it enough weight that the account can [lose any one signer](https://tempo.xyz/developers/docs/accounts/account-configuration#plan-for-recovery) and still reach the threshold, and no more.

`client.ts` connects to the Tempo Accounts devnet ([set up the devnet key](https://tempo.xyz/developers/docs/accounts/create#connect-to-the-devnet)). The remove and replace examples use the same `account.ts` and `client.ts`.

:::code-group
```ts [add-signer.ts]
import { writeFileSync } from 'node:fs'
import { AccountConfig } from 'viem/tempo'
import { account, config } from './account'
import { client } from './client'

// The new signer's address
const newSigner = process.env.NEW_SIGNER as `0x${string}`

const { config: updated } = await client.accounts.updateConfigSync({
  account,
  nextConfig: { // [!code hl]
    owners: [...config.owners, { owner: newSigner, weight: 1 }], // [!code hl]
    threshold: config.threshold, // [!code hl]
  }, // [!code hl]
})

// Save the new configuration. The next action rebuilds the account from it.
writeFileSync('account.json', JSON.stringify({ address: account.address, config: AccountConfig.toRpc(updated) }))
```

```ts [account.ts]
import { readFileSync } from 'node:fs'
import { isAddressEqual } from 'viem'
import { Account, AccountConfig } from 'viem/tempo'

// The address and configuration your app saved for the account
const saved = JSON.parse(readFileSync('account.json', 'utf8'))
export const config = AccountConfig.fromRpc(saved.config)

// The signers approving this time. Their weights must reach the threshold.
const approvers = [Account.fromSecp256k1(process.env.SIGNER_KEY as `0x${string}`)]

const owners = config.owners.map(({ owner, weight }) => ({
  owner: approvers.find((key) => isAddressEqual(key.address, owner)) ?? owner,
  weight,
}))
// Until the first update, the configuration derives the address.
export const account =
  config.version === 0n
    ? Account.fromConfig({ address: 'infer', owners, salt: config.salt, threshold: config.threshold })
    : Account.fromConfig({ ...config, address: saved.address, owners })
```

```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}`)}` } },
  }),
})
```
:::

`account.ts` rebuilds the account from the record your app saved and attaches the approving keys. The new signer can be a passkey, a device key, or a key in a KMS or hardware wallet. It can also be an address from a recovery provider. Viem sorts the signers by address, as Tempo requires. [`updateConfig`](https://viem.sh/tempo/actions/accounts.updateConfig) in Viem lists every option.

Tempo can't tell whether a signer still holds its key. Have each new signer approve once before you rely on it.

<span id="what-removing-a-signer-stops" />

<span id="revoke-what-the-old-signer-authorized" />

## Remove a Signer

When an approver leaves or a key is lost, the remaining signers approve a list without that signer.

```ts [remove-signer.ts]
import { writeFileSync } from 'node:fs'
import { isAddressEqual } from 'viem'
import { AccountConfig } from 'viem/tempo'
import { account, config } from './account'
import { client } from './client'

// The address of the signer that's leaving
const departing = process.env.DEPARTING_SIGNER as `0x${string}`

const { config: updated } = await client.accounts.updateConfigSync({
  account,
  nextConfig: { // [!code hl]
    owners: config.owners.filter(({ owner }) => !isAddressEqual(owner, departing)), // [!code hl]
    threshold: config.threshold, // [!code hl]
  }, // [!code hl]
})

writeFileSync('account.json', JSON.stringify({ address: account.address, config: AccountConfig.toRpc(updated) }))
```

If the remaining weights fall short of the threshold, lower `threshold` in the same update. Or [replace the signer](https://tempo.xyz/developers/docs/accounts/account-configuration/configuration-updates#replace-a-signer-in-one-update) instead. Lowering the threshold to 1 lets any one remaining signer pay and change signers alone. A 2-of-3 multisig that drops to two signers at threshold 1 no longer needs two approvals. For a team multisig, [replace an approver](https://tempo.xyz/developers/docs/accounts/custodians/team-multisig#replace-an-approver).

[Access keys](https://tempo.xyz/developers/docs/accounts/access-keys) keep working after the update. A device key or agent key the removed signer authorized keeps paying within its limits. [Revoke every key](https://tempo.xyz/developers/docs/accounts/access-keys/revocation#revoke-after-a-signer-change) its holder could reach, including any an attacker added.

<span id="rotate-a-signer-at-the-same-weight" />

<span id="rotate-a-key-by-replacing-it-in-one-update" />

<span id="replace-a-signer-in-one-update" />

## Replace a Signer

Rotate when the holder stays but the key changes, such as a new hardware wallet, device, or wallet provider. Put the new key at the old one's weight and keep the threshold. Every group of signers that could approve before still can.

```ts [rotate-signer.ts]
import { writeFileSync } from 'node:fs'
import { isAddressEqual } from 'viem'
import { AccountConfig } from 'viem/tempo'
import { account, config } from './account'
import { client } from './client'

// The key being replaced, and its replacement
const oldSigner = process.env.OLD_SIGNER as `0x${string}`
const newSigner = process.env.NEW_SIGNER as `0x${string}`

const { config: updated } = await client.accounts.updateConfigSync({
  account,
  nextConfig: { // [!code hl]
    owners: config.owners.map((entry) => // [!code hl]
      isAddressEqual(entry.owner, oldSigner) ? { owner: newSigner, weight: entry.weight } : entry, // [!code hl]
    ), // [!code hl]
    threshold: config.threshold, // [!code hl]
  }, // [!code hl]
})

writeFileSync('account.json', JSON.stringify({ address: account.address, config: AccountConfig.toRpc(updated) }))
```

Because one update swaps the keys, the account never has both keys active, or neither.

<span id="rotate-a-compromised-signer" />

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

<span id="remove-a-lost-or-compromised-signer" />

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

### Lost or Compromised Signers

When a signer is lost or compromised, the other signers approve the update without it. Tempo has no recovery service or override. This works only if their weights still reach the threshold. Check the [recovery rule](https://tempo.xyz/developers/docs/accounts/account-configuration#plan-for-recovery) when you create the account. To replace a user's lost passkey, [recover their account](https://tempo.xyz/developers/docs/accounts/apps/recover-an-account).

Treat a compromised signer as an attacker. Leave it out of the approvers. If its weight reaches the threshold alone, it can remove your other signers before you remove it.

<span id="add-signer-limits" />

<span id="limits-on-adding-signers" />

<span id="signer-rotation-limits" />

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

<span id="remove-signer-limits" />

<span id="limits-on-removing-signers" />

<span id="signer-update-limits" />

## Limits

* An account holds 1 to 48 signers, with total weight at most 255. A transaction carries at most 8 approvals, so the 8 largest weights must reach the threshold. Viem checks these limits before sending and throws `Invalid account config` with the reason. A signer already on the list fails with the reason `owners must be strictly ascending`.
* The approving signers must reach the current threshold, and the new signer list must reach the new one. If the attached keys fall short, Viem throws `Local owners do not meet the threshold.` If the new list falls short, it throws `Invalid account config: threshold exceeds total owner weight.`
* `multisig configuration commitment mismatch` means the account was built from an outdated configuration. It no longer matches the hash Tempo stores, for example because another configuration update landed first. Load the latest saved configuration and rebuild the update.
* Rebuild with `address: 'infer'` only while the configuration is version 0. After a configuration update, the current signers no longer derive the address. Pass the saved `address`, as [Signing Thresholds](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds#collect-approvals) does.

## Related

[Account Configuration](https://tempo.xyz/developers/docs/accounts/account-configuration): Covers signers, weights, and recovery planning for configurable accounts.

[Signing Thresholds](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds): Use for collecting approvals that reach a threshold.

[Revocation](https://tempo.xyz/developers/docs/accounts/access-keys/revocation): Use for revoking access keys after a signer change.

[Recover a User's Account](https://tempo.xyz/developers/docs/accounts/apps/recover-an-account): Use for replacing a user's lost passkey.

[Configurable Accounts](https://tempo.xyz/developers/docs/development/configurable-accounts#configuration-updates): Covers the protocol reference for configuration updates.
