> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usenexio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Environments

> Keep test traffic apart from live traffic, and know what changes between a sandbox and live.

An environment separates one stream of traffic from another inside your org. Every API key belongs to exactly one environment, and runs, webhook endpoints and conversations are recorded under the environment of the key that created them. A live key never reads sandbox runs, and a sandbox key never reads live runs.

## Kinds

| Kind      | How many            | Who creates it                     | Slug                                        |
| --------- | ------------------- | ---------------------------------- | ------------------------------------------- |
| `live`    | Exactly one per org | Nexio                              | `live`                                      |
| `sandbox` | Up to 5 per org     | You, in the portal or with the API | Your choice, for example `dev` or `staging` |

The `live` environment cannot be created, renamed to another slug, or deleted.

## What differs between live and sandbox

| Behavior                                                                        | Live                                                         | Sandbox                                                                    |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------- |
| Run `environment` field and webhook `environment` field                         | `live`                                                       | `test` (every sandbox reads `test`)                                        |
| Run the unreleased draft of an engine (`"engine_version": "draft"`)             | Refused: `400 engine_version_draft_requires_sandbox_key`     | Allowed                                                                    |
| Deterministic [sandbox fixtures](/reference/sandbox-fixtures) (`test_scenario`) | Refused: `400 test_scenario_sandbox_only`                    | Allowed, with a scoped key that holds `runs:test`                          |
| Webhook secret rotation overlap                                                 | 24 hours                                                     | 5 minutes                                                                  |
| Records and graph, with an organization key                                     | Allowed                                                      | Refused: `403 scoped_key_required` (a scoped key works in any environment) |
| Monthly run cap                                                                 | Shared: live and sandbox runs count against the same org cap | Shared                                                                     |
| Rate limits                                                                     | Shared: buckets are per org, not per environment             | Shared                                                                     |

The `environment` field on runs and webhooks is a compatibility label with two values. Use the key's environment slug, not this label, to tell two sandboxes apart.

## Slugs and names

| Rule                | Value                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Slug pattern        | `^[a-z0-9_]{1,16}$`: 1 to 16 lowercase letters, digits or underscores                                                          |
| Reserved slugs      | `live`, `test`, `internal`, `admin`, `api`, `nexio`, `draft`, `all`                                                            |
| Slug after creation | Cannot change. `kind` and `created_at` cannot change either                                                                    |
| Name                | Optional display label, at most 64 bytes of UTF-8 (64 characters when every character is ASCII). The only field you can change |
| Maximum sandboxes   | 5 per org                                                                                                                      |

Some older orgs have a sandbox with the slug `test`. It keeps working, but no new environment can take that slug.

## Manage environments in the portal

Open **Settings**, then **Environments**. You need the admin or developer role. You can:

* create a sandbox (slug, optional name),
* rename one,
* delete a sandbox that no API key, webhook endpoint or run still uses.

To create a key for an environment, open **Settings**, then **API keys**, and pick the environment in the create dialog.

## Manage environments with the API

The environment routes accept organization keys only. A scoped key gets `403 insufficient_capability`. Any organization key, from any environment, can manage all of the org's environments.

| Call              | Route                                | Reference                                                            |
| ----------------- | ------------------------------------ | -------------------------------------------------------------------- |
| Create a sandbox  | `POST /api/v1/environments`          | [Create environment](/api-reference/environments/create-environment) |
| List environments | `GET /api/v1/environments`           | [List environments](/api-reference/environments/list-environments)   |
| Rename            | `PATCH /api/v1/environments/{slug}`  | [Update environment](/api-reference/environments/update-environment) |
| Delete a sandbox  | `DELETE /api/v1/environments/{slug}` | [Delete environment](/api-reference/environments/delete-environment) |

Create a sandbox:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.usenexio.com/api/v1/environments \
    -H "Authorization: Bearer $NEXIO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"slug": "staging", "name": "Staging"}'
  ```

  ```python Python theme={null}
  import os
  import requests

  resp = requests.post(
      "https://api.usenexio.com/api/v1/environments",
      headers={"Authorization": "Bearer " + os.environ["NEXIO_API_KEY"]},
      json={"slug": "staging", "name": "Staging"},
      timeout=30,
  )
  resp.raise_for_status()
  print(resp.json())
  ```

  ```typescript TypeScript theme={null}
  const resp = await fetch("https://api.usenexio.com/api/v1/environments", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NEXIO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ slug: "staging", name: "Staging" }),
  });
  if (!resp.ok) throw new Error(`${resp.status} ${await resp.text()}`);
  console.log(await resp.json());
  ```
</CodeGroup>

Response `201`:

```json theme={null}
{
  "id": "7a3e1c52-94b0-4f6d-8e21-5c0d9b7f3a18",
  "slug": "staging",
  "kind": "sandbox",
  "name": "Staging",
  "created_at": "2026-09-23T15:10:42Z"
}
```

The request body is at most 4 KiB and accepts only `slug` and `name`.

### Deleting a sandbox

A sandbox can be deleted only when nothing references it. Every API key ever created for it counts, including revoked keys: revoking a key does not clear the blocker. Its webhook endpoints and its runs also count. Runs are removed by the 90-day retention purge, except runs flagged to be kept. While anything remains, the API answers `409` with a count of each blocker:

```json theme={null}
{
  "code": "environment_in_use",
  "message": "Environment still has bound API keys, webhook endpoints, or runs. Revoke / delete those first.",
  "blockers": {
    "api_keys": 1,
    "webhook_endpoints": 0,
    "runs": 37
  }
}
```

This is the one error body with a top-level `blockers` object instead of `details`. A successful delete answers `204` with no body.

## Errors

| HTTP | Code                         | Cause                                                          | Fix                                                  |
| ---- | ---------------------------- | -------------------------------------------------------------- | ---------------------------------------------------- |
| 400  | `environment_slug_invalid`   | Slug missing or not `^[a-z0-9_]{1,16}$`                        | Use 1 to 16 lowercase letters, digits or underscores |
| 400  | `environment_slug_reserved`  | Slug is one of the 8 reserved slugs                            | Choose another slug                                  |
| 400  | `environment_slug_taken`     | Your org already has that slug                                 | Choose another slug                                  |
| 400  | `environment_name_invalid`   | Name longer than 64 bytes of UTF-8 text                        | Shorten the name                                     |
| 400  | `environment_limit_reached`  | Your org already has 5 sandboxes                               | Delete a sandbox you no longer use                   |
| 400  | `environment_live_immutable` | Tried to delete `live`                                         | The live environment is managed by Nexio             |
| 400  | `invalid_request`            | Invalid JSON, an unknown field, or a PATCH body without `name` | Send only the documented fields                      |
| 403  | `insufficient_capability`    | Called with a scoped key                                       | Use an organization key                              |
| 404  | `environment_not_found`      | No environment with that slug in your org                      | Check the slug with `GET /api/v1/environments`       |
| 409  | `environment_in_use`         | Keys, webhook endpoints or runs still reference the sandbox    | Clear the blockers listed in `blockers`              |

## Next

<CardGroup cols={2}>
  <Card title="Authentication and access" icon="key" href="/authentication">
    Keys, capabilities and which routes need a live key.
  </Card>

  <Card title="Sandbox fixtures" icon="flask" href="/reference/sandbox-fixtures">
    Drive a run to a chosen terminal state without calling any provider.
  </Card>
</CardGroup>
