# Creating keys

`create_private_keys` mints a fresh private key **inside the signer** (the API
service never sees plaintext) and returns the addresses derived from it. You choose
the **curve** and which **address formats** to derive.

::: info
The key is generated in the isolated signer, envelope-encrypted under KMS, and
stored as ciphertext. Only the derived public addresses and the `privateKeyId` come
back.
:::

## Request

`POST /public/v1/submit/create_private_keys`

| Parameter | Type | Default | Notes |
| --- | --- | --- | --- |
| `name` | string | `"unnamed"` | Human label for the key. |
| `curve` | string | `CURVE_SECP256K1` | One of `CURVE_SECP256K1`, `CURVE_ED25519`, `CURVE_P256`. |
| `addressFormats` | string[] | curve default | Address formats to derive. Omit to get the curve's default format. |
| `environment` | string | `"production"` | Bound into the KMS encryption context. |

When `addressFormats` is omitted, the signer derives the **curve default**:

| Curve | Default address format |
| --- | --- |
| `CURVE_SECP256K1` | `ADDRESS_FORMAT_ETHEREUM` |
| `CURVE_ED25519` | `ADDRESS_FORMAT_SOLANA` |
| `CURVE_P256` | `ADDRESS_FORMAT_COMPRESSED` |

### Example — SDK

```ts
const { privateKeyId, addresses } = await client.createPrivateKey({
  name: "treasury-evm",
  curve: "CURVE_SECP256K1",
  addressFormats: ["ADDRESS_FORMAT_ETHEREUM"],
});
```

### Example — raw HTTP

```json
{
  "type": "ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2",
  "timestampMs": "1748956800000",
  "organizationId": "org-…",
  "parameters": {
    "name": "treasury-evm",
    "curve": "CURVE_SECP256K1",
    "addressFormats": ["ADDRESS_FORMAT_ETHEREUM"]
  }
}
```

## Response

`result.createPrivateKeysResult` holds the new key id and the derived addresses
(one `{ format, address }` entry per requested format, in request order):

```json
{
  "activity": {
    "status": "ACTIVITY_STATUS_COMPLETED",
    "type": "ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2",
    "result": {
      "createPrivateKeysResult": {
        "privateKeyIds": ["b3f1…"],
        "addresses": [
          { "format": "ADDRESS_FORMAT_ETHEREUM", "address": "0x1234…" }
        ]
      }
    }
  }
}
```

A bad curve fails with `INVALID_CURVE`; an unknown address format fails with
`INVALID_ADDRESS_FORMAT` — both **before** any key is minted.

## Multi-format keys

A single secp256k1 key can derive many chains at once. Request several formats and
each comes back as its own entry:

```json
{
  "parameters": {
    "name": "multichain",
    "curve": "CURVE_SECP256K1",
    "addressFormats": [
      "ADDRESS_FORMAT_ETHEREUM",
      "ADDRESS_FORMAT_COSMOS",
      "ADDRESS_FORMAT_BITCOIN_MAINNET_P2WPKH"
    ]
  }
}
```

## Curve ↔ address-format compatibility

An address format is only valid for the curve whose public key it derives from. The
families:

### Raw public-key forms (secp256k1 / P-256)

`ADDRESS_FORMAT_UNCOMPRESSED`, `ADDRESS_FORMAT_COMPRESSED` — the raw SEC1 point,
no chain encoding. `COMPRESSED` is the default for `CURVE_P256`.

### secp256k1 chains

EVM, Bitcoin, Cosmos and friends derive from a **secp256k1** key:

| Family | Formats |
| --- | --- |
| Ethereum / EVM | `ADDRESS_FORMAT_ETHEREUM` |
| Cosmos | `ADDRESS_FORMAT_COSMOS` |
| Sei | `ADDRESS_FORMAT_SEI` |
| Tron | `ADDRESS_FORMAT_TRON` |
| Dogecoin | `ADDRESS_FORMAT_DOGE_MAINNET`, `ADDRESS_FORMAT_DOGE_TESTNET` |
| XRP | `ADDRESS_FORMAT_XRP` |
| Spark | `ADDRESS_FORMAT_SPARK_MAINNET`, `ADDRESS_FORMAT_SPARK_REGTEST` |
| Bitcoin | the 20 variants below |

**Bitcoin** covers every network × script-type pair —
{mainnet, testnet, signet, regtest} × {P2PKH, P2SH, P2WPKH, P2WSH, P2TR}:

```
ADDRESS_FORMAT_BITCOIN_MAINNET_P2PKH    ADDRESS_FORMAT_BITCOIN_MAINNET_P2SH
ADDRESS_FORMAT_BITCOIN_MAINNET_P2WPKH   ADDRESS_FORMAT_BITCOIN_MAINNET_P2WSH
ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR
ADDRESS_FORMAT_BITCOIN_TESTNET_P2PKH    ADDRESS_FORMAT_BITCOIN_TESTNET_P2SH
ADDRESS_FORMAT_BITCOIN_TESTNET_P2WPKH   ADDRESS_FORMAT_BITCOIN_TESTNET_P2WSH
ADDRESS_FORMAT_BITCOIN_TESTNET_P2TR
ADDRESS_FORMAT_BITCOIN_SIGNET_P2PKH     ADDRESS_FORMAT_BITCOIN_SIGNET_P2SH
ADDRESS_FORMAT_BITCOIN_SIGNET_P2WPKH    ADDRESS_FORMAT_BITCOIN_SIGNET_P2WSH
ADDRESS_FORMAT_BITCOIN_SIGNET_P2TR
ADDRESS_FORMAT_BITCOIN_REGTEST_P2PKH    ADDRESS_FORMAT_BITCOIN_REGTEST_P2SH
ADDRESS_FORMAT_BITCOIN_REGTEST_P2WPKH   ADDRESS_FORMAT_BITCOIN_REGTEST_P2WSH
ADDRESS_FORMAT_BITCOIN_REGTEST_P2TR
```

### ed25519 chains

Solana and friends derive from an **ed25519** key:

| Chain | Format |
| --- | --- |
| Solana | `ADDRESS_FORMAT_SOLANA` |
| Sui | `ADDRESS_FORMAT_SUI` |
| Aptos | `ADDRESS_FORMAT_APTOS` |
| Stellar | `ADDRESS_FORMAT_XLM` |
| TON | `ADDRESS_FORMAT_TON_V3R2`, `ADDRESS_FORMAT_TON_V4R2`, `ADDRESS_FORMAT_TON_V5R1` |

See the [Reference](/signing/reference) for the verbatim enum lists.

## Wallets & accounts

If you prefer Turnkey's wallet/account model, `create_wallet` and
`create_wallet_accounts` mint HD-style accounts (each its own key) with a per-account
`curve` + `addressFormat`, up to 20 accounts per request. They reuse the same signer
key-minting path as `create_private_keys`.
