# An instant backend, before anyone signs up

An agent can make a Flatstack backend for someone who has no account yet: a sandbox. It is a real project, with a D1 database, functions, sign-in for the app's users, file storage and hosting at a `flatstack.app` address, and it lasts 24 hours. The person you're building for keeps it by opening its claim link: they sign up and pay, or sign in to an account they already have. Nothing is lost when they do. A sandbox nobody claims is deleted.

This isn't a free tier. A sandbox is small, it never sends email, and only one person can ever keep it, by paying for a plan or adding it to the one they have.

## Making one

Over MCP, connect to the sandbox endpoint. It needs no sign-in:

```bash
claude mcp add --transport http flatstack-sandbox https://api.flatstack.dev/mcp/sandbox
```

Then call `create_sandbox`. Over HTTP it's one unauthenticated request:

```bash
curl -X POST https://api.flatstack.dev/v1/sandboxes \
  -H 'content-type: application/json' \
  -d '{ "template": "starter", "ref": "<referral code>" }'
```

From the CLI, without signing in: `npx @flatstackdev/cli sandbox create --json`.

Both fields are optional. `template` is one of `starter`, `next`, `astro` or `react-router` and is recorded on the sandbox. `ref` is a [referral code](../api/billing.md); it goes into checkout when the sandbox is claimed. The answer, `201`:

```json
{
  "sandbox": true,
  "sandboxId": "sbx_…",
  "projectId": "prj_…",
  "url": "https://sandbox-k3v9q2x7mf.flatstack.app",
  "token": "fs_dep_…",
  "claimUrl": "https://flatstack.dev/claim?token=fs_clm_…",
  "expiresAt": "2026-11-01T12:00:00.000Z",
  "status": "ready",
  "limits": { "requests": 100000, "functionCpuMs": 200000, "databaseStorageBytes": 104857600, "…": "…" },
  "mcp": "https://api.flatstack.dev/mcp/sandbox",
  "warnings": []
}
```

`token` is shown once. Keep it for the rest of the session and don't show it to anyone. `claimUrl` is for the person: give it to them as soon as there's something worth keeping.

## Building in it

The token reaches the sandbox's own project and nothing else. With it you can:

| MCP tool (on `/mcp/sandbox`) | API route |
| --- | --- |
| `deploy` | `POST /v1/projects/{projectId}/deploys`, and `/assets`, `/assets/missing` for site files |
| `get_deploy_status` | `GET /v1/projects/{projectId}/deploys`, `…/deploys/{deployId}` |
| `apply_schema` | `POST /v1/projects/{projectId}/migrations` |
| `run_security_scan` | `POST /v1/projects/{projectId}/scan` |
| `set_env`, `set_secret` | `PUT /v1/projects/{projectId}/env/{key}`, `…/secrets/{key}` |
| `get_logs` | `GET /v1/projects/{projectId}/logs` |
| `search_docs` | `GET /v1/docs/search` |
| | `GET /v1/sandbox`: the sandbox's expiry, limits and usage so far |

On the sandbox endpoint, send the token as `Authorization: Bearer fs_dep_…`, or, if your client can't change its headers mid-session, as the `token` argument of each tool. You don't need `projectId` there: the token names the sandbox. From the CLI, `FLATSTACK_TOKEN=<token> flatstack deploy --project <projectId>`.

Every other route and tool refuses the token with `403 forbidden`, and so does the main `/mcp` endpoint. `run_sql` and `list_tables` aren't there yet; they arrive with the SQL tools.

## Limits

| Limit | Value |
| --- | --- |
| Projects | 1, D1 only |
| Requests | 100,000 |
| Function CPU | 200,000 ms |
| Database storage | 100 MB |
| File storage | 500 MB |
| End users | 1,000 |
| Realtime connections | 10 |
| Vector records | 5,000 |
| Email | logged, never sent |
| AI gateway, branch environments, custom domains, backups | none |
| Logs | kept 1 day |
| CPU per request, cron interval | as Hobby |

The usage limits cover the sandbox's whole 24 hours, not a month. Past one, the sandbox is capped the way an account is (requests get `429 requests_capped`, writes that add data get `507`), never billed. Results on the sandbox endpoint warn at 80%, and in the last two hours they say when the sandbox will be deleted.

Email your app sends is written to the sandbox's email log and never delivered, to anyone.

## How many

Three sandboxes an hour and ten a day from one address (an IPv4 address, or an IPv6 /64), and the same from one agent: its network, User-Agent and MCP client together. The fourth in an hour gets `429 sandbox_rate_limited`, with `details.limit` (`ip` or `fingerprint`) and `details.window` (`hour` or `day`). Only keyed hashes of the address and the fingerprint are kept.

## Keeping it

When the person opens the claim link:

- **Without an account**, they sign up, choose a plan and pay. Once Stripe confirms the payment, the account is created and the sandbox moves into it as its first project, in an active slot. The referral code goes into checkout with it. If the 24 hours ran out while they were paying, they still get the account they paid for, without the sandbox.
- **With an account**, one click on **Keep it** moves the sandbox in as a [parked project](../api/projects.md#active-and-parked-projects). They need a role that can create projects (owner, admin or developer). Promote it to an active slot when it needs the full plan. If every parked slot is taken, it lands [archived](../api/projects.md) instead: its database, users and files are kept in an archive for 12 months, it isn't served, and it takes no slot. Restore it once a slot is free.

Agents signed in to an account can claim for the person with `claim_sandbox` on the main `/mcp` endpoint, or `flatstack sandbox claim <claim link>`, which both call `POST /v1/sandboxes/claim` with `{ "claim": "<claim link or fs_clm_ token>" }`.

Either way the project keeps its ID, address, database, files, deploys, variables and secrets, and takes the account's limits from then on. The sandbox token stops working; the account's members and tokens take over.

The response's `state` says where it landed: `active`, `parked` or `archived`. A claim can fail with:

- `410 sandbox_expired`: the 24 hours are over and the sandbox has been, or is about to be, deleted.
- `409 conflict`: someone else already claimed it.

## Signed in, you don't need a sandbox

`create_sandbox` on the main `/mcp` endpoint, `POST /v1/sandboxes` with a token, and `flatstack sandbox create` when signed in make a parked project in your account instead: no 24-hour clock, the account's limits, nothing to claim. With no parked slot free it fails with `403 parked_slots_full`, which lists the parked projects you could remove.

## What's deleted, and when

An unclaimed sandbox is deleted within a minute of its `expiresAt`: its Worker, database, hostname, token and every row. Sandboxes never appear in an account's project list, slot counts, usage or setup checklist; Flatstack's own admin panel counts them.