# Create claimable sandboxes for your app-building platform's users

Use the Claimable Sandboxes API to set up Stripe for users of your app-building platform.

This guide is for app-building and coding platforms that use the Claimable Sandboxes API to create sandboxes for their users. To get a claimable sandbox for your own integration, run `stripe sandboxes create` in the [Stripe CLI](https://docs.stripe.com/cli/sandbox) instead.

> Claimable sandboxes are in private preview and not accepting new users.

Claimable sandboxes are anonymous [sandboxes](https://docs.stripe.com/sandboxes.md) that aren’t initially owned by an existing account. You can programmatically create claimable sandboxes for your users with the Claimable Sandboxes API and populate each sandbox with data to help your users get started.

When your users are ready to take ownership of a sandbox and make projects live, they can claim the sandbox by visiting its onboarding URL. On that page, they can create a new Stripe account or choose an existing one to move the sandbox into.

## Build a claimable sandbox integration

In this guide, you’ll integrate the Claimable Sandboxes API into a fictional agent-assisted coding platform called Bizbot and work with the following entities:

- Bizbot: An agent-assisted coding platform
- `bizbot`: The Stripe App that uses the Claimable Sandboxes API
- Jenny Rosen: An end user of Bizbot

## Create a Stripe App

To use the Claimable Sandboxes API to create claimable sandboxes, you must first create a *Stripe App* (An app that you can build on top of Stripe to customize the functionality of the Stripe Dashboard UI, leverage Stripe user data, store data on Stripe, and more). This lets your platform act on the sandbox on behalf of your end user and access sandbox data.

Stripe Apps doesn’t support public distribution on Connect platforms, so claimable sandboxes aren’t available on Connect platforms.

[Install the Stripe CLI](https://docs.stripe.com/stripe-cli.md) v1.52.0 or later first. Then install the [Stripe Apps CLI plugin](https://docs.stripe.com/stripe-apps/create-app.md#install-stripe-apps-cli). The [Generate CLI plugin](https://docs.stripe.com/stripe-apps/create-app.md#install-generate-cli) creates your app’s initial project and configuration. Use `--shape claimable-sandboxes` to configure it for a claimable-sandbox integration.

```bash
# Install the Stripe Apps CLI plugin (v1.19.0 or later)
stripe plugin install apps

# Create your app
# This guide uses `bizbot` as an example.
# Make sure you use a unique ID for your app.
stripe generate app bizbot --shape claimable-sandboxes
```

Claimable sandboxes require a v2 [manifest](https://docs.stripe.com/stripe-apps/reference/app-manifest.md) with these settings. If you use a v1 manifest, run `stripe apps migrate` first:

```yaml
shapes:
  - claimable-sandboxes
declarations:
    distribution_type: public
    stripe_api_access:
        permissions:
            - permission: event_read
              purpose: Read lifecycle and authorization events for Bizbot users.
    stripe_api_access_type: platform
    sandbox_install_compatible: true
```

Your app can request the additional [permissions](https://docs.stripe.com/stripe-apps/reference/permissions.md) that it needs to operate on the sandbox. Request only the permissions required for the functionality that your app provides. For example, showing a list of subscriptions requires the `subscription_read` permission.

After you configure your manifest, authenticate the Stripe CLI to your live mode account and [upload your app](https://docs.stripe.com/stripe-apps/upload-install-app.md) from your project root directory. When `stripe login` opens the browser, select your live account, not a sandbox:

```bash
stripe login
stripe apps upload
```

Live account uploads let users install your app on their live accounts later.

After you upload the app, Stripe automatically creates a [managed app sandbox](https://docs.stripe.com/stripe-apps/enable-sandbox-support.md#managed-sandbox) with the same name as your app ID. Use this sandbox’s secret API key, not your live account’s key, to create claimable sandboxes:

1. In the Dashboard account picker, select **Switch to sandbox** > **Manage sandboxes** and open the sandbox named after your app ID.
2. In that sandbox, [open the API keys page](https://docs.stripe.com/sandboxes/dashboard/manage-access.md#manage-api-keys) and reveal the secret key.
3. Store the key in a server-side environment variable or secrets manager. Use it as `STRIPE_APP_SANDBOX_SECRET_KEY` in this guide. Don’t embed secret keys in code. See [best practices for managing secret keys](https://docs.stripe.com/keys-best-practices.md).

This key authenticates your platform’s requests. It’s separate from the keys returned when you create a claimable sandbox for a user. The following diagram shows the account structure.

This diagram shows a live mode account of the Bizbot account with the managed app sandbox as a child node. (See full diagram at https://docs.stripe.com/sandboxes/claimable-sandboxes)

```text
[acct_bizbot_livemodeLive account] --> [acct_bizbot_sandboxManaged app sandbox]
```

After you upload the app, Stripe reviews your request. Based on your business use case, we might grant access to the feature or contact you for more information. Proceed to the next steps only after Stripe grants access.

## Set up external testing channel for app

Before you begin development, set up an [external testing channel](https://docs.stripe.com/stripe-apps/test-app.md#set-up-test) for your app. This lets you test the API without first publishing your app.

Before you launch your integration to users, you must [submit your app for review and publish it](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#review-and-publish).

## Call the Claimable Sandboxes API

Use the secret key from your managed app sandbox account to create claimable sandboxes.

```curl
curl -X POST https://api.stripe.com/v2/core/claimable_sandboxes \
  -H "Authorization: Bearer {{STRIPE_APP_SANDBOX_SECRET_KEY}}" \
  -H "Stripe-Version: 2026-04-22.preview" \
  --json '{
    "app_channel": "testing",
    "enable_mcp_access": true,
    "onboarding_link_details": {
        "refresh_url": "https://example.com/refresh"
    },
    "prefill": {
        "email": "jenny.rosen@example.com",
        "name": "Jenny Sandbox",
        "country": "US"
    }
  }'
```

Use these parameters when you create a claimable sandbox.

| Parameter | Description |
| --- | --- |
| `app_channel` | Selects the Stripe App channel used to install your app in the sandbox. Use `testing` during development, as this guide does. Before you launch to users, [submit your app for review and publish it](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#review-and-publish), then use `public`. |
| `enable_mcp_access` | Set this parameter to `true` to receive an API key for the [Stripe MCP](https://docs.stripe.com/mcp.md) server. This lets a large language model (LLM) securely operate on your users’ Stripe accounts. |
| `onboarding_link_details` | A required hash containing `refresh_url`, the URL your user is redirected to if they open an expired onboarding link. Your server must [renew the onboarding link](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#onboarding-links), then redirect your user to the new URL. |
| `prefill` | A hash that lets you prefill values that your user provides when claiming the sandbox. For example, you can set `prefill.name` to your user’s project name. |

Here’s what the API response looks like after you create a claimable sandbox:

```json
{
   "id": "clmsbx_test_123",
   "object": "v2.core.claimable_sandbox",
   "app_channel": "testing",
   "claimed_at": null,
   "created": "2026-04-22T00:00:00.000Z",
   "expires_at": "2026-06-21T00:00:00.000Z",
   "onboarding_link_details": {
      "expires_at": "2026-04-29T00:00:00.000Z",
      "refresh_url": "https://example.com/refresh",
      "url": "https://dashboard.stripe.com/onboard_sandbox/{{SIGNATURE}}"
   },
   "owner_details": null,
   "prefill": {
      "email": "jenny.rosen@example.com",
      "name": "Jenny Sandbox",
      "country": "US"
   },
   "sandbox_details": {
      "account": "acct_jenny_sandbox",
      "api_keys": {
         "publishable": "pk_test_123......",
         "secret": "sk_test_123.....",
         "mcp": "ek_test_123....."
      }
   },
   "status": "unclaimed"
}
```

### Availability

When you call the API, specify the country where Stripe should create the sandbox by passing `prefill.country`. This API supports claimable sandbox creation in these regions:

### Country availability

- AE
- AR
- AT
- AU
- BE
- BG
- BR
- CA
- CH
- CI
- CR
- CY
- CZ
- DE
- DK
- DO
- EE
- ES
- FI
- FR
- GB
- GI
- GR
- GT
- HK
- HR
- HU
- ID
- IE
- IN
- IT
- JP
- KR
- LI
- LT
- LU
- LV
- MT
- MX
- MY
- NL
- NO
- NZ
- PE
- PH
- PL
- PT
- RO
- RS
- SE
- SG
- SI
- SK
- SN
- TH
- TT
- US
- UY

### Sandbox and onboarding link expiration

A claimable sandbox expires after 60 days. Find the exact expiration timestamp in the top-level `expires_at` property. Seven days before expiration, Stripe sends an email to the address in `prefill.email` to remind your users to claim the sandbox before Stripe deletes it. If a user doesn’t claim the sandbox within 60 days, Stripe permanently deletes it.

The onboarding link expires after seven days. [Renew expired onboarding links](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#onboarding-links) so your user can continue claiming the sandbox, onboarding, or installing your app.

### Use API keys and authentication

The keys and authentication method that you use depend on the entity performing the action:

- LLM agents that act on behalf of the user must use [MCP tools](https://docs.stripe.com/mcp.md#tools) with the MCP key.
- Deterministic code in your platform, for example code that retrieves payment data to build a dashboard that lists payments, must [call APIs](https://docs.stripe.com/stripe-apps/build-backend.md#using-stripe-apis) on the sandbox using [Stripe Apps authentication](https://docs.stripe.com/stripe-apps/build-backend.md#authenticate-requests). Stripe limits access to the permissions that you originally requested in your manifest.
- The API response returns the `publishable` and `secret` keys for your platform to populate the end user’s application, for example through an `.env` file. When an end user of a generated application creates a payment, those API calls use the `secret` and `publishable` keys.

Save the API keys from the create response because Stripe doesn’t return them in subsequent retrieve requests.

> This MCP key expires seven days after a user claims the sandbox. Follow the [MCP authorization specification](https://modelcontextprotocol.io/specification/draft/basic/authorization) to make sure your platform can handle expired keys.

When you create a new claimable sandbox, Stripe automatically pre-installs your platform’s Stripe App in that sandbox. This creates a connection between your platform and the sandbox and lets you perform actions on behalf of your users in the sandbox. The account structure now looks like this:

This diagram shows that the managed app sandbox of Bizbot has a connection to the unclaimed sandbox for Jenny Rosen, which is created via the preinstalled Stripe App (See full diagram at https://docs.stripe.com/sandboxes/claimable-sandboxes)

```text
[acct_bizbot_livemodeLive account] --> [acct_bizbot_sandboxManaged app sandbox]
[acct_bizbot_sandboxManaged app sandbox] -- Sandbox app installed --> [acct_jenny_sandboxUnclaimed sandbox]
```

In the Bizbot example, because the Bizbot app is preinstalled in the sandbox account `acct_jenny_sandbox`, the Bizbot managed app sandbox account `acct_bizbot_sandbox` can now call APIs and listen for events from `acct_jenny_sandbox`. See [application permissions](https://docs.stripe.com/stripe-apps/how-stripe-apps-work.md#application-permissions)

## Use onboarding links

The `onboarding_link_details.url` property contains the claimable sandbox onboarding link. It directs your user to their next required step in the Dashboard:

- **Claim the sandbox**: If the sandbox is unclaimed, the link opens the claim flow.
- **Onboard the account**: If your user claimed the sandbox into a new account but hasn’t completed onboarding, the link opens the account application.
- **Install your app**: If your app isn’t installed on the owner account, the link opens the live mode installation flow.

The link expires after seven days. Check `onboarding_link_details.expires_at` for its exact expiration time. If your user opens an expired link, Stripe redirects them to `onboarding_link_details.refresh_url`.

At that URL, your server must call the renew onboarding link API. When your server receives the new URL, redirect your user to it. The renewed link expires after another seven days.

You can renew the link only while the claimable sandbox’s `status` is `unclaimed` or `claimed`.

## Claim the sandbox

When your user is ready to claim the sandbox, your platform must provide the onboarding URL so the user can claim it in the Dashboard. If the user already has a Stripe account, they can claim the sandbox into that existing Stripe account. If the user doesn’t have a Stripe account, Stripe prompts them to register a new one and then moves the sandbox into it.

After the user claims the sandbox, your platform receives the v2.core.claimable_sandbox.claimed event. Stripe populates `owner_details` on the claimable sandbox object. The value of `owner_details.app_install_status` depends on whether the user claimed the sandbox into an existing Stripe account or a new one.

For example, if the user claims the sandbox into a new Stripe account, `app_install_status` is `pending_onboarding`.

```json
{
   "id": "clmsbx_test_123",
   "object": "v2.core.claimable_sandbox",
   "app_channel": "testing",
   "claimed_at": "2026-04-23T00:00:00.000Z",
   "created": "2026-04-22T00:00:00.000Z",
   "expires_at": null,
   "onboarding_link_details": {
      "expires_at": "2026-04-29T00:00:00.000Z",
      "refresh_url": "https://example.com/refresh",
      "url": "https://dashboard.stripe.com/onboard_sandbox/{{SIGNATURE}}"
   },
   "owner_details": {
      "account": null,
      "app_install_status": "pending_onboarding"
   },
   "prefill": {
      "email": "jenny.rosen@example.com",
      "country": "US"
   },
   "sandbox_details": {
      "account": "acct_jenny_sandbox",
      "api_keys": null
   },
   "status": "claimed"
}
```

The `app_install_status` field shows the state of the owner account, which is the live account that claimed the sandbox. This field is useful for monitoring the claimable sandbox lifecycle. See the possible enum values.

In this scenario, the updated account structure looks like this after the claim:

This diagram shows that the managed app sandbox of Bizbot has a connection to the claimed sandbox for Jenny Rosen (See full diagram at https://docs.stripe.com/sandboxes/claimable-sandboxes)

```text
[acct_bizbot_livemodeLive account] --> [acct_bizbot_sandboxManaged app sandbox]
[acct_bizbot_sandboxManaged app sandbox] -- Sandbox app installed --> [acct_jenny_sandboxClaimed sandbox]
[acct_jenny_livemodeOwner account (not yet onboarded)] --> [acct_jenny_sandboxClaimed sandbox]
```

## User submits the account application

If your user claims the sandbox into a new Stripe account, the user must onboard the Stripe account by submitting the account application before going live. After the user claims the sandbox, accessing the onboarding link again redirects the user to the account application in the Dashboard.

After your user successfully submits the account application, your platform receives the v2.core.claimable_sandbox.updated event, which shows that `owner_details.app_install_status` has updated to `pending_install`.

The updated account structure looks like this after onboarding:

This diagram shows that the managed app sandbox of Bizbot has a connection to the claimed sandbox for Jenny Rosen, and the live account is onboarded (See full diagram at https://docs.stripe.com/sandboxes/claimable-sandboxes)

```text
[acct_bizbot_livemodeLive account] --> [acct_bizbot_sandboxManaged app sandbox]
[acct_bizbot_sandboxManaged app sandbox] -- Sandbox app installed --> [acct_jenny_sandboxClaimed sandbox]
[acct_jenny_livemodeOwner account (onboarded)] --> [acct_jenny_sandboxClaimed sandbox]
```

## User installs your Stripe app into the live owner account

After your user submits the account application, Stripe prompts the user to install your platform’s Stripe App in the live account. After installation, your platform receives the v2.core.claimable_sandbox.updated event, which shows that `owner_details.app_install_status` has updated to `installed`. Stripe also populates `owner_details.account` with the owner account ID.

After installation, the sandbox’s `status` changes to `live`. This indicates that the claimable sandbox object has reached the end of its lifecycle and won’t change again. At this stage, your claimable sandbox integration is complete and ready for production.

```json
{
   "id": "clmsbx_test_123",
   "object": "v2.core.claimable_sandbox",
   "app_channel": "testing",
   "claimed_at": "2026-04-23T00:00:00.000Z",
   "created": "2026-04-22T00:00:00.000Z",
   "expires_at": null,
   "onboarding_link_details": {
      "expires_at": "2026-04-29T00:00:00.000Z",
      "refresh_url": "https://example.com/refresh",
      "url": "https://dashboard.stripe.com/onboard_sandbox/{{SIGNATURE}}"
   },
   "owner_details": {
      "account": "acct_jenny_livemode",
      "app_install_status": "installed"
   },
   "prefill": {
      "email": "jenny.rosen@example.com",
      "country": "US"
   },
   "sandbox_details": {
      "account": "acct_jenny_sandbox",
      "api_keys": null
   },
   "status": "live"
}
```

The following diagram shows the final account structure.

This shows the relationship between the 4 different Stripe accounts. The live mode Bizbot account can access Jenny's live mode account. The sandbox app can access Jenny's sandbox. (See full diagram at https://docs.stripe.com/sandboxes/claimable-sandboxes)

```text
[acct_bizbot_livemodeLive account] --> [acct_bizbot_sandboxManaged app sandbox]
[acct_bizbot_sandboxManaged app sandbox] -- Sandbox app installed --> [acct_jenny_sandboxClaimed sandbox]
[acct_bizbot_livemodeLive account] -- Live mode app installed --> [acct_jenny_livemodeOwner account (onboarded)]
[acct_jenny_livemodeOwner account (onboarded)] --> [acct_jenny_sandboxClaimed sandbox]
```

After installation, your platform can perform actions on both the sandbox and the live owner account on behalf of your end user. The types of actions depend on the permissions you’ve configured on your Stripe app.

In the Bizbot example, after the Bizbot app is installed in the owner account, the Bizbot live account `acct_bizbot_livemode` can call APIs and listen for events from the live account `acct_jenny_livemode`. This is similar to how the Bizbot managed app sandbox account `acct_bizbot_sandbox` can access the sandbox account `acct_jenny_sandbox`. See [application permissions](https://docs.stripe.com/stripe-apps/how-stripe-apps-work.md#application-permissions)

## Submit app for review and publish

Follow these steps before you take your integration live:

1. **Submit your app for review**: [Submit your app](https://docs.stripe.com/stripe-apps/publish-app.md) to Stripe for review. In addition to meeting the usual [app listing requirements](https://docs.stripe.com/stripe-apps/publish-app.md#write-your-listing), consider the following listing requirements for claimable sandbox apps:

   - Provide test credentials in your app listing for a testing account on your platform. Enable the claimable sandbox integration on the testing account, so the app reviewer can sign in to your platform and test the feature from your user’s perspective.
   - Load sufficient tokens or credits into the testing account, if applicable.
   - In the testing guidelines, provide a prompt that the app reviewers can use to trigger the claimable sandbox integration. For example, “Create a full stack web app for my plant shop and add Stripe checkout to accept payments”.

2. **Publish your app**: After Stripe approves your app, [publish your app](https://docs.stripe.com/stripe-apps/publish-app.md#app-review-decision-and-publication).

3. **Update your API integration**: Update your Claimable Sandboxes API integration to use `app_channel: public` instead of `app_channel: testing`.

Use `app_channel: testing` only for testing. It allows at most 25 live mode installations. Don’t launch your integration with the testing channel.

## Optional: Test a new app version

To test a new version of your Stripe App before you publish it, follow these steps:

1. **Update the test version of your app**: To test a new version, [update the test version](https://docs.stripe.com/stripe-apps/test-app.md#update-test-version).
2. **Create a claimable sandbox with `app_channel: testing`**: Use the [Claimable Sandboxes API](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#api) and pass the `app_channel: testing` parameter. This creates a claimable sandbox with the new version of your app preinstalled.

After you verify that the new version works as expected, follow [these steps](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#review-and-publish) to submit for approval and publish before you launch it to your end users. Use `app_channel: public` when you create claimable sandboxes in production.

## See also

- [Create a Stripe App](https://docs.stripe.com/stripe-apps/create-app.md)
- [App manifest reference](https://docs.stripe.com/stripe-apps/reference/app-manifest.md)
- [Stripe MCP](https://docs.stripe.com/mcp.md)
