# Test mode

Every organization has two environments: **Production** and **Test**. Test is included on every plan, from Free up, with no setup and no extra role. You build and check deep linking, install attribution, and analytics against Test data, and none of it reaches your live numbers.

## What Test holds

Test is a separate copy of the parts of WarpLink you build against:

- **Apps and links.** Register a debug build as a Test app and create Test links for it.
- **Analytics and installs.** Test clicks and Test installs appear in Test only.
- **Keys.** Keys created in Test start with `wl_test_`.
- **Webhooks.** Test has its own endpoints and its own signing secrets.

Team, billing, domains, security settings, and account settings are shared. Test is not a second organization to manage. Overview shows Production numbers in both environments.

Test starts empty. Nothing is copied from Production, so you register the apps and create the links you want to test.

## The key decides the environment

On REST, MCP, and every SDK, the key prefix picks the environment.

| Prefix | Environment | Reads and writes |
|--------|-------------|------------------|
| `wl_live_` | Production | Production data only |
| `wl_test_` | Test | Test data only |

A Test key never touches Production, and a Production key never touches Test. A Production id sent with a Test key returns `404 NOT_FOUND`, and the same holds in reverse.

```bash
# Production: a wl_live_ key reads Production links
curl https://api.warplink.app/v1/links \
  -H "Authorization: Bearer wl_live_YOUR_API_KEY"

# Test: a wl_test_ key reads Test links
curl https://api.warplink.app/v1/links \
  -H "Authorization: Bearer wl_test_YOUR_API_KEY"
```

Create a key in the environment you want it for. A key created through [the Keys API](https://warplink.app/docs/api/keys) or the dashboard in Test is a `wl_test_` key, and a key created in Production is a `wl_live_` key. A key keeps its environment for its whole life, so you never convert one.

Endpoints that manage the organization itself, such as domains, invitations, and billing, need a Production key. A Test key on one of those returns `403 ENVIRONMENT_NOT_SUPPORTED`.

## Test apps and hosts

A Test app is registered like a live app, with its own bundle ID or package name, for example your debug build.

- Its host is the live handle with `-test` added, for example `acme-test.aplnk.to`.
- The host has its own AASA and assetlinks files, so your Production app never opens a Test link.
- A Test app cannot reuse a Production app's bundle ID or package name. That returns `409 TEST_IDENTIFIER_CONFLICT`.
- Test hosts send `X-Robots-Tag: noindex`, so search engines never list Test links.

Test allows as many apps as your plan allows, counted apart from your Production apps. After a downgrade, Test apps over the new limit stay, read only, with their links and data. Their links keep redirecting.

Every Test link opens a Test app. Test has no web-only links, so `POST /v1/links` with a Test key and no `app_id` returns `400 TEST_APP_REQUIRED`.

## Limits

The same limits apply on every plan.

| Limit | Value |
|-------|-------|
| Links | 100 |
| Stored clicks | 10,000 per billing period of the organization |
| Analytics retention | 90 days |

At 100 links, `POST /v1/links` returns `403 TEST_LINK_LIMIT` until you delete a Test link. Past 10,000 stored clicks, Test links keep redirecting. New Test clicks are not stored until the next billing period starts. Redirects never stop.

Test links, clicks, and installs never count toward your plan: not in the click limit, the usage banner, the usage emails, or the analytics freeze.

## Webhooks in Test

Test webhook endpoints are separate from live ones, and each has its own signing secret. Test events go only to Test endpoints, and live events go only to live endpoints. Every event carries `"environment": "test"` or `"environment": "production"`. See [Receive referral webhooks](https://warplink.app/docs/guides/webhooks).

## Switch environments

In the dashboard, Test mode is an on and off row at the end of the organization menu. Switching opens the same page in the other environment: Links in Production becomes Links in Test. Pages with a Test copy show a Test mark so you always know which data you are looking at. A write goes to the environment the page was opened in. If another tab switched since, the request is refused with `409 ENVIRONMENT_CHANGED` and nothing is written to either environment. Reload the page and try again.

In an MCP client connected over OAuth, call `switch_environment` with `environment` set to `test` or `production`. Every tool result names the environment it used, in the result metadata and in an `Environment` line of text. Changing the organization resets the connection to Production.

A connection made with an API key never switches. A `wl_test_` key is always Test and a `wl_live_` key is always Production.

## Agents start in Test

An agent that signs up through [`POST /v1/agent/signup`](https://warplink.app/docs/api/agent-signup) starts in Test. The access token it gets acts in Test, and the first keys it creates start with `wl_test_`. The token is an opaque OAuth token, not a key, so it carries no `wl_test_` prefix. The signup response names its environment as `test`.

Links in an unclaimed workspace are paused, and a paused link does not redirect. An agent can create and configure Test apps and links, then ask the person to claim the workspace before it checks redirects on a device. A switch to Production on an unclaimed workspace returns `CLAIM_REQUIRED`. See [AI agents](https://warplink.app/docs/ai-agents) for the full flow.

## SDK behavior

SDK 1.3.0 on iOS, Android, React Native, and Flutter keeps Test and live state apart on the device. Stored attribution, markers, and deferred deep link state are kept per key environment, so a Test build never reads or clears what a live build on the same device stored. Live keys keep the storage names earlier releases used, so upgrading resets nothing.

To test a first install again without deleting the app, call `clearAttribution()` with a `wl_test_` key. It clears the attribution stored on the device. When automatic deferred checks are enabled, it also starts a new check; otherwise your app runs the check itself. With a `wl_live_` key it does nothing and succeeds. Only the device is cleared: server records are not deleted.

SDKs before 1.3.0 do not separate storage by key. With those versions, a Test build and a live build on the same device share one stored attribution state.

Use a `wl_test_` key in debug builds and a `wl_live_` key in release builds. Each SDK page shows the call with both prefixes.

## Next steps

- [Test your integration](https://warplink.app/docs/guides/testing) walks through a full check from a Test app to a Test click.
- [Authentication](https://warplink.app/docs/authentication) covers key types and prefixes.
- [AI agents](https://warplink.app/docs/ai-agents) shows the flow an agent follows, starting in Test.
- [Errors](https://warplink.app/docs/errors) lists every Test error code.
