# Exporting and Importing Full Keys (Exit MPC)

The Vertex can reconstruct a complete private key from its distributed key shares and hand it to a single recipient, as well as take a pre-existing private key and split it into MPC key shares.
These flows let you exit the MPC setup entirely (for example, when migrating to another system) or onboard keys that were generated outside of MPC.

This is different from [Backing Up Key Shares](/vertex/backup_key_shares): a share backup exports only this Vertex's share for disaster recovery, and the key remains distributed. A full export combines a threshold of shares into the original private key, so a single party ends up holding the entire key.

:::warning[Security Note]
After a full export, the resulting private key is no longer protected by MPC: whoever holds it can sign on its own.
Treat full exports as sensitive ceremonies, restrict the `KeysExporter` and `KeysImporter` Roles to dedicated Users, and consider deleting or [freezing](https://docs.sodot.dev/vertex-api-reference/#tag/admin/POST/admin/keys/freeze) the key shares after a successful export.
:::

All endpoints on this page exist for each supported algorithm under its own prefix: `ecdsa`, `ed25519`, `bip340`, `exportable-ed25519` and `sr25519`.
The examples below use `ecdsa`; replace the prefix to match your key's algorithm.
HMAC and RS256 keys have analogous export endpoints that are documented separately.

## Exporting a Full Private Key

A full export is an MPC ceremony: a threshold (`t`) of the parties holding shares of the key participate, and only one of them (the recipient) receives the reconstructed private key.
The recipient is designated by its `export_id`.

:::note[Permissions]
The [`export/exportId`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/GET/ecdsa/export/exportId) and [`export/full`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/export/full) endpoints require a User with the `KeysManager` or `KeysExporter` Role.
:::

### Step 1: Get the Export ID of the Recipient

On the device that should receive the full private key, fetch the `export_id` of its key share using the [`export/exportId`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/GET/ecdsa/export/exportId) endpoint:

```bash
curl -L -X GET 'https://<YOUR_VERTEX>/ecdsa/export/exportId?key_id=<KEY_ID>' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

The endpoint takes either a `key_id` or a `key_name` query parameter (but not both) and returns:

```json
{
  "export_id": "GxJiV8M2fRscBkm1MmK5LJZgdE4T2sfjE8apBWPFg9Pf"
}
```

Share this `export_id` with the other parties: it is the value they will pass as `export_to` in the next step.
If the recipient is an SDK device rather than a Vertex, use the equivalent SDK operation to obtain its export ID.

### Step 2: Create a Room for the Export

Like signing, a full export requires a Relay Room. For a `t-of-n` key, `t` is the required room size.
Create it using the [`create-room`](https://docs.sodot.dev/vertex-api-reference/#tag/common/POST/create-room) endpoint:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/create-room' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_size": 2
}'
```

### Step 3: Run the Export Ceremony

All `t` participating parties call the [`export/full`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/export/full) endpoint with the same `room_uuid` and the same `export_to`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/ecdsa/export/full' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID>",
  "export_to": "<EXPORT_ID_OF_RECIPIENT>"
}'
```

The request takes either a `key_id` or a `key_name` (but not both). The response is:

```json
{
  "private_key": "..."
}
```

Only the party whose `export_id` equals `export_to` receives the reconstructed private key; all other participants receive a `200 OK` with an empty `private_key` string.

If the key share was frozen, the request fails with a `400` response and the `frozen_key_share` error type; a missing key results in `key_not_found`. See [Vertex Errors](/vertex/vertex_errors) for the full error format.

## Importing a Full Private Key

The import flow is the reverse ceremony: a party in possession of a complete private key distributes it as `t-of-n` MPC key shares.
The resulting key shares correspond to the exact same public key as the original private key, so existing addresses and identities keep working.

There are two sides to the ceremony:

* The **importer**, who holds the private key, calls [`import/key-importer`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/import/key-importer).
* Every **recipient**, who joins the quorum without ever seeing the private key, calls [`import/import-key-share-recipient`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/import/import-key-share-recipient).

:::note[Permissions]
Both import endpoints require a User with the `KeysManager` or `KeysImporter` Role.
:::

### Step 1: Create Key Shares and Exchange Keygen IDs

Exactly like [key generation](/vertex/keygen/generating_keys), every party (importer and recipients alike) first calls the [`create`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/create) endpoint to receive a `key_id` and a `keygen_id`, and all parties exchange their `keygen_id`s over an authenticated channel.

### Step 2: Create a Room for the Import

Create a Relay Room with `room_size` equal to `n`, the total number of parties, using the [`create-room`](https://docs.sodot.dev/vertex-api-reference/#tag/common/POST/create-room) endpoint.

### Step 3: Run the Import Ceremony

The importer calls [`import/key-importer`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/import/key-importer) with the private key material:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/ecdsa/import/key-importer' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID_FROM_CREATE>",
  "threshold": 2,
  "full_private_key": "<PRIVATE_KEY>",
  "others_keygen_ids": [
    "<KEYGEN_ID_OF_PARTY_2>",
    "<KEYGEN_ID_OF_PARTY_3>"
  ]
}'
```

