---
title: Test environments
description: "Try delegated access end to end in your test environment, with your own team's accounts, before ZevID approves your live app."
---

You can build and test delegated access **without ZevID's approval**, in your
test environment (`env_test_…`, your `pk_test_…` and `sk_test_…` keys). It
runs against the real Zev products, so the only thing that is "test" is who
can use it: **your own team, and no one else.**

## Set it up

1. Switch the console to your **test** environment and open **Delegated
   access**.
2. Under **Set up a test app**, pick the product and register your first
   callback. That creates your test app at ZevID straight away, with every
   permission the product offers to partners.
3. Use your `pk_test_…` key on [authorize](/delegated-access/flow/) and your
   `sk_test_…` key on token and revoke. Everything else is identical.

Test callbacks may use **any https host or `http://localhost`**: preview
deployments and local development both work. Your live callbacks must still
be on your verified domain.

## Who can use it

Only the people in your ZevAuth workspace, connecting with **their own ZevID
accounts**. We send ZevID your workspace's members whenever your team changes,
and every day, up to **25 people** (owners and admins first). Add teammates
under **Team** in the console.

Anyone else who reaches your test environment is stopped on ZevID's screen
("This is a test version of *your app*. Only *your app*'s team can connect
it.") and your callback receives:

```text
?error=access_denied&error_reason=not_a_test_member&state=...
```

**Sign in with ZevID** on a test environment works for anyone today. ZevID
can extend the same team-only rule to it; once that is on, someone outside the
team gets `error=zevid_not_a_test_member` on your sign-in callback. Keeping
your team in ZevAuth up to date means nothing changes for your team when it
happens. Delegated access is team-only from the start.

Removing someone from your workspace ends their test connections at once
(and their test sign-ins, once team-only sign-in is on). Their live connections are untouched.

> **Note:**
> The **Delegated access** page shows how many people ZevID has on your team.
> "No team list yet" means ZevID has not received it: it is sent the first time
> the environment uses ZevID, and whenever your team changes.

## Limits

| | Test | Live |
| --- | --- | --- |
| Who can connect | Your team (up to 25 people) | Anyone with a product account |
| Approval and domain verification | Not needed | Needed, per product |
| Permissions | Every permission the product offers to partners, except any it keeps live-only | What ZevID approved for you |
| High-risk permissions | Work, with the same "Confirm it's you" | With ZevID's approval |
| Callbacks | Any https host, or localhost | https on your verified domain |
| Connections | At most **25** at once | No cap |
| How long a connection lasts | Ends on its own after a set number of days, shown in the console and as `grant_expires_at` | Until it is disconnected |
| Emails to the user | None | Connect, new permissions, removal |

When a test connection ends, refresh fails with `invalid_grant`, reason
`test_grant_expired`, and you receive `delegated_access.connection.revoked`
with the same reason. Reconnect to keep testing. The token response tells you
when in advance, as `grant_expires_at`.

A product may refuse test apps entirely (`test_apps_not_allowed`) or keep
some permissions live-only (`scope_not_for_test_apps`).

## Test and live never mix

They are separate apps at ZevID. A teammate's test connection never counts as
a live one, and never pre-fills live consent. Codes, refresh tokens and
connection ids from one never work with the other (`client_mismatch`).

Every token response and webhook says which one you are looking at:

| Where | Field |
| --- | --- |
| Token response | `tenant_mode`: `"test"` or `"live"`, and `grant_expires_at` (test only, otherwise `null`) |
| Webhooks | `data.tenantMode` and `data.expiresAt` |

The ZevID consent screen and the user's **Apps and websites** page label your
app "Test version", so nobody mistakes it for the live one.

## Going live

Testing is the rehearsal. To take real users, request approval for your
**live** environment: see [Getting approved](/delegated-access/getting-approved/).
Then register your live callbacks and switch your keys. No test connection
carries over.
