# Environments (Universal)

Production, staging and development environments for universal push — tag devices at registration, send to one environment at a time, and switch an environment on or off per app.

Every universal device belongs to exactly one **product environment**: `production`, `staging` or `development`. A send targets one environment and reaches **only** the devices tagged with it — a staging send never touches a production device, and the other way around. `development` is the place for agent and developer testing.

Nothing changes until you use it: a registration without `environment` makes a production device, and a send without `environment` is a production send.

> **Not the APNs sandbox:**
>
> This is your product's environment, not Apple's APNs host. Native Notify picks the APNs production or sandbox host per token by itself — a debug build's token and a release build's token both work in any product environment.

## Tag a device

Pass `environment` when you [register](/docs/universal-push/registration):

```json
{
  "appId": 123,
  "appToken": "yourAppToken",
  "deviceId": "A1B2C3D4-install-key",
  "platform": "android",
  "environment": "staging",
  "tokens": { "fcmToken": "your-fcm-registration-token" }
}
```

- A new device without `environment` is a `production` device.
- Re-registering with a different `environment` **moves** the device (its tokens go with it).
- Re-registering without `environment` keeps the stored tag.
- An unknown value is `400 invalid_environment`.

## Send to an environment

Pass the same field on a [send](/docs/universal-push/sending):

```json
{
  "appId": 123,
  "appToken": "yourAppToken",
  "title": "Staging check",
  "message": "Only staging devices receive this.",
  "environment": "staging",
  "audience": { "type": "all" }
}
```

Every audience — `all`, `devices`, `subscribers` and `group` — is filtered to the send's environment, and the response echoes it as `environment`. Each [inbox](/docs/universal-push/web-inbox) entry records the environment it was sent in.

A [test send](/docs/universal-push/verification) goes to the one device or token you name, whatever its environment.

## List and switch environments

```text
GET  https://app.nativenotify.com/api/universal/environments/:appId/:appToken
POST https://app.nativenotify.com/api/universal/environments/:appId/:appToken
```

The GET answers `200` with all three environments, always in this order:

```json
{
  "environments": [
    { "key": "production", "label": "Production", "enabled": true, "deviceCount": 1280, "tokenCount": 1302 },
    { "key": "staging", "label": "Staging", "enabled": true, "deviceCount": 4, "tokenCount": 4 },
    { "key": "development", "label": "Development", "enabled": false, "deviceCount": 1, "tokenCount": 1 }
  ]
}
```

- `enabled` — the effective state. An environment that was never switched is on.
- `deviceCount` — devices tagged with the environment; `tokenCount` — every token those devices hold, live and retired (the live/retired split is on [health](/docs/universal-push/verification)).

The POST switches one environment and answers with the same shape:

```json
{ "key": "development", "enabled": false }
```

`key` also accepts the alias `environment`; `enabled` must be a boolean.

While an environment is **off**, new registrations into it and new sends to it are refused with `403 environment_disabled` — before anything is written or sent. Stored devices and tokens are never touched, so switching it back on restores delivery as it was. Production can be switched off too; that affects every call that does not name an environment.

## Errors

| Status | Code                                             | Meaning                                                                    |
| ------ | ------------------------------------------------ | -------------------------------------------------------------------------- |
| `400`  | `invalid_body`, `missing_field`, `invalid_field` | Bad body, no `key` / `enabled`, a non-boolean `enabled`, or a bad `appId`. |
| `400`  | `invalid_environment`                            | `key` is not `production`, `staging` or `development`.                     |
| `403`  | `environment_disabled`                           | A registration or send targeted an environment that is switched off.       |
| `404`  | `app_not_found`                                  | The app is not one this caller can access.                                 |