At the same time, every recipient calls [`import/import-key-share-recipient`](https://docs.sodot.dev/vertex-api-reference/#tag/ecdsa/POST/ecdsa/import/import-key-share-recipient) with the same `room_uuid`; the request body is identical except that it carries no private key material:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/ecdsa/import/import-key-share-recipient' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID_FROM_CREATE>",
  "threshold": 2,
  "others_keygen_ids": [
    "<KEYGEN_ID_OF_IMPORTER>",
    "<KEYGEN_ID_OF_PARTY_3>"
  ]
}'
```

Receiving a `200 OK` response indicates that the import was successful, and each party can use its own `key_id` for signing and other operations. A failed distribution results in a `400` response with the `failed_to_import_key_share` error type.

:::note[Private key format per algorithm]
The importer's request body differs slightly by algorithm:

* **Ecdsa, BIP340 and ExportableEd25519** take the key as `full_private_key`.
* **Ed25519** ([`ed25519/import/key-importer`](https://docs.sodot.dev/vertex-api-reference/#tag/ed25519/POST/ed25519/import/key-importer)) takes `privkey_value` together with `privkey_type`, which is either `"raw"` (a raw scalar) or `"rfc8032"` (an RFC 8032 seed).
* **Sr25519** ([`sr25519/import/key-importer`](https://docs.sodot.dev/vertex-api-reference/#tag/sr25519/POST/sr25519/import/key-importer)) takes `privkey_value` together with `privkey_type`, which is either `"raw"` or `"minisecret"`.

The recipient request body is the same for all algorithms.
:::

## ExportableEd25519 Keys

`exportable-ed25519` is a dedicated Ed25519 key type for keys that are designed to be exported in full later, for example API keys for exchanges that must eventually be surrendered to the exchange or rotated out of MPC.

Unlike the other algorithms, ExportableEd25519 keys are not generated with an MPC keygen protocol.
Instead, one party **samples** the raw key and splits it into shares, which the other parties **receive**.

:::warning[Trusted Sampling Party]
The sampling party briefly holds the complete raw key during generation, so it is essentially trusted in this process.
Run the sampling step on the party you trust the most.
:::

### Generating an ExportableEd25519 Key

First, every party calls the [`exportable-ed25519/create`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/create) endpoint (with an optional `key_name` query parameter) to set up a key share:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/exportable-ed25519/create' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

The response contains a `key_id` and a `keygen_id`. As with regular key generation, all parties exchange `keygen_id`s over an authenticated channel and create a Relay Room of size `n` using [`create-room`](https://docs.sodot.dev/vertex-api-reference/#tag/common/POST/create-room).

Then, the sampling party calls [`exportable-ed25519/sample-key`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/sample-key):

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/exportable-ed25519/sample-key' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID_FROM_CREATE>",
  "num_parties": 3,
  "threshold": 2,
  "others_keygen_ids": [
    "<KEYGEN_ID_OF_PARTY_2>",
    "<KEYGEN_ID_OF_PARTY_3>"
  ]
}'
```

While every other party calls [`exportable-ed25519/receive-key`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/receive-key) with the same request body (using its own `key_id` and the other parties' `keygen_id`s).
Receiving a `200 OK` response on all parties indicates that the key was generated successfully.

Both endpoints require a User with the `KeysManager` Role.

### Generating in Cluster Mode

When the parties are Vertex servers configured as a [Cluster](/vertex/keygen/cluster_keygen), you can skip the `create` and `keygen_id` exchange steps.
The sampling Vertex calls [`cluster/exportable-ed25519/sample-key`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/cluster/exportable-ed25519/sample-key) and the other Vertices call [`cluster/exportable-ed25519/receive-key`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/cluster/exportable-ed25519/receive-key):

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/cluster/exportable-ed25519/sample-key' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "cluster_name": "my_cluster",
  "room_uuid": "<ROOM_UUID>",
  "num_parties": 3,
  "threshold": 2
}'
```

The request may also include an optional `key_name`, and a `policy_name` or `policy_id` to attach a Policy to the new key.
In response, each Vertex receives its own new `key_id`:

```json
{
  "key_id": "..."
}
```

### Signing with an ExportableEd25519 Key

ExportableEd25519 keys produce standard Ed25519 signatures using the [`exportable-ed25519/sign`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/sign) endpoint, which requires a User with the `CryptoUser` or `KeysManager` Role.
As with regular [signing](/vertex/signing), a Relay Room of size `t` is required:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/exportable-ed25519/sign' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID>",
  "msg": "<HEX_ENCODED_MESSAGE>"
}'
```

The request takes either a `key_id` or a `key_name`, and optionally hex encoded `extra_data` for the [Rule Server](/vertex/policies/rule_server).
Note that unlike the other algorithms, ExportableEd25519 does not support a `derivation_path`.
The response contains the hex encoded signature:

```json
{
  "signature": "..."
}
```

### Exporting and Importing ExportableEd25519 Keys

ExportableEd25519 keys use the same export and import ceremonies described above, under the `exportable-ed25519` prefix:

* [`exportable-ed25519/export/exportId`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/GET/exportable-ed25519/export/exportId) to get the recipient's export ID.
* [`exportable-ed25519/export/full`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/export/full) to reconstruct and export the full private key.
* [`exportable-ed25519/import/key-importer`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/import/key-importer) and [`exportable-ed25519/import/import-key-share-recipient`](https://docs.sodot.dev/vertex-api-reference/#tag/exportableed25519/POST/exportable-ed25519/import/import-key-share-recipient) to bring an existing Ed25519 key into MPC.

ExportableEd25519 shares can also be [backed up](/vertex/backup_key_shares) like any other key share, using the `exportable-ed25519/backup` endpoints.
