# Import and generate keys

Keys are the core resource in the vault. Each key has a scheme, a venue, an environment, one or more versions, and optionally a metadata string. All key material stays inside the vault's MPC cluster; you receive only public keys and signatures. See the full [Keys API reference](/api-reference#tag/keys) for every operation, and the [Security Groups reference](/api-reference#tag/securitygroups) for managing which keys belong to which groups.

## List and filter keys

See the [list keys endpoint](/api-reference#tag/keys) in the reference for the full parameter spec.

```
GET /v1/keys
```

Query parameters (all optional):

| Parameter | Type | Description |
|---|---|---|
| `cursor` | string | Opaque cursor for keyset pagination |
| `limit` | integer (1-100) | Page size |
| `key_ids` | string | Comma-separated key IDs to include |
| `venues` | string | Comma-separated venue IDs to filter by |
| `environments` | string | Comma-separated environment strings to filter by |
| `is_frozen` | boolean | `true` returns only frozen keys; `false` returns only active keys |
| `display_name_like` | string | Case-insensitive substring match on display name |

Response:

```json
{
  "items": [ /* Key objects */ ],
  "total": 42,
  "next_cursor": "<opaque string or null>"
}
```

Example: fetch the first 20 keys in the `production` environment:

```bash
curl -s \
  -H "Authorization: ApiKey $API_KEY_UUID" \
  "$CV_BASE/v1/keys?environments=production&limit=20"
```

This endpoint requires only the `Authorization` header (no nonce, timestamp, or assertion).

## Get a single key

[Fetch a single key](/api-reference#tag/keys) to retrieve its full object including all versions, security groups, and freeze state.

```
GET /v1/keys/{key_id}
```

```bash
curl -s \
  -H "Authorization: ApiKey $API_KEY_UUID" \
  "$CV_BASE/v1/keys/my-key-id"
```

Response:

```json
{
  "id": "my-key-id",
  "display_name": "My signing key",
  "scheme": "ed25519",
  "venue": "binance",
  "environment": "production",
  "is_frozen": false,
  "freeze_reason": null,
  "metadata": null,
  "latest_version": 1,
  "versions": [
    {
      "version": 1,
      "created_at": "2026-01-10T12:00:00Z",
      "created_by": "my-trading-bot",
      "expiration_date": "never",
      "key_descriptor": {
        "scheme": "Ed25519",
        "public_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
      },
      "venue_api_key": null
    }
  ],
  "security_groups": [
    { "id": "trading-keys", "display_name": "Trading keys", "signing_policy": "default-policy" }
  ],
  "created_at": "2026-01-10T12:00:00Z",
  "created_by": "my-trading-bot",
  "last_updated_at": "2026-01-10T12:00:00Z",
  "last_updated_by": "my-trading-bot"
}
```

## Generate a key

[Generate a key](/api-reference#tag/keys) by calling the create endpoint. This endpoint is asynchronous. It returns either `200` with the key object or `202` with a `request_id` to poll.

```
POST /v1/keys
```

Request body:

```json
{
  "id": "my-new-key",
  "display_name": "My new signing key",
  "scheme": "ed25519",
  "venue": "my-venue-id",
  "environment": "production",
  "expiration_date": "never",
  "metadata": null
}
```

The `scheme` field accepts: `ed25519`, `hmac-sha256`, `hmac-sha384`, `hmac-sha512`, `rs256`.

The `id` is caller-supplied and permanent. Choose an ID that is stable and meaningful in your system.

The `expiration_date` field is a free-form string: `"unset"`, `"never"`, or a date in `YYYY-MM-DD` format.

Example curl call (requires all four auth headers; see the [authentication guide](/develop/authentication/machine-users) for how to generate them):

```bash
curl -s -X POST "$CV_BASE/v1/keys" \
  -H "Authorization: ApiKey $API_KEY_UUID" \
  -H "X-Request-Timestamp: $TS_HEX" \
  -H "X-Request-Nonce: $NONCE_HEX" \
  -H "X-Request-Assertion: $ASSERTION" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-new-key",
    "display_name": "My new signing key",
    "scheme": "ed25519",
    "venue": "my-venue-id",
    "environment": "production",
    "expiration_date": "never"
  }'
```

If the response is `202`, [poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` until the status is no longer `"processing"`.

## Importing an HMAC key (exchange API secret)

Exchange API secrets (HMAC keys) are imported through the vault web UI, not the server-side API. The secret is split into shares in your browser using Sodot's HMAC key-sharing SDK and the encrypted shares are sent directly to the key store nodes. The raw secret never reaches the orchestrator or your server.

This is by design: zero-exposure import means the secret is never in a place where server-side code, logs, or proxies could capture it. If your integration needs to programmatically register an HMAC key, use the UI flow and record the key ID for subsequent API calls.

## Update key metadata

[Update key metadata](/api-reference#tag/keys) with merge-patch semantics: only fields present in the body are updated; omitted fields are left unchanged.

```
PATCH /v1/keys/{key_id}
```

```json
{
  "display_name": "Updated display name",
  "environment": "staging",
  "metadata": "optional free-form string or null to clear",
  "versions": [
    {
      "version": "Latest",
      "venue_api_key": "new-venue-api-key-value",
      "expiration_date": "2027-12-31"
    }
  ]
}
```

The `versions` array applies per-version updates. Each entry targets a version by number or by the string `"Latest"`. Omit `versions` (or send an empty array) to leave all versions unchanged.

## Rotate a key (create a new version)

[Rotate a key](/api-reference#tag/keys) to create a new version. The existing versions remain valid and can still be targeted by signing requests using `"Specific": <version_number>`. New signing requests default to `"Latest"`.

```
POST /v1/keys/{key_id}/versions
```

This endpoint is asynchronous. [Poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` if you receive `202`.

```bash
curl -s -X POST "$CV_BASE/v1/keys/my-key-id/versions" \
  -H "Authorization: ApiKey $API_KEY_UUID" \
  -H "X-Request-Timestamp: $TS_HEX" \
  -H "X-Request-Nonce: $NONCE_HEX" \
  -H "X-Request-Assertion: $ASSERTION" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Delete a key

[Delete a key](/api-reference#tag/keys) to permanently remove it and all its versions. This is irreversible. The `delete_keys` capability must be granted in the Administrative Policy.

```
DELETE /v1/keys/{key_id}
```

To delete multiple keys in one request, send a JSON body to `DELETE /v1/keys` listing each key and which of its versions to remove. The `versions` selector is either `"All"` or `{ "Specific": [<version numbers>] }`. This endpoint takes no query parameters.

```json
{
  "keys": [
    { "key_id": "my-binance-key", "versions": "All" },
    { "key_id": "my-bybit-key", "versions": { "Specific": [0, 1] } }
  ]
}
```

See the full [Keys API reference](/api-reference#tag/keys) for the request body schema.
