<!-- tempo-docs-context -->
> Source: [/docs/accounts/custodians/team-multisig](https://tempo.xyz/developers/docs/accounts/custodians/team-multisig.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.

# Set Up a Team Multisig

Set up one Tempo account that three approvers share, where any two must approve each payment.

## Overview

A team multisig is one account whose payments need approvals from several people. It's for stablecoins your team holds together, such as a company's funds or a shared budget. No single person or key moves them.

Here you create a multisig account on the Tempo Accounts devnet. Its three signers are keys held by finance, operations, and a controller. Tempo checks the approvals itself. Neither one stolen key nor your own backend can pay without a second approver.

<span id="treasury-model" />

Each approver's signer has a weight of 1, and the [threshold](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds) is 2. The same threshold covers configuration updates, so no single approver can add or remove a signer.

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

## Setup

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

::::steps
<span id="create-the-treasury-account" />

<span id="create-the-treasury" />

### Create the Multisig

Keep each approver's key somewhere different, so one breach can't reach two of them. Use a hardware wallet, a separate KMS per team, or the approver's passkey. Here finance and operations sign with [private keys](https://tempo.xyz/developers/docs/accounts/authentication/private-keys). The controller signs with a passkey on their own device, so your app knows only the controller's address.

:::code-group
```ts [multisig.ts]
import { Account } from 'viem/tempo'
import { client } from './client'

const env = (name: string) => process.env[name] as `0x${string}`

const finance = Account.fromSecp256k1(env('FINANCE_SIGNER_KEY'))
const operations = Account.fromSecp256k1(env('OPERATIONS_SIGNER_KEY'))
const controller = env('CONTROLLER_PASSKEY_ADDRESS') // the key stays on the controller's device

const multisig = Account.fromConfig({ // [!code hl]
  address: 'infer', // [!code hl]
  owners: [ // [!code hl]
    { owner: finance.address, weight: 1 }, // [!code hl]
    { owner: operations.address, weight: 1 }, // [!code hl]
    { owner: controller, weight: 1 }, // [!code hl]
  ], // [!code hl]
  threshold: 2, // [!code hl]
}) // [!code hl]

// Save with your multisig record.
const saved = { address: multisig.address, config: multisig.config } // [!code hl]

await client.faucet.fundSync({ account: saved.address }) // devnet test stablecoins
```

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

`address: 'infer'` computes the address from the first signers and threshold. Creating the multisig sends nothing. Tempo records the multisig with its first transaction. The address can receive stablecoins before then. On the devnet, the [faucet](https://tempo.xyz/developers/docs/accounts/create#fund-an-account-on-devnet) sends it test stablecoins. The multisig pays transaction fees from its own balance unless a [fee payer](https://tempo.xyz/developers/docs/accounts/fees/fee-sponsorship) sponsors them.

Tempo stores only a hash of the configuration (signers, weights, threshold, salt, and version). The hash is enough to check approvals, but not to tell your app who the signers are. Save the address and configuration with your multisig record. The configuration contains bigints, so [encode it with `AccountConfig.toRpc`](https://tempo.xyz/developers/docs/development/configurable-accounts#keep-the-current-configuration). The address stays the same when signers change. Every approval is built from the saved configuration.

<span id="collect-approvals" />

### Approve a Payment

To pay, rebuild the multisig from its saved record and attach the keys of the approvers signing this payment. The other signers stay as addresses.

```ts [multisig.ts]
import { isAddressEqual, parseUnits } from 'viem'

// A test stablecoin on the devnet
const stablecoin = '0x20c0000000000000000000000000000000000001'
// Placeholder: the vendor's address
const vendor = env('VENDOR_ADDRESS')

/** Rebuilds the multisig from its saved record, with the approvers' keys attached. */
function openMultisig(approvers: readonly Account.RootAccount[]) {
  const { address, config } = saved
  const owners = config.owners.map(({ owner, weight }) => ({
    // other signers stay addresses
    owner: approvers.find((key) => isAddressEqual(key.address, owner)) ?? owner, // [!code hl]
    weight,
  }))
  // Never updated (version 0): the address still comes from the first signers.
  if (config.version === 0n)
    return Account.fromConfig({ address: 'infer', owners, salt: config.salt, threshold: config.threshold })
  return Account.fromConfig({ ...config, address, owners })
}

// Finance and operations approve: 1 + 1 reaches the threshold of 2.
await client.token.transferSync({ // [!code hl]
  account: openMultisig([finance, operations]), // [!code hl]
  // 1,200 of the stablecoin, in base units (6 decimals)
  amount: parseUnits('1200', 6), // [!code hl]
  to: vendor, // [!code hl]
  token: stablecoin, // [!code hl]
}) // [!code hl]
```

Viem signs with the attached keys, highest weight first, until they reach the threshold. With only finance's key attached, it throws `Local owners do not meet the threshold.` and sends nothing. Call `openMultisig` for every action, not once at startup. A [configuration update](https://tempo.xyz/developers/docs/accounts/account-configuration/configuration-updates#add-a-signer) raises the configuration's version and returns the new `config`. Save it, because Tempo rejects approvals built from an older record with `multisig configuration commitment mismatch`.

This example holds two approvers' keys in one process, so that process could pay alone. In production, have each approver check the recipient and amount and sign on their own device. Your app can [collect approvals from separate devices](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds#approvers-on-separate-devices). Your own contract can also [verify the multisig's approvals](https://tempo.xyz/developers/docs/accounts/policies/signing-thresholds#verify-approvals).
::::

<span id="offboard-an-approver" />

<span id="remove-a-departing-approver" />

## Replace an Approver

When an approver leaves, swap their signer for their successor's in one configuration update, at the same weight and threshold. The two remaining approvers approve it, so the multisig stays 2 of 3 throughout.

```ts [multisig.ts]
const successor = env('NEW_CONTROLLER_PASSKEY_ADDRESS') // the successor's passkey, on their device

const replaced = await client.accounts.updateConfigSync({ // [!code hl]
  account: openMultisig([finance, operations]), // [!code hl]
  nextConfig: { // [!code hl]
    owners: saved.config.owners.map((entry) => // [!code hl]
      isAddressEqual(entry.owner, controller) ? { owner: successor, weight: entry.weight } : entry, // [!code hl]
    ), // [!code hl]
    threshold: saved.config.threshold, // [!code hl]
  }, // [!code hl]
}) // [!code hl]
// Save it with the multisig record.
saved.config = replaced.config // [!code hl]
```

The multisig keeps its address and balance. The departing approver's key can no longer approve anything. A [configuration update](https://tempo.xyz/developers/docs/accounts/account-configuration/configuration-updates#remove-a-signer) also removes a signer without a successor, or rotates the key of an approver who stays.

Removing a signer doesn't revoke [access keys](https://tempo.xyz/developers/docs/accounts/access-keys). [List the keys the multisig has authorized](https://tempo.xyz/developers/docs/accounts/access-keys/revocation#revoke-after-a-signer-change). Revoke every access key the departing approver held or managed with `client.accessKey.revokeSync`. Set to zero any token allowance one of those keys approved. If the key was an [admin access key](https://tempo.xyz/developers/docs/accounts/access-keys/admin-access-keys), revoke the keys it authorized too.

<span id="treasury-limits" />

<span id="give-a-team-a-budget" />

<span id="rotate-an-approvers-key" />

<span id="treasury-limits-and-changes" />

## Limits and Changes

A multisig holds 1 to 48 signers. A transaction carries at most 8 approvals, so set a threshold that eight approvers can reach.

A team or a backend can pay within a daily limit without two approvals per payment. Authorize an [access key](https://tempo.xyz/developers/docs/accounts/access-keys) with a [spending limit](https://tempo.xyz/developers/docs/accounts/access-keys/spending-limits) for it.

A configuration update voids approvals collected for a payment that hasn't been sent, so collect them again. If another update landed first, rebuild the multisig from the latest saved configuration.

## Related

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

[Configuration Updates](https://tempo.xyz/developers/docs/accounts/account-configuration/configuration-updates): Use for adding, removing, or rotating a signer.

[Spending Limits](https://tempo.xyz/developers/docs/accounts/access-keys/spending-limits): Covers how much an access key can spend.

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

[Configurable Accounts](https://tempo.xyz/developers/docs/development/configurable-accounts): Covers configuration hashes and keeping the current configuration.
