# Request Export Token

Start migrating a server-custodial wallet to client-side custody: re-confirm the user's password and receive a one-time export token.

## Overview

Step 1 of the key migration flow described in [Non-Custodial Key Lifecycle](https://docs.aureahub.com/docs/guide-non-custodial-keys.md). The server verifies the user's password and then, in one database transaction, invalidates any earlier unused export token for the wallet, stores a new token valid for **10 minutes**, and moves the wallet to `keyManagementScheme: "client_side_pending"`.

- The token is returned once, as 64 hex characters (32 random bytes). The server stores only its SHA-256 hash.
- Send it in the `X-Export-Token` header of [Export Encrypted Key](https://docs.aureahub.com/docs/wallets-export-key.md).
- While the wallet is `client_side_pending`, its server-held key material is untouched and the migration can still be cancelled. Transfers and swaps for the wallet are, however, prepared for client-side signing, so finish the export promptly or cancel.
- The call returns `409` if the wallet is already `client_side_pending` or `client_side`, or is an MPC wallet (`mpc_tss`) with no server-held key. If a token expires unused, call [Cancel Migration](https://docs.aureahub.com/docs/wallets-cancel-migration.md) first, then request a new token.

> ⚠️ This call does not check whether key export is enabled for your tenant — `POST /v1/wallets/export-key` does, and returns `400` if it is not. In that case the wallet stays `client_side_pending` until you cancel the migration.

## Endpoint

### `POST /v1/wallets/request-export-token`

Authentication: bearer token required.

Verifies the password, issues a one-time export token (TTL 10 min) and moves the wallet to client_side_pending.

**Request body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `walletId` | string (uuid) | yes | Server-custodial wallet to migrate. |
| `password` | string | yes | The user's account password. |

**Responses**

`200` OK

```json
{
  "exportToken": "37b6466d6bf9045a6800da16f3d821cb8b2485492a3c896cc15063c58ea7874d"
}
```

`400` Bad Request

```json
{
  "statusCode": 400,
  "error": "BadRequestError",
  "message": "Invalid password"
}
```

## Errors

Error responses

|  |  |  |
| --- | --- | --- |
| 400 | — | `Invalid password` |
| 404 | — | `Wallet not found` — unknown id, or the wallet belongs to another user. |
| 409 | — | `Migration is already in progress or completed for this wallet` |
| 409 | — | `This is a self-custodial MPC wallet — there is no server-held key to export.` |

## Implementation

```javascript
async function requestExportToken(accessToken, walletId, password) {
  const res = await fetch('https://api.aureahub.com/v1/wallets/request-export-token', {
    method: 'POST',
    headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ walletId, password })
  });
  if (!res.ok) throw new Error((await res.json()).message);
  const { exportToken } = await res.json();
  return exportToken; // use as X-Export-Token within 10 minutes
}
```

---

Web version: https://docs.aureahub.com/#wallets-export-token
