# Managed API keys

Programmatically create and manage API keys on behalf of your app's users.

An approved Stripe App can programmatically create and manage API keys on behalf of your app’s users. App-managed keys eliminate the need for users to manually copy and paste secret keys from the app settings. They also allow you to manage key rotation and expiration while maintaining clear key ownership and audit trails.

The following table summarizes the differences between user-managed and app-managed keys.

|                  | User-managed RAKs                                           | App-managed RAKs                                                                                                                                             |
| ---------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Key creation     | User clicks **View API keys**                               | App creates keys using the API                                                                                                                               |
| User interaction | User copies keys from settings and pastes them into the app | No manual key handling                                                                                                                                       |
| Key delivery     | Returned to user in plaintext in a UI dialog                | Returned directly to app as ciphertext                                                                                                                       |
| Key management   | User owns and manages keys                                  | App manages keys; user can choose to keep or expire keys on uninstall. User can expire managed keys immediately without uninstall from the Stripe Dashboard. |

## Common use cases for app-managed keys

Use app-managed API keys if your app:

- Works within a [claimable sandbox](https://docs.stripe.com/sandboxes/claimable-sandboxes.md) workflow where you provision accounts on behalf of users.
- Serves non-technical users who might not be familiar with handling Stripe API keys.
- Handles key lifecycle changes, such as rotation and expiration, without user intervention.

## Prerequisites

To use app-managed API keys, you must add the `api_key_write` permission to your [Stripe App manifest](https://docs.stripe.com/stripe-apps/reference/app-manifest.md). This permission requires explicit approval, so you must also submit your app to Stripe for review even if earlier versions were approved.

The standard app review process can take several days, but Stripe can expedite it for early access partners.

After we approve your app with this permission, it has access to the [API Keys](https://docs.stripe.com/api/v2/api-keys.md?api-version=preview) endpoints.

## Installation requirements

Users must install your app using the standard Stripe App installation flow. App-managed keys can only be created after:

- The user has authorized your app.
- Your app has been granted the `api_key_write` permission.
- The user’s account is activated (for live mode keys).

## Usage

Authenticate your API requests using an API key of your app. You can use either an unrestricted key or a restricted key that has the `api_key_write` Connect permission.

Create an API key on behalf of the user by calling an [API Keys](https://docs.stripe.com/api/v2/api-keys.md?api-version=preview) endpoint and including their `Account` ID in the `Stripe-Context` header.

> #### Preview restriction
> 
> The current API Keys preview only supports creating unrestricted secret keys. A future version will add support for creating restricted API keys (RAKs). The general release won’t support unrestricted keys.

For live mode secret keys, key tokens are encrypted and you must provide an RSA public key in PEM format. Tokens are encrypted using JSON Web Encryption (JWE) with RSA-OAEP and AES-256-GCM.

1. Generate an RSA key pair.

```node
const crypto = require('crypto');

// Generate RSA key pair (minimum 2048 bits)
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
  modulusLength: 2048,
  publicKeyEncoding: {
    type: "spki",
    format: "pem",
  },
  privateKeyEncoding: {
    type: "pkcs8",
    format: "pem",
  },
});

// Store privateKey securely (for example, in a secrets manager)
// Use publicKey in the API requests.
```

1. Create a secret key for a user’s account. Provide your public key to Stripe for encrypting the secret token.

```curl
curl -X POST https://api.stripe.com/v2/iam/api_keys \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-04-22.preview" \
  -H "Stripe-Context: {{CONTEXT_ID}}" \
  --json '{
    "type": "secret_key",
    "name": "Integration key for MyApp",
    "note": "This key is used for Stripe integrations with MyApp",
    "public_key": {
        "pem_key": {
            "data": "{{PUBLIC_KEY_PEM}}",
            "algorithm": "RSA"
        }
    }
  }'
```

1. Decrypt the response payload using your private key.

```node
const jose = require('jose');

const secretKeyCiphertext = secretKey.secret_key.encrypted_secret.ciphertext;

const importedPrivateKey = await jose.importPKCS8(privateKey, 'RSA-OAEP-256');
const { plaintext } = await jose.compactDecrypt(secretKeyCiphertext, importedPrivateKey);
const secretKeyToken = new TextDecoder().decode(plaintext);
// secretKeyToken is now ready to use (e.g., "sk_live_...")
```

1. Create a publishable key for a user’s account. Encryption isn’t supported for publishable keys, and tokens are returned in plaintext.

```curl
curl -X POST https://api.stripe.com/v2/iam/api_keys \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-04-22.preview" \
  -H "Stripe-Context: {{CONTEXT_ID}}" \
  --json '{
    "type": "publishable_key"
  }'
```

Access the token from the response:

```node
const publishableKeyToken = publishableKey.publishable_key.token;
```

## Listen to API key events

Use [Connect webhooks](https://docs.stripe.com/connect/webhooks.md#connect-webhooks) to receive notifications about API key lifecycle changes for your users’ accounts. This allows your app to respond to key events such as rotation or expiration. Because these events occur on connected accounts rather than your platform account, you must use Connect webhooks—not Account webhooks—to receive them.

Configure your Connect webhook endpoint to listen for the following event types:

| Event type                                                                                                                                               | Description                        |
| -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| [v2.iam.api_key.created](https://docs.stripe.com/api/v2/core/events/event-types.md?api-version=2025-11-17.preview#v2_event_types-v2.iam.api_key.created) | Occurs when an API key is created. |
| [v2.iam.api_key.updated](https://docs.stripe.com/api/v2/core/events/event-types.md?api-version=2025-11-17.preview#v2_event_types-v2.iam.api_key.updated) | Occurs when an API key is updated. |
| [v2.iam.api_key.rotated](https://docs.stripe.com/api/v2/core/events/event-types.md?api-version=2025-11-17.preview#v2_event_types-v2.iam.api_key.rotated) | Occurs when an API key is rotated. |
| [v2.iam.api_key.expired](https://docs.stripe.com/api/v2/core/events/event-types.md?api-version=2025-11-17.preview#v2_event_types-v2.iam.api_key.expired) | Occurs when an API key is expired. |

To set up a Connect webhook endpoint that receives events for your users’ accounts, see [Connect webhooks](https://docs.stripe.com/connect/webhooks.md#connect-webhooks).
