---
title: "Rotate keys"
description: "Rotate a Flume partner key with POST /keys. The old key keeps working for an overlap you choose, so you can switch with no downtime."
url: https://docs.tryflume.ai/guides/rotate-keys
updated: 2026-11-11
---

# Rotate keys

> Rotate a Flume partner key with POST /keys. The old key keeps working for an overlap you choose, so you can switch with no downtime.

`POST /keys` issues a new partner key. The key you called with keeps working for `overlap_days`, so you can deploy the new key before the old one stops.

## Before you begin

- A working partner key. Flume issues your first one. See [Authentication and keys](https://docs.tryflume.ai/concepts/authentication.md).

## 1. List your keys

**curl**

```sh
curl -X GET "https://sandbox-api.tryflume.ai/partner/v1/keys" \
  -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"
```

**Python**

```python
import os

import requests

headers = {
    "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"],
}

response = requests.get(
    "https://sandbox-api.tryflume.ai/partner/v1/keys",
    headers=headers,
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Node.js**

```js
const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/keys", {
  method: "GET",
  headers: {
    "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY,
  },
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(await response.json());
```

**Response (200)**

```json
{
  "keys": [
    {
      "key_id": "pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d",
      "prefix": "sk_dev_acme_Qm4t",
      "label": "rotation 2026-Q4",
      "status": "ACTIVE",
      "created_at": "2026-10-16T08:00:00Z",
      "last_used_at": "2026-10-16T09:14:22Z",
      "expires_at": null
    },
    {
      "key_id": "pk_9e1b4d7a2c5f4068b3a1d9c7e5f2a0b4",
      "prefix": "sk_dev_acme_8f3k",
      "label": null,
      "status": "EXPIRING",
      "created_at": "2026-07-02T10:30:00Z",
      "last_used_at": "2026-10-16T07:58:41Z",
      "expires_at": "2026-10-23T08:00:00Z"
    }
  ]
}
```

| `status` | Meaning |
|---|---|
| `ACTIVE` | Working |
| `EXPIRING` | Replaced by a newer key. Works until `expires_at` |
| `REVOKED` | Stopped by you |
| `EXPIRED` | Its overlap ran out |

The list never contains a secret.

## 2. Issue the new key

`Idempotency-Key` is required.

**curl**

```sh
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/keys" \
  -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "rotation 2026-Q4",
  "overlap_days": 7
}'
```

**Python**

```python
import os
import uuid

import requests

headers = {
    "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"],
    "Idempotency-Key": str(uuid.uuid4()),
}

response = requests.post(
    "https://sandbox-api.tryflume.ai/partner/v1/keys",
    headers=headers,
    json={
        "label": "rotation 2026-Q4",
        "overlap_days": 7,
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Node.js**

```js
import { randomUUID } from "node:crypto";

const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/keys", {
  method: "POST",
  headers: {
    "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY,
    "Idempotency-Key": randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "label": "rotation 2026-Q4",
    "overlap_days": 7
  }),
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(await response.json());
```

**Response (201)**

```json
{
  "key_id": "pk_3f9a1c2e5b7d4a6f8e0c1b2d3a4f5e6d",
  "prefix": "sk_dev_acme_Qm4t",
  "label": "rotation 2026-Q4",
  "status": "ACTIVE",
  "created_at": "2026-10-16T08:00:00Z",
  "last_used_at": null,
  "expires_at": null,
  "secret": "sk_dev_acme_Qm4tW…"
}
```

- `secret` is the new key. It is shown once.
- The key you called with becomes `EXPIRING`. It stops after `overlap_days`.
- `overlap_days` is 0 to 30. The default is 7.
- A replay of the same `Idempotency-Key` returns `secret: null`.

## 3. Deploy and revoke

1. Store the new secret in your secret manager.
2. Deploy it everywhere.
3. Check `last_used_at` on the old key with `GET /keys`.
4. Revoke the old key, or let its overlap run out.

**curl**

```sh
curl -X DELETE "https://sandbox-api.tryflume.ai/partner/v1/keys/pk_9e1b4d7a2c5f4068b3a1d9c7e5f2a0b4" \
  -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"
```

**Python**

```python
import os

import requests

headers = {
    "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"],
}

response = requests.delete(
    "https://sandbox-api.tryflume.ai/partner/v1/keys/pk_9e1b4d7a2c5f4068b3a1d9c7e5f2a0b4",
    headers=headers,
    timeout=30,
)
response.raise_for_status()
print(response.status_code)
```

**Node.js**

```js
const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/keys/pk_9e1b4d7a2c5f4068b3a1d9c7e5f2a0b4", {
  method: "DELETE",
  headers: {
    "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY,
  },
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(response.status);
```

**Response (204)**: no body.

A revoke takes effect at once. You cannot revoke your only working key.

## Limits

| | Sandbox | Live |
|---|---|---|
| Active keys | 5 | 2 |
| Keys issued in 24 hours | 10 | 1 |

## If a key leaks

| The leaked key is | Do this |
|---|---|
| Not the one you call with | Call `DELETE /keys/{key_id}` on it. It stops at once |
| The one you call with | Call `POST /keys` with `overlap_days: 0`. The old key stops at once. Deploy the new secret |

Then [tell Flume](https://docs.tryflume.ai/support.md). Send the key prefix, never the key.

> **On live you may not be able to rotate:** Live allows 1 new key in 24 hours, and the key Flume issued you counts. You also cannot revoke your only working key. If you cannot rotate a leaked live key, contact Flume at once and ask Flume to revoke it.

## Handle the errors

| You get | Do this |
|---|---|
| `409` `KEY_LIMIT_REACHED` | Revoke a key you no longer use, then rotate |
| `429` `KEY_ROTATION_RATE_LIMITED` | Wait. Your current key keeps working |
| `409` `LAST_ACTIVE_KEY` | Issue a replacement before you revoke |
| `400` `IDEMPOTENCY_KEY_REQUIRED` on `POST /keys` | Send the `Idempotency-Key` header |
| `422` `VALIDATION_FAILED` on `POST /keys` | Fix the field named in `error.field`, for example `overlap_days` |
| `404` `NOT_FOUND` | Check `key_id` |

## See also

- [Rotate a key](https://docs.tryflume.ai/api/rotate-key.md)
- [Revoke a key](https://docs.tryflume.ai/api/revoke-key.md)
