# MPC Wallet Setup

An MPC wallet's address exists seconds after its key generation. It can receive once its backup is kept and its second ceremony is done, and it signs from then on: one setup state, the same for every client, tells your app which step comes next.

## Overview

- **One state per wallet.** [Get a Wallet's Setup](https://docs.aureahub.com/docs/mpc-setup-get.md) answers the wallet's `state`, its `nextStep`, `canReceive`, `canSpend` and when each step happened. Your web app, your mobile app and your server read the same answer.
- **Your server hears each step** as a `wallet.setup.*` event (Events), and a bank account bound to the wallet's address as `account.bound`.
- **Nothing is skipped.** The key is still split 2-of-3, the user still keeps a backup, the verification of the bank ramp still decides when an account opens, and your policy still checks every payment. Only the waiting is gone: a step that does not need another one no longer waits for it.
- For the ceremonies themselves see [Start DKG Ceremony](https://docs.aureahub.com/docs/mpc-dkg-start.md), [Register MPC Wallet](https://docs.aureahub.com/docs/mpc-wallet-register.md), [Start Aux Ceremony](https://docs.aureahub.com/docs/mpc-aux-start.md) and [Start a Backup Proof](https://docs.aureahub.com/docs/mpc-backup-proof-start.md).

## Receive Now, Sign Later

The rule: **receiving needs the address and a kept backup; spending needs the second ceremony.** A CGGMP wallet's key shares exist from its key generation on; its second ceremony (the auxiliary ceremony) is what signing needs. A wallet can receive from the backup on, before its second ceremony ends (Can Receive, Can Spend):

1. **Address in seconds.** The key generation (DKG) and [Register MPC Wallet](https://docs.aureahub.com/docs/mpc-wallet-register.md) give the wallet its address, typically in about 15 seconds. Show it at once.
2. **The second ceremony runs in the background** ([Start Aux Ceremony](https://docs.aureahub.com/docs/mpc-aux-start.md)). It takes from tens of seconds to a few minutes, depending on the device. Run it off the main thread and let the user go on.
3. **The backup.** The user keeps the wallet's backup — the recovery file, or the passkey backup — and your app reports it ([Report a Setup Step](https://docs.aureahub.com/docs/mpc-setup-report.md)) or has the user prove it ([Start a Backup Proof](https://docs.aureahub.com/docs/mpc-backup-proof-start.md)). From then on two shares are in the user's hands, and the wallet can be recovered without Aurea. `canReceive` turns `true` at once, before the second ceremony ends.
4. **The account.** A bank account that delivers to the wallet [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md) opens as soon as the wallet can receive (`canReceive`) *and* the user's verification is approved. Start the verification while the wallet is made: the account opens by itself when the approval arrives (A Bank Account on the Approval).
5. **Spending.** A payment, a signed message or a link that needs the wallet's signature waits for `canSpend`. Until then show *"Ready in a few minutes"* on the spend actions only — receiving, the address and the balance are already there.

> Tip: Machine time to "account ready" is then the verification's own time plus a few seconds: the wallet is never what the user waits for.

## Setup States

| state | Meaning | nextStep |
| --- | --- | --- |
| pending | Registered after its key generation. A CGGMP wallet still needs its second ceremony. | `start_aux` |
| aux_running | The second ceremony is running. | `wait_for_aux` |
| ready_for_backup | The wallet can sign; the user keeps its backup next. | `confirm_backup` |
| backup_confirmed | The backup is confirmed: proven, or reported by your app (`backupConfirmedBy` `proof` or `client_report`). | `activate` |
| active | Your app reported the setup complete. | `none` |
| retired | A pending wallet given up after its balance read zero on every chain (Start Over). Its shares are kept at least 90 days, until `sharesKeptUntil`. | `create_wallet` |

- `nextStep`: `start_aux` — run the second ceremony on this same wallet; `wait_for_aux` — one is running; `confirm_backup` — have the user keep the backup, then report or prove it; `activate` — report the setup complete; `none` — done; `create_wallet` — this wallet is retired, make a new one.
- States only move forward. A step reported twice changes nothing (`changed` `false`).
- A GG18 wallet has no second ceremony: it is past `pending` from its key generation on.

## Can Receive, Can Spend

- `canReceive`: registered, not retired, and its backup confirmed — its second ceremony need not be done. Bind the address for receiving — an account's deposits, a payment to it — only then.
- **Receiving before the second ceremony.** `canReceive` turns `true` from the backup on, without the second ceremony: the address takes deposits while the device still works on it. The backup kept that early still recovers the wallet — should the device be lost before the wallet's first signature, the second ceremony runs again from the backup (A Lost Device Before the First Signature).
- `canSpend`: it can receive, and its second ceremony is done (a GG18 wallet from its key generation on). Enable payments, signed messages and links that need a signature only then.
- [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md) Your tenant can have the routes that bind an address for receiving — the bank ramp's pay-in, its account link, a card onramp session, a payout to the address — refuse an MPC address whose backup is not confirmed: `409` `MPC_WALLET_BACKUP_REQUIRED`, with the wallet's `walletId` and `nextStep` `confirm_backup`. Until then the routes answer as before.

## Resume After a Reload

- A setup survives a reload, a closed tab or a locked phone. On start, read [List Wallet Setups](https://docs.aureahub.com/docs/mpc-setup-list.md) and resume the newest wallet whose `state` is not `active` or `retired`, from its `nextStep`: never a second key generation.
- An interrupted second ceremony runs again on the same wallet (`start_aux`): the address, the account bound to it and any funds received stay as they are.
- **One wallet in setup per user and purpose** (curve and network type): a second key generation or registration is answered `409` `MPC_WALLET_SETUP_PENDING`, with the pending wallet's `walletId`, `address`, `state`, `nextStep` and `purpose` — resume it, or start over (Start Over).
- **An abandoned wallet does not block.** A pending wallet no client went on with — a CGGMP wallet whose second ceremony never started, with no backup and no ceremony but its key generation, registered more than about 30 seconds ago — is retired by the next key generation of its purpose once its balance reads zero on every chain, and that key generation goes on (`wallet.setup.retired`; nothing is deleted). One the user can resume, or one that holds funds, still answers `409`.

## Start Over

- When the user's device no longer holds the wallet's device share — another browser, cleared data — and the backup was never kept, [Restart a Pending Wallet](https://docs.aureahub.com/docs/mpc-wallet-restart.md) retires the pending wallet once its balance reads zero on every chain, and a new key generation can make another. Nothing is deleted.
- Three restarts per user in 24 hours by default. Past them the answer is `429` `MPC_WALLET_RESTART_LIMIT` with `Retry-After` and the support address, [help@aureahub.com](mailto:help@aureahub.com).
- A wallet that holds funds is never retired (`409` `MPC_WALLET_NOT_EMPTY`): the user recovers it with its backup instead.

## A Lost Device Before the First Signature

- Because wallets receive before the second ceremony, a wallet can lose its device share — another browser, cleared data — after its backup was kept but before its first signature, perhaps with funds already received. A **recovery** second ceremony on the same key shares makes it able to sign again: the address, its accounts and its funds stay as they are.
- Restore the wallet from its backup on the user's device, then call [Start Aux Ceremony](https://docs.aureahub.com/docs/mpc-aux-start.md) with `{ "wireVersion": 2, "auxRecovery": true }`. The restored client takes part as both of the user's parties, `DEVICE` and `BACKUP`, and Aurea's cosigner runs the ceremony on the same shares; then the wallet signs as before.
- Only for a wallet that has never signed — a backup proof counts as a signature —, its backup confirmed, not retired, on relay version 2 and with Aurea's cosigner. Otherwise `409` `MPC_AUX_RECOVERY_REFUSED` with `details.reason`: `signed` (it has signed: recover it with its backup as any other wallet), `no_backup` (its backup was never confirmed: the device share was its only copy — Start Over), `retired` (make a new wallet), `v3_wallet` (a wallet of a kind that has no recovery ceremony), `relay_v1` (send `wireVersion: 2`) or `tenant_cosigner` (made with your tenant's own co-signer).
- Without `auxRecovery`, Start Aux Ceremony is the ordinary second ceremony.

## A Bank Account on the Approval

- [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md) Ask for the user's pay-in account ([`POST /v1/ramp/bank/payin/initiate`](https://docs.aureahub.com/docs/payin-initiate.md)) with `bindOnApproval: true` while the user's verification is still under way: instead of the refusal of before, the request is kept and answered `202` with `waiting` — its `requestId`, `accountKind`, `currency`, `isSandbox`, `status` (`waiting`, `bound`, `failed` or `cancelled`), `attempts`, `lastErrorCode`, `accountId`, `createdAt`, `closedAt` and `nextStep` (`wait_for_approval`, `none` or `ask_again`). `accountKind` is `bank_payin` for a pay-in account.
- [List Waiting Accounts](https://docs.aureahub.com/docs/payin-waiting-list.md) answers the user's waiting accounts, newest first, and [Withdraw a Waiting Account](https://docs.aureahub.com/docs/payin-waiting-cancel.md) withdraws one before it opens: nothing had reached the bank ramp.
- When the approval arrives and the wallet can receive, the account opens by itself — the same account the call would have opened — and your server hears `account.bound` with `reason` `auto_bound`. A rejected verification is never kept: ask again once the user is approved (`ask_again`).
- Without `bindOnApproval`, or where your tenant does not have it, the route answers exactly as before.
- A bound account keeps working for its address: Aurea refreshes the account's details with the bank ramp by itself, before they lapse. If a refresh is answered with other details for the same address, `account.bound` says so (`reason` `account_changed`). Read the current details from [Payment Methods](https://docs.aureahub.com/docs/payin-methods.md) before you show them.
- Aurea reads each account's status at the bank ramp instead of refreshing it every day: Payment Methods answers `accountStatus` — `Active`, `PendingClosure`, `Closed` or `Missing` (no longer listed) — and an account that is no longer `Active` is asked for again by itself, the new one replacing it (`account.bound`, `reason` `account_changed`).

## Events

To the [webhook endpoints](https://docs.aureahub.com/docs/guide-events.md) that name them; a testnet wallet's news to your sandbox endpoints, a mainnet one's to your production ones.

| type | Sent when |
| --- | --- |
| wallet.setup.aux_started | The wallet's second ceremony started (it cannot sign yet). |
| wallet.setup.ready_for_backup | The wallet can sign: the user keeps its backup next. |
| wallet.setup.backup_confirmed | The backup is confirmed: proven, or reported by your app. |
| wallet.setup.completed | Your app reported the setup complete. |
| wallet.setup.retired | A pending wallet was retired after its balance read zero: a new one can be made. |
| account.bound | An account was bound to the address it delivers to: opened, re-pointed to another address, opened by itself on the verification's approval, or changed by a refresh. |

`wallet.setup.*`'s `data`: `walletId`, `userId`, `chain`, `isTestnet`, `address`, `curve`, `scheme`, `state` (the state after the step), `reason` (retired only: `restart` or `orphan` — left behind by a newer wallet for 30 days; otherwise `null`) and `changedAt`. Never a share, nor anything about the key but its curve and engine.

```json
{
  "type": "wallet.setup.backup_confirmed",
  "data": {
    "walletId": "7c1e9b24-3f5a-4d8e-9b6c-1a2f3e4d5c6b",
    "userId": "2b8f0c1d-5e6a-4f7b-8c9d-0e1f2a3b4c5d",
    "chain": "base",
    "isTestnet": true,
    "address": "0xAbCd…",
    "curve": "secp256k1",
    "scheme": "cggmp",
    "state": "backup_confirmed",
    "reason": null,
    "changedAt": "2026-11-05T09:30:12.000Z"
  }
}
```

`account.bound`'s `data`: `bindingId`, `accountKind` (`bank_payin` — a user's pay-in account; `remittance_account` — a [Remittance](https://docs.aureahub.com/docs/guide-remittance.md) customer's account; `bank_link` — a wallet linked for a euro IBAN at the bank ramp), `accountId`, `userId`, `customerId`, `walletId`, `address`, `network`, `isSandbox`, `reason` (`opened`, `repointed`, `auto_bound` or `account_changed`), `previousAddress` (the address bound before, for `repointed` and `account_changed`) and `boundAt`. Never an account number: read it from the account's own route.

```json
{
  "type": "account.bound",
  "data": {
    "bindingId": "a3d5c7e9-1b2f-4a6c-8e0d-2f4b6d8a0c1e",
    "accountKind": "bank_payin",
    "accountId": "5e7a9c1b-3d4f-4b6a-8c0e-1f3a5c7e9b2d",
    "userId": "2b8f0c1d-5e6a-4f7b-8c9d-0e1f2a3b4c5d",
    "customerId": null,
    "walletId": null,
    "address": "0xAbCd…",
    "network": "…",
    "isSandbox": true,
    "reason": "auto_bound",
    "previousAddress": null,
    "boundAt": "2026-11-05T09:31:40.000Z"
  }
}
```

- Each event is sent once per step, with a stable event id: a re-sent delivery is the same event ([Webhooks & Events › Order and Duplicates](https://docs.aureahub.com/docs/guide-events.md)). Read [Get a Wallet's Setup](https://docs.aureahub.com/docs/mpc-setup-get.md) for the current state rather than relying on the order of arrival.

## Errors and Their Next Step

Every refusal carries a stable `details.code` ([Errors & Codes](https://docs.aureahub.com/docs/errors.md)); most also name the step to take.

| Status | details.code | What to do next |
| --- | --- | --- |
| 409 | MPC_WALLET_SETUP_PENDING | The user already has a wallet in setup: resume it from `details.nextStep` (`details.walletId`, `details.address`), or start over with Restart a Pending Wallet. |
| 409 | MPC_WALLET_BACKUP_REQUIRED | The wallet cannot receive yet: have the user keep the backup, report it (`backup_confirmed`) or prove it, then call again. |
| 409 | MPC_WALLET_SETUP_STEP_REFUSED | The step cannot be taken now: `details.reason` is `aux_not_done` (finish the second ceremony), `backup_not_confirmed` (confirm the backup) or `retired` (make a new wallet); `details.nextStep` names the step. |
| 422 | MPC_WALLET_SETUP_REPORT_INVALID | `at` is outside the wallet's life: send the time the step happened in your app, not before the wallet was registered and not in the future, or leave it out. |
| 409 | MPC_WALLET_NOT_PENDING | Only a wallet in setup can be restarted: an active wallet is recovered with its backup. |
| 409 | MPC_WALLET_NOT_EMPTY | The wallet holds funds and is never retired: recover it with its backup. |
| 503 | MPC_WALLET_BALANCE_UNVERIFIED | The balance could not be read on every chain: try again in a minute. |
| 429 | MPC_WALLET_RESTART_LIMIT | The day's restarts are used: wait for `Retry-After`, or write to [help@aureahub.com](mailto:help@aureahub.com). |
| 429 | MPC_SETUP_THROTTLED | Too many setup calls: wait for `Retry-After`. |
| 403 | MPC_WALLET_SETUP_RULES_OFF | The setup rules are off for your tenant: ask your Aurea contact. |
| 403 | MPC_WALLET_NOT_OWNED | Only the wallet's own user drives its setup: sign in as that user. |
| 404 | MPC_WALLET_NOT_FOUND | No such MPC wallet in your tenant: read List Wallet Setups for the user's wallets. |
| 409 | MPC_AUX_RECOVERY_REFUSED | This wallet cannot run a recovery second ceremony: `details.reason` says why (A Lost Device Before the First Signature). |
| 409 | MPC_WALLET_ABORT_HOLD | The wallet's ceremonies are paused after repeated failed ceremonies, until our team has reviewed it: write to [help@aureahub.com](mailto:help@aureahub.com) with `details.walletId` (`details.nextStep` `contact_support`). |
| 503 | MPC_COSIGNER_QUARANTINED | Signing is paused for the wallet's cosigner after a failed ceremony: try again later. |
| 503 | MPC_AUX_FINISHING | A signature started right after the second ceremony, while Aurea's cosigner still stores its share: send it again after `Retry-After` (2 seconds). |
| 404 | WAITING_ACCOUNT_NOT_FOUND | No such waiting account of the user: read List Waiting Accounts. |

---

Web version: https://docs.aureahub.com/#guide-wallet-setup
