# Replace a leaked key

URL: /products/api/replace-a-leaked-key · Id: `guide:replace-a-leaked-key`

> Say "a customer posted their API key in public, replace it" and get a new key with the same scope, while the old one stops within the hour.

Rolling an [API Key](/business/nouns/api-key) issues a replacement with the same name and scope; the old key works for an hour while the new one deploys. The [Customer](/business/nouns/customer)'s admins see the new secret once.

Needs: [Ship an API](/products/api/ship-an-api)

**Before it runs**

- You leave the chat: Never: it all happens in the chat.

## What you get
- 1 new key with the same name and scope, its secret shown once (API Key)
- 1 old key that stops working in one hour (API Key)
- 1 email to the customer’s admins naming the key and when the old one stops (Message)

## Your agent does
**For agents**

- Guide: `guide:replace-a-leaked-key`, status `live`. Page: [/products/api/replace-a-leaked-key.md](/products/api/replace-a-leaked-key.md).
- Approval: none. Run it when the founder asks.
- Leaves the chat: never.
- Needs first: [Ship an API](/products/api/ship-an-api).
- Run: call the steps in order, each through the API, SDK, CLI or MCP tab below; a later step passes the `$id` an earlier one returns. Each value marked example is the sample business’s: pass the founder’s own.

| Step | Verb | Inputs (type, required, this guide’s value) | Returns |
| --- | --- | --- | --- |
| 1 | [`APIKey.roll`](/business/nouns/api-key/roll) (`verb:APIKey.roll`) | `apiKey` APIKey (an id), required: example `"api_key_nightly_sync"`; `expireIn` string: example `"1h"` | `apiKey` APIKey, `secret` string |

Returns: the last call answers with `apiKey`, `secret` and its `action`, whose `status` is `done`, `pending` (waiting on an Approval or a Worker), `failed`, `canceled` or `refused`:

```json
{
  "api": "https://api.sb/leaseline.app",
  "apiKey": {
    "$id": "https://api.sb/leaseline.app/api-keys/api_key_nightly_sync",
    "$type": "APIKey"
  },
  "secret": "sk_live_9c1e…",
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/action_1",
    "operation": "APIKey.roll",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/leaseline.app/actions/action_1"
  }
}
```

Next: offer “Stop the old key now, not in an hour”, or “Show me every key this customer has”.

Calls: `APIKey.roll`

Ask:

```text
A customer posted their API key in public, replace it.
Roll a leaked key.
Kill that key and give them a new one.
```

API:

```bash
# APIKey.roll
curl -X POST https://api.sb/leaseline.app/api-keys/api_key_nightly_sync/roll \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"expireIn":"1h"}'
```

SDK:

```ts
import { $ } from 'startup-builder'

await $.APIKey.roll({ apiKey: 'api_key_nightly_sync', expireIn: '1h' })
```

CLI:

```bash
npx startup-builder do APIKey.roll --json '{"apiKey":"api_key_nightly_sync","expireIn":"1h"}'
```

MCP:

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.APIKey.roll({ apiKey: 'api_key_nightly_sync', expireIn: '1h' })"
  }
}
```

## Change it
- Stop the old key now, not in an hour.
- Show me every key this customer has.
- Let the new key read only.

## Under the hood
- Systems: 
- Nouns: [API Key](/business/nouns/api-key), [Tenant](/business/nouns/tenant), [Customer](/business/nouns/customer)
- Events: [`APIKey.rolled`](/business/events/APIKey.rolled)