# Create claimable sandboxes Create anonymous, claimable sandboxes for your users to build Stripe integrations. 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-app`: 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 allows your platform to 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. To get started, install the required [Stripe CLI plugins](https://docs.stripe.com/stripe-apps/create-app.md#install-stripe-apps-cli) and create your app: ```bash # Install the Stripe Apps CLI plugin (v1.16.21 or later) stripe plugin install apps # Install the Stripe generate CLI plugin stripe plugin install generate # Create your app stripe generate app bizbot-app --shape claimable-sandboxes ``` These are the default settings in your claimable sandbox app [manifest](https://docs.stripe.com/stripe-apps/reference/app-manifest.md) file. ```yaml declarations: distribution_type: public stripe_api_access: permissions: - permission: event_read purpose: Allows Bizbot access to read events. 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, [upload your app](https://docs.stripe.com/stripe-apps/upload-install-app.md) to your account by running this command from your project root directory: ```bash stripe apps upload ``` 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. In the Bizbot example, the app ID might be `bizbot-app`. 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) 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, make sure that you 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 Sandbox 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@stripe.com", "name": "Jenny Sandbox", "country": "US" } }' ``` Use these parameters when you create a claimable sandbox. | Parameter | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `app_channel` | An enum with the values `public` or `testing`. When in development, use `testing`. When you launch this feature to your users, use `public`. For details, see [Submit app for review and publish](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#review-and-publish). | | `enable_mcp_access` | Set this parameter to `true` to prompt Stripe to send you an API key that you can use to communicate with the [Stripe MCP](https://docs.stripe.com/mcp.md) server. This allows a large language model (LLM) to securely operate on your users’ Stripe accounts. | | `onboarding_link_details` | A hash that lets you specify the `refresh_url` that your end user is redirected to if they access an expired onboarding link. This parameter is required when you create a new claimable sandbox. The `refresh_url` must call a method on your server that invokes the Claimable Sandboxes Renew Onboarding Link API to renew the onboarding link expiration. | | `prefill` | A hash that lets you prefill values that your user provides when claiming the sandbox. For example, you can set `name` to your user’s project name. | > In this development guide, we call the API with `app_channel: testing`. Before taking this feature live, you must update to use `app_channel: public`. For details, see [Submit app for review and publish](https://docs.stripe.com/sandboxes/claimable-sandboxes.md#review-and-publish). 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@stripe.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" } ``` ### Sandbox and onboarding link expiry A claimable sandbox expires after 60 days. Find the exact expiration timestamp in the top-level `expires_at` field. 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 is valid for seven days. You can see the exact expiration timestamp in the `onboarding_link_details.expires_at` field. If a user accesses an expired onboarding link, Stripe redirects the user to the URL in `onboarding_link_details.refresh_url`. Your `refresh_url` must call a method on your server that invokes the Claimable Sandboxes Renew Onboarding Link API, which renews the link for another seven days. ### 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) 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 [How Stripe Apps work](https://docs.stripe.com/stripe-apps/how-stripe-apps-work.md) for details. ## 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. The `owner_details` hash is populated 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@stripe.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) ## 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 the `app_install_status` field 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) ## 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 the `app_install_status` field has updated to `installed`. The `owner_details.account` field is also populated with the ID of the owner account. After installation, the sandbox `status` field 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@stripe.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) 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`. To learn more, see [How Stripe Apps work](https://docs.stripe.com/stripe-apps/how-stripe-apps-work.md). ## Use onboarding links The onboarding link for a claimable sandbox is available in the `onboarding_link_details.url` field. This stateful link redirects your user to the next action required to complete the claimable sandbox lifecycle. - If the claimable sandbox is unclaimed, the link redirects your user to the Dashboard to claim it. - If the sandbox owner account isn’t onboarded yet, the link redirects your user to the account application in the Dashboard, as long as the user has access to the claimable sandbox. - If your platform’s Stripe App isn’t installed in the owner account, the link redirects your user to a Dashboard page that explains that the app isn’t installed and includes a link to install it, as long as the user has access to the claimable sandbox. The onboarding link is active for seven days. Find the expiration date in `onboarding_link_details.expires_at`. When the onboarding link expires, accessing the link redirects your user to the URL set in `onboarding_link_details.refresh_url`. ## 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. Provide credentials in your app listing for a testing account on your platform that’s flagged into the feature. This access lets the app reviewer sign in to your platform and test the claimable sandbox feature from your user’s perspective. 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 Sandbox API integration to use `app_channel: public` instead of `app_channel: testing`. > Use `app_channel: testing` only for testing. This channel has restrictions. For example, you can make at most 25 live mode installs on the testing channel. Don’t take your integration live 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)